git.delta.rocks / unique-network / refs/commits / 0ceea0af66a9

difftreelog

fix account token ownership limit when transfer (#1080)

Daniel Shiposha2024-06-25parent: #347f218.patch.diff
in: master
* fix: account token ownership limit when mint

* fix: limit tests

* fix: account token ownership limit rft mint

* Revert "fix: account token ownership limit rft mint"

This reverts commit 054110e015fd237fed77be1ef21bba6cea13fbea.

* Revert "fix: limit tests"

This reverts commit 75699574656bb0a34caddf3c9abdbffef79ba85d.

* Revert "fix: account token ownership limit when mint"

This reverts commit b89a3c86d3bf0888a636bc7da1949affa12d867d.

* fix: account token ownership limit when transfer

* test: account token ownership transfer tests

---------

3 files changed

modifiedjs-packages/tests/limits.test.tsdiffbeforeafterboth
before · js-packages/tests/limits.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import type {IKeyringPair} from '@polkadot/types/types';18import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '@unique/test-utils/util.js';1920describe('Number of tokens per address (NFT)', () => {21  let alice: IKeyringPair;2223  before(async () => {24    await usingPlaygrounds(async (helper, privateKey) => {25      const donor = await privateKey({url: import.meta.url});26      [alice] = await helper.arrange.createAccounts([10n], donor);27    });28  });2930  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {31    const collection = await helper.nft.mintCollection(alice, {});32    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});3334    for(let i = 0; i < 10; i++){35      await expect(collection.mintToken(alice)).to.be.not.rejected;36    }37    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);38    for(let i = 1; i < 11; i++) {39      await expect(collection.burnToken(alice, i)).to.be.not.rejected;40    }41    await collection.burn(alice);42  });4344  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {45    const collection = await helper.nft.mintCollection(alice, {});46    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});4748    await collection.mintToken(alice);49    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);5051    await collection.burnToken(alice, 1);52    await expect(collection.burn(alice)).to.be.not.rejected;53  });54});5556describe('Number of tokens per address (ReFungible)', () => {57  let alice: IKeyringPair;5859  before(async function() {60    await usingPlaygrounds(async (helper, privateKey) => {61      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);6263      const donor = await privateKey({url: import.meta.url});64      [alice] = await helper.arrange.createAccounts([10n], donor);65    });66  });6768  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {69    const collection = await helper.rft.mintCollection(alice, {});70    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});7172    for(let i = 0; i < 10; i++){73      await expect(collection.mintToken(alice, 10n)).to.be.not.rejected;74    }75    await expect(collection.mintToken(alice, 10n)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);76    for(let i = 1; i < 11; i++) {77      await expect(collection.burnToken(alice, i, 10n)).to.be.not.rejected;78    }79    await collection.burn(alice);80  });8182  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {83    const collection = await helper.rft.mintCollection(alice, {});84    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});8586    await collection.mintToken(alice);87    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);8889    await collection.burnToken(alice, 1);90    await expect(collection.burn(alice)).to.be.not.rejected;91  });92});9394// todo:playgrounds skipped ~ postponed95describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => {96  /*let alice: IKeyringPair;97  let bob: IKeyringPair;98  let charlie: IKeyringPair;99100  before(async () => {101    await usingApi(async (api, privateKeyWrapper) => {102      alice = privateKeyWrapper('//Alice');103      bob = privateKeyWrapper('//Bob');104      charlie = privateKeyWrapper('//Charlie');105    });106  });107108  itSub.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {109    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});110    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});111    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');112    await setCollectionSponsorExpectSuccess(collectionId, alice.address);113    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');114    await transferExpectSuccess(collectionId, tokenId, alice, bob);115    const aliceBalanceBefore = await getFreeBalance(alice);116117    // check setting SponsorTimeout = 5, fail118    await waitNewBlocks(5);119    await transferExpectSuccess(collectionId, tokenId, bob, charlie);120    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);121    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);122123    // check setting SponsorTimeout = 7, success124    await waitNewBlocks(2); // 5 + 2125    await transferExpectSuccess(collectionId, tokenId, charlie, bob);126    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);127    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;128    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);129    await destroyCollectionExpectSuccess(collectionId);130  });131132  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {133134    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});135    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});136    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');137    await setCollectionSponsorExpectSuccess(collectionId, alice.address);138    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');139    await transferExpectSuccess(collectionId, tokenId, alice, bob);140    const aliceBalanceBefore = await getFreeBalance(alice);141142    // check setting SponsorTimeout = 1, fail143    await waitNewBlocks(1);144    await transferExpectSuccess(collectionId, tokenId, bob, charlie);145    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);146    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);147148    // check setting SponsorTimeout = 5, success149    await waitNewBlocks(4);150    await transferExpectSuccess(collectionId, tokenId, charlie, bob);151    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);152    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;153    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);154    await destroyCollectionExpectSuccess(collectionId);155  });156});157158describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => {159  let alice: IKeyringPair;160  let bob: IKeyringPair;161  let charlie: IKeyringPair;162163  before(async () => {164    await usingApi(async (api, privateKeyWrapper) => {165      alice = privateKeyWrapper('//Alice');166      bob = privateKeyWrapper('//Bob');167      charlie = privateKeyWrapper('//Charlie');168    });169  });170171  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {172    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});173    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});174    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');175    await setCollectionSponsorExpectSuccess(collectionId, alice.address);176    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');177    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');178    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');179    const aliceBalanceBefore = await getFreeBalance(alice);180181    // check setting SponsorTimeout = 5, fail182    await waitNewBlocks(5);183    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');184    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);185    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);186187    // check setting SponsorTimeout = 7, success188    await waitNewBlocks(2); // 5 + 2189    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');190    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);191    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;192    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);193194    await destroyCollectionExpectSuccess(collectionId);195  });196197  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {198199    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});200    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});201    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');202    await setCollectionSponsorExpectSuccess(collectionId, alice.address);203    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');204    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');205    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');206    const aliceBalanceBefore = await getFreeBalance(alice);207208    // check setting SponsorTimeout = 1, fail209    await waitNewBlocks(1);210    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');211    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);212    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);213214    // check setting SponsorTimeout = 5, success215    await waitNewBlocks(4);216    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');217    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);218    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;219    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);220221    await destroyCollectionExpectSuccess(collectionId);222  });223});224225describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => {226  let alice: IKeyringPair;227  let bob: IKeyringPair;228  let charlie: IKeyringPair;229230  before(async () => {231    await usingApi(async (api, privateKeyWrapper) => {232      alice = privateKeyWrapper('//Alice');233      bob = privateKeyWrapper('//Bob');234      charlie = privateKeyWrapper('//Charlie');235    });236  });237238  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {239    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});240    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});241    const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible');242    await setCollectionSponsorExpectSuccess(collectionId, alice.address);243    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');244    await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible');245    const aliceBalanceBefore = await getFreeBalance(alice);246247    // check setting SponsorTimeout = 5, fail248    await waitNewBlocks(5);249    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');250    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);251    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);252253    // check setting SponsorTimeout = 7, success254    await waitNewBlocks(2); // 5 + 2255    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');256    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);257    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;258    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);259    await destroyCollectionExpectSuccess(collectionId);260  });261262  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {263264    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});265    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});266    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');267    await setCollectionSponsorExpectSuccess(collectionId, alice.address);268    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');269    await transferExpectSuccess(collectionId, tokenId, alice, bob);270    const aliceBalanceBefore = await getFreeBalance(alice);271272    // check setting SponsorTimeout = 1, fail273    await waitNewBlocks(1);274    await transferExpectSuccess(collectionId, tokenId, bob, charlie);275    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);276    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);277278    // check setting SponsorTimeout = 5, success279    await waitNewBlocks(4);280    await transferExpectSuccess(collectionId, tokenId, charlie, bob);281    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);282    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;283    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);284    await destroyCollectionExpectSuccess(collectionId);285  });*/286});287288describe('Collection zero limits (NFT)', () => {289  let alice: IKeyringPair;290  let bob: IKeyringPair;291  let charlie: IKeyringPair;292293  before(async () => {294    await usingPlaygrounds(async (helper, privateKey) => {295      const donor = await privateKey({url: import.meta.url});296      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);297    });298  });299300  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {301    const collection = await helper.nft.mintCollection(alice, {});302    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});303304    for(let i = 0; i < 10; i++){305      await collection.mintToken(alice);306    }307    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);308  });309310  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {311    const collection = await helper.nft.mintCollection(alice, {});312    await collection.setLimits(alice, {sponsorTransferTimeout: 0});313    const token = await collection.mintToken(alice);314315    await collection.setSponsor(alice, alice.address);316    await collection.confirmSponsorship(alice);317318    await token.transfer(alice, {Substrate: bob.address});319    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);320321    // check setting SponsorTimeout = 0, success with next block322    await helper.wait.newBlocks(1);323    await token.transfer(bob, {Substrate: charlie.address});324    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);325    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;326  });327});328329describe('Collection zero limits (Fungible)', () => {330  let alice: IKeyringPair;331  let bob: IKeyringPair;332  let charlie: IKeyringPair;333334  before(async () => {335    await usingPlaygrounds(async (helper, privateKey) => {336      const donor = await privateKey({url: import.meta.url});337      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);338    });339  });340341  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {342    const collection = await helper.ft.mintCollection(alice, {});343    await collection.setLimits(alice, {sponsorTransferTimeout: 0});344    await collection.mint(alice, 3n);345346    await collection.setSponsor(alice, alice.address);347    await collection.confirmSponsorship(alice);348349    await collection.transfer(alice, {Substrate: bob.address}, 2n);350    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);351352    // check setting SponsorTimeout = 0, success with next block353    await helper.wait.newBlocks(1);354    await collection.transfer(bob, {Substrate: charlie.address});355    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);356    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;357  });358});359360describe('Collection zero limits (ReFungible)', () => {361  let alice: IKeyringPair;362  let bob: IKeyringPair;363  let charlie: IKeyringPair;364365  before(async function() {366    await usingPlaygrounds(async (helper, privateKey) => {367      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);368369      const donor = await privateKey({url: import.meta.url});370      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);371    });372  });373374  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {375    const collection = await helper.rft.mintCollection(alice, {});376    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});377    for(let i = 0; i < 10; i++){378      await collection.mintToken(alice);379    }380    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);381  });382383  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {384    const collection = await helper.rft.mintCollection(alice, {});385    await collection.setLimits(alice, {sponsorTransferTimeout: 0});386    const token = await collection.mintToken(alice, 3n);387388    await collection.setSponsor(alice, alice.address);389    await collection.confirmSponsorship(alice);390391    await token.transfer(alice, {Substrate: bob.address}, 2n);392    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);393394    // check setting SponsorTimeout = 0, success with next block395    await helper.wait.newBlocks(1);396    await token.transfer(bob, {Substrate: charlie.address});397    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);398    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;399  });400});401402describe('Effective collection limits (NFT)', () => {403  let alice: IKeyringPair;404405  before(async () => {406    await usingPlaygrounds(async (helper, privateKey) => {407      const donor = await privateKey({url: import.meta.url});408      [alice] = await helper.arrange.createAccounts([10n], donor);409    });410  });411412  itSub('Effective collection limits', async ({helper}) => {413    const collection = await helper.nft.mintCollection(alice, {});414    await collection.setLimits(alice, {ownerCanTransfer: true});415416    {417      // Check that limits are undefined418      const collectionInfo = await collection.getData();419      const limits = collectionInfo?.raw.limits;420      expect(limits).to.be.any;421422      expect(limits.accountTokenOwnershipLimit).to.be.null;423      expect(limits.sponsoredDataSize).to.be.null;424      expect(limits.sponsoredDataRateLimit).to.be.null;425      expect(limits.tokenLimit).to.be.null;426      expect(limits.sponsorTransferTimeout).to.be.null;427      expect(limits.sponsorApproveTimeout).to.be.null;428      expect(limits.ownerCanTransfer).to.be.true;429      expect(limits.ownerCanDestroy).to.be.null;430      expect(limits.transfersEnabled).to.be.null;431    }432433    { // Check that limits is undefined for non-existent collection434      const limits = await helper.collection.getEffectiveLimits(999999);435      expect(limits).to.be.null;436    }437438    { // Check that default values defined for collection limits439      const limits = await collection.getEffectiveLimits();440441      expect(limits.accountTokenOwnershipLimit).to.be.eq(100000000);442      expect(limits.sponsoredDataSize).to.be.eq(2048);443      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});444      expect(limits.tokenLimit).to.be.eq(4294967295);445      expect(limits.sponsorTransferTimeout).to.be.eq(5);446      expect(limits.sponsorApproveTimeout).to.be.eq(5);447      expect(limits.ownerCanTransfer).to.be.true;448      expect(limits.ownerCanDestroy).to.be.true;449      expect(limits.transfersEnabled).to.be.true;450    }451452    {453      // Check the values for collection limits454      await collection.setLimits(alice, {455        accountTokenOwnershipLimit: 99_999,456        sponsoredDataSize: 1024,457        tokenLimit: 123,458        transfersEnabled: false,459      });460461      const limits = await collection.getEffectiveLimits();462463      expect(limits.accountTokenOwnershipLimit).to.be.eq(99999);464      expect(limits.sponsoredDataSize).to.be.eq(1024);465      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});466      expect(limits.tokenLimit).to.be.eq(123);467      expect(limits.sponsorTransferTimeout).to.be.eq(5);468      expect(limits.sponsorApproveTimeout).to.be.eq(5);469      expect(limits.ownerCanTransfer).to.be.true;470      expect(limits.ownerCanDestroy).to.be.true;471      expect(limits.transfersEnabled).to.be.false;472    }473  });474});
after · js-packages/tests/limits.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import type {IKeyringPair} from '@polkadot/types/types';18import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '@unique/test-utils/util.js';1920describe('Number of tokens per address (NFT)', () => {21  let alice: IKeyringPair;22  let bob: IKeyringPair;2324  before(async () => {25    await usingPlaygrounds(async (helper, privateKey) => {26      const donor = await privateKey({url: import.meta.url});27      [alice, bob] = await helper.arrange.createAccounts([10n, 0n], donor);28    });29  });3031  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {32    const collection = await helper.nft.mintCollection(alice, {});33    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});3435    for(let i = 0; i < 10; i++){36      await expect(collection.mintToken(alice)).to.be.not.rejected;37    }38    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);39    for(let i = 1; i < 11; i++) {40      await expect(collection.burnToken(alice, i)).to.be.not.rejected;41    }42    await collection.burn(alice);43  });4445  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {46    const collection = await helper.nft.mintCollection(alice, {});47    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});4849    await collection.mintToken(alice);50    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);5152    await collection.burnToken(alice, 1);53    await expect(collection.burn(alice)).to.be.not.rejected;54  });5556  itSub('Can transfer tokens to address equal to accountTokenOwnershipLimit', async ({helper}) => {57    const collection = await helper.nft.mintCollection(alice, {});58    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});5960    // Limit 1 - can transfer #1 token61    const tkn1 = await collection.mintToken(alice);62    await collection.transferToken(alice, tkn1.tokenId, {Substrate: bob.address});6364    // Limit 1 - cannot transfer #2 token65    const tkn2 = await collection.mintToken(alice);66    await expect(collection.transferToken(alice, tkn2.tokenId, {Substrate: bob.address})).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);6768    // Increase limit to 269    // Now can transfer token #270    await collection.setLimits(alice, {accountTokenOwnershipLimit: 2});71    await collection.transferToken(alice, tkn2.tokenId, {Substrate: bob.address});7273    // But cannot transfer token #374    const tkn3 = await collection.mintToken(alice);75    await expect(collection.transferToken(alice, tkn3.tokenId, {Substrate: bob.address})).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);76  });77});7879describe('Number of tokens per address (ReFungible)', () => {80  let alice: IKeyringPair;81  let bob: IKeyringPair;8283  before(async function() {84    await usingPlaygrounds(async (helper, privateKey) => {85      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);8687      const donor = await privateKey({url: import.meta.url});88      [alice, bob] = await helper.arrange.createAccounts([10n, 0n], donor);89    });90  });9192  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {93    const collection = await helper.rft.mintCollection(alice, {});94    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});9596    for(let i = 0; i < 10; i++){97      await expect(collection.mintToken(alice, 10n)).to.be.not.rejected;98    }99    await expect(collection.mintToken(alice, 10n)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);100    for(let i = 1; i < 11; i++) {101      await expect(collection.burnToken(alice, i, 10n)).to.be.not.rejected;102    }103    await collection.burn(alice);104  });105106  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {107    const collection = await helper.rft.mintCollection(alice, {});108    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});109110    await collection.mintToken(alice);111    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);112113    await collection.burnToken(alice, 1);114    await expect(collection.burn(alice)).to.be.not.rejected;115  });116117  itSub('Can transfer tokens to address equal to accountTokenOwnershipLimit', async ({helper}) => {118    const collection = await helper.rft.mintCollection(alice, {});119    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});120121    // Limit 1 - can transfer #1 token122    const tkn1 = await collection.mintToken(alice);123    await collection.transferToken(alice, tkn1.tokenId, {Substrate: bob.address});124125    // Limit 1 - cannot transfer #2 token126    const tkn2 = await collection.mintToken(alice);127    await expect(collection.transferToken(alice, tkn2.tokenId, {Substrate: bob.address})).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);128129    // Increase limit to 2130    // Now can transfer token #2131    await collection.setLimits(alice, {accountTokenOwnershipLimit: 2});132    await collection.transferToken(alice, tkn2.tokenId, {Substrate: bob.address});133134    // But cannot transfer token #3135    const tkn3 = await collection.mintToken(alice);136    await expect(collection.transferToken(alice, tkn3.tokenId, {Substrate: bob.address})).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);137  });138});139140// todo:playgrounds skipped ~ postponed141describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => {142  /*let alice: IKeyringPair;143  let bob: IKeyringPair;144  let charlie: IKeyringPair;145146  before(async () => {147    await usingApi(async (api, privateKeyWrapper) => {148      alice = privateKeyWrapper('//Alice');149      bob = privateKeyWrapper('//Bob');150      charlie = privateKeyWrapper('//Charlie');151    });152  });153154  itSub.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {155    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});156    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});157    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');158    await setCollectionSponsorExpectSuccess(collectionId, alice.address);159    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');160    await transferExpectSuccess(collectionId, tokenId, alice, bob);161    const aliceBalanceBefore = await getFreeBalance(alice);162163    // check setting SponsorTimeout = 5, fail164    await waitNewBlocks(5);165    await transferExpectSuccess(collectionId, tokenId, bob, charlie);166    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);167    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);168169    // check setting SponsorTimeout = 7, success170    await waitNewBlocks(2); // 5 + 2171    await transferExpectSuccess(collectionId, tokenId, charlie, bob);172    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);173    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;174    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);175    await destroyCollectionExpectSuccess(collectionId);176  });177178  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {179180    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});181    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});182    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');183    await setCollectionSponsorExpectSuccess(collectionId, alice.address);184    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');185    await transferExpectSuccess(collectionId, tokenId, alice, bob);186    const aliceBalanceBefore = await getFreeBalance(alice);187188    // check setting SponsorTimeout = 1, fail189    await waitNewBlocks(1);190    await transferExpectSuccess(collectionId, tokenId, bob, charlie);191    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);192    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);193194    // check setting SponsorTimeout = 5, success195    await waitNewBlocks(4);196    await transferExpectSuccess(collectionId, tokenId, charlie, bob);197    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);198    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;199    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);200    await destroyCollectionExpectSuccess(collectionId);201  });202});203204describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => {205  let alice: IKeyringPair;206  let bob: IKeyringPair;207  let charlie: IKeyringPair;208209  before(async () => {210    await usingApi(async (api, privateKeyWrapper) => {211      alice = privateKeyWrapper('//Alice');212      bob = privateKeyWrapper('//Bob');213      charlie = privateKeyWrapper('//Charlie');214    });215  });216217  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {218    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});219    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});220    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');221    await setCollectionSponsorExpectSuccess(collectionId, alice.address);222    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');223    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');224    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');225    const aliceBalanceBefore = await getFreeBalance(alice);226227    // check setting SponsorTimeout = 5, fail228    await waitNewBlocks(5);229    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');230    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);231    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);232233    // check setting SponsorTimeout = 7, success234    await waitNewBlocks(2); // 5 + 2235    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');236    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);237    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;238    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);239240    await destroyCollectionExpectSuccess(collectionId);241  });242243  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {244245    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});246    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});247    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');248    await setCollectionSponsorExpectSuccess(collectionId, alice.address);249    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');250    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');251    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');252    const aliceBalanceBefore = await getFreeBalance(alice);253254    // check setting SponsorTimeout = 1, fail255    await waitNewBlocks(1);256    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');257    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);258    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);259260    // check setting SponsorTimeout = 5, success261    await waitNewBlocks(4);262    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');263    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);264    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;265    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);266267    await destroyCollectionExpectSuccess(collectionId);268  });269});270271describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => {272  let alice: IKeyringPair;273  let bob: IKeyringPair;274  let charlie: IKeyringPair;275276  before(async () => {277    await usingApi(async (api, privateKeyWrapper) => {278      alice = privateKeyWrapper('//Alice');279      bob = privateKeyWrapper('//Bob');280      charlie = privateKeyWrapper('//Charlie');281    });282  });283284  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {285    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});286    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});287    const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible');288    await setCollectionSponsorExpectSuccess(collectionId, alice.address);289    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');290    await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible');291    const aliceBalanceBefore = await getFreeBalance(alice);292293    // check setting SponsorTimeout = 5, fail294    await waitNewBlocks(5);295    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');296    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);297    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);298299    // check setting SponsorTimeout = 7, success300    await waitNewBlocks(2); // 5 + 2301    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');302    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);303    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;304    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);305    await destroyCollectionExpectSuccess(collectionId);306  });307308  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {309310    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});311    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});312    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');313    await setCollectionSponsorExpectSuccess(collectionId, alice.address);314    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');315    await transferExpectSuccess(collectionId, tokenId, alice, bob);316    const aliceBalanceBefore = await getFreeBalance(alice);317318    // check setting SponsorTimeout = 1, fail319    await waitNewBlocks(1);320    await transferExpectSuccess(collectionId, tokenId, bob, charlie);321    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);322    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);323324    // check setting SponsorTimeout = 5, success325    await waitNewBlocks(4);326    await transferExpectSuccess(collectionId, tokenId, charlie, bob);327    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);328    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;329    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);330    await destroyCollectionExpectSuccess(collectionId);331  });*/332});333334describe('Collection zero limits (NFT)', () => {335  let alice: IKeyringPair;336  let bob: IKeyringPair;337  let charlie: IKeyringPair;338339  before(async () => {340    await usingPlaygrounds(async (helper, privateKey) => {341      const donor = await privateKey({url: import.meta.url});342      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);343    });344  });345346  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {347    const collection = await helper.nft.mintCollection(alice, {});348    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});349350    for(let i = 0; i < 10; i++){351      await collection.mintToken(alice);352    }353    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);354  });355356  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {357    const collection = await helper.nft.mintCollection(alice, {});358    await collection.setLimits(alice, {sponsorTransferTimeout: 0});359    const token = await collection.mintToken(alice);360361    await collection.setSponsor(alice, alice.address);362    await collection.confirmSponsorship(alice);363364    await token.transfer(alice, {Substrate: bob.address});365    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);366367    // check setting SponsorTimeout = 0, success with next block368    await helper.wait.newBlocks(1);369    await token.transfer(bob, {Substrate: charlie.address});370    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);371    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;372  });373});374375describe('Collection zero limits (Fungible)', () => {376  let alice: IKeyringPair;377  let bob: IKeyringPair;378  let charlie: IKeyringPair;379380  before(async () => {381    await usingPlaygrounds(async (helper, privateKey) => {382      const donor = await privateKey({url: import.meta.url});383      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);384    });385  });386387  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {388    const collection = await helper.ft.mintCollection(alice, {});389    await collection.setLimits(alice, {sponsorTransferTimeout: 0});390    await collection.mint(alice, 3n);391392    await collection.setSponsor(alice, alice.address);393    await collection.confirmSponsorship(alice);394395    await collection.transfer(alice, {Substrate: bob.address}, 2n);396    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);397398    // check setting SponsorTimeout = 0, success with next block399    await helper.wait.newBlocks(1);400    await collection.transfer(bob, {Substrate: charlie.address});401    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);402    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;403  });404});405406describe('Collection zero limits (ReFungible)', () => {407  let alice: IKeyringPair;408  let bob: IKeyringPair;409  let charlie: IKeyringPair;410411  before(async function() {412    await usingPlaygrounds(async (helper, privateKey) => {413      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);414415      const donor = await privateKey({url: import.meta.url});416      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);417    });418  });419420  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {421    const collection = await helper.rft.mintCollection(alice, {});422    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});423    for(let i = 0; i < 10; i++){424      await collection.mintToken(alice);425    }426    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);427  });428429  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {430    const collection = await helper.rft.mintCollection(alice, {});431    await collection.setLimits(alice, {sponsorTransferTimeout: 0});432    const token = await collection.mintToken(alice, 3n);433434    await collection.setSponsor(alice, alice.address);435    await collection.confirmSponsorship(alice);436437    await token.transfer(alice, {Substrate: bob.address}, 2n);438    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);439440    // check setting SponsorTimeout = 0, success with next block441    await helper.wait.newBlocks(1);442    await token.transfer(bob, {Substrate: charlie.address});443    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);444    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;445  });446});447448describe('Effective collection limits (NFT)', () => {449  let alice: IKeyringPair;450451  before(async () => {452    await usingPlaygrounds(async (helper, privateKey) => {453      const donor = await privateKey({url: import.meta.url});454      [alice] = await helper.arrange.createAccounts([10n], donor);455    });456  });457458  itSub('Effective collection limits', async ({helper}) => {459    const collection = await helper.nft.mintCollection(alice, {});460    await collection.setLimits(alice, {ownerCanTransfer: true});461462    {463      // Check that limits are undefined464      const collectionInfo = await collection.getData();465      const limits = collectionInfo?.raw.limits;466      expect(limits).to.be.any;467468      expect(limits.accountTokenOwnershipLimit).to.be.null;469      expect(limits.sponsoredDataSize).to.be.null;470      expect(limits.sponsoredDataRateLimit).to.be.null;471      expect(limits.tokenLimit).to.be.null;472      expect(limits.sponsorTransferTimeout).to.be.null;473      expect(limits.sponsorApproveTimeout).to.be.null;474      expect(limits.ownerCanTransfer).to.be.true;475      expect(limits.ownerCanDestroy).to.be.null;476      expect(limits.transfersEnabled).to.be.null;477    }478479    { // Check that limits is undefined for non-existent collection480      const limits = await helper.collection.getEffectiveLimits(999999);481      expect(limits).to.be.null;482    }483484    { // Check that default values defined for collection limits485      const limits = await collection.getEffectiveLimits();486487      expect(limits.accountTokenOwnershipLimit).to.be.eq(100000000);488      expect(limits.sponsoredDataSize).to.be.eq(2048);489      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});490      expect(limits.tokenLimit).to.be.eq(4294967295);491      expect(limits.sponsorTransferTimeout).to.be.eq(5);492      expect(limits.sponsorApproveTimeout).to.be.eq(5);493      expect(limits.ownerCanTransfer).to.be.true;494      expect(limits.ownerCanDestroy).to.be.true;495      expect(limits.transfersEnabled).to.be.true;496    }497498    {499      // Check the values for collection limits500      await collection.setLimits(alice, {501        accountTokenOwnershipLimit: 99_999,502        sponsoredDataSize: 1024,503        tokenLimit: 123,504        transfersEnabled: false,505      });506507      const limits = await collection.getEffectiveLimits();508509      expect(limits.accountTokenOwnershipLimit).to.be.eq(99999);510      expect(limits.sponsoredDataSize).to.be.eq(1024);511      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});512      expect(limits.tokenLimit).to.be.eq(123);513      expect(limits.sponsorTransferTimeout).to.be.eq(5);514      expect(limits.sponsorApproveTimeout).to.be.eq(5);515      expect(limits.ownerCanTransfer).to.be.true;516      expect(limits.ownerCanDestroy).to.be.true;517      expect(limits.transfersEnabled).to.be.false;518    }519  });520});
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -746,7 +746,7 @@
 				.ok_or(ArithmeticError::Overflow)?;
 
 			ensure!(
-				balance_to < collection.limits.account_token_ownership_limit(),
+				balance_to <= collection.limits.account_token_ownership_limit(),
 				<CommonError<T>>::AccountTokenLimitExceeded,
 			);
 
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -679,7 +679,7 @@
 				.checked_add(1)
 				.ok_or(ArithmeticError::Overflow)?;
 			ensure!(
-				account_balance_to < collection.limits.account_token_ownership_limit(),
+				account_balance_to <= collection.limits.account_token_ownership_limit(),
 				<CommonError<T>>::AccountTokenLimitExceeded,
 			);