1234567891011121314151617import {ApiPromise} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import {expect} from 'chai';20import getBalance from './substrate/get-balance';21import {default as usingApi, submitTransactionAsync} from './substrate/substrate-api';22import {23 burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess,24 destroyCollectionExpectSuccess,25 findUnusedAddress,26 getCreateCollectionResult,27 getCreateItemResult,28 transferExpectFailure,29 transferExpectSuccess,30 addCollectionAdminExpectSuccess,31 getCreatedCollectionCount,32 toSubstrateAddress,33 getTokenOwner,34 normalizeAccountId,35 getBalance as getTokenBalance,36 transferFromExpectSuccess,37 transferFromExpectFail,38 requirePallets,39 Pallets,40} from './util/helpers';41import {42 subToEth,43 itWeb3, 44} from './eth/util/helpers';45import {request} from 'https';4647let alice: IKeyringPair;48let bob: IKeyringPair;49let charlie: IKeyringPair;5051describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {52 before(async () => {53 await usingApi(async (api, privateKeyWrapper) => {54 alice = privateKeyWrapper('//Alice');55 bob = privateKeyWrapper('//Bob');56 });57 });58 59 it('Balance transfers and check balance', async () => {60 await usingApi(async (api, privateKeyWrapper) => {61 const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alice.address, bob.address]);6263 const transfer = api.tx.balances.transfer(bob.address, 1n);64 const events = await submitTransactionAsync(alice, transfer);65 const result = getCreateItemResult(events);66 67 expect(result.success).to.be.true;6869 const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alice.address, bob.address]);7071 72 expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;73 74 expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;75 });76 });7778 it('Inability to pay fees error message is correct', async () => {79 await usingApi(async (api, privateKeyWrapper) => {80 81 const pk = await findUnusedAddress(api, privateKeyWrapper);8283 const badTransfer = api.tx.balances.transfer(bob.address, 1n);84 85 const badTransaction = async () => {86 const events = await submitTransactionAsync(pk, badTransfer);87 const result = getCreateCollectionResult(events);88 89 expect(result.success).to.be.false;90 };91 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');92 });93 });9495 it('[nft] User can transfer owned token', async () => {96 const nftCollectionId = await createCollectionExpectSuccess();97 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');98 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 'NFT');99 });100101 it('[fungible] User can transfer owned token', async () => {102 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});103 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');104 await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob, 1, 'Fungible');105 });106107 it('[refungible] User can transfer owned token', async function() {108 await requirePallets(this, [Pallets.ReFungible]);109110 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});111 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');112 await transferExpectSuccess(113 reFungibleCollectionId,114 newReFungibleTokenId,115 alice,116 bob,117 100,118 'ReFungible',119 );120 });121122 it('[nft] Collection admin can transfer owned token', async () => {123 const nftCollectionId = await createCollectionExpectSuccess();124 await addCollectionAdminExpectSuccess(alice, nftCollectionId, bob.address);125 const newNftTokenId = await createItemExpectSuccess(bob, nftCollectionId, 'NFT', bob.address);126 await transferExpectSuccess(nftCollectionId, newNftTokenId, bob, alice, 1, 'NFT');127 });128129 it('[fungible] Collection admin can transfer owned token', async () => {130 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});131 await addCollectionAdminExpectSuccess(alice, fungibleCollectionId, bob.address);132 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible', bob.address);133 await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, 1, 'Fungible');134 });135136 it('[refungible] Collection admin can transfer owned token', async function() {137 await requirePallets(this, [Pallets.ReFungible]);138139 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});140 await addCollectionAdminExpectSuccess(alice, reFungibleCollectionId, bob.address);141 const newReFungibleTokenId = await createItemExpectSuccess(bob, reFungibleCollectionId, 'ReFungible', bob.address);142 await transferExpectSuccess(143 reFungibleCollectionId,144 newReFungibleTokenId,145 bob,146 alice,147 100,148 'ReFungible',149 );150 });151});152153describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => {154 before(async () => {155 await usingApi(async (api, privateKeyWrapper) => {156 alice = privateKeyWrapper('//Alice');157 bob = privateKeyWrapper('//Bob');158 charlie = privateKeyWrapper('//Charlie');159 });160 });161162 it('[nft] Transfer with not existed collection_id', async () => {163 await usingApi(async (api) => {164 const nftCollectionCount = await getCreatedCollectionCount(api);165 await transferExpectFailure(nftCollectionCount + 1, 1, alice, bob, 1);166 });167 });168169 it('[fungible] Transfer with not existed collection_id', async () => {170 await usingApi(async (api) => {171 const fungibleCollectionCount = await getCreatedCollectionCount(api);172 await transferExpectFailure(fungibleCollectionCount + 1, 0, alice, bob, 1);173 });174 });175176 it('[refungible] Transfer with not existed collection_id', async function() {177 await requirePallets(this, [Pallets.ReFungible]);178179 await usingApi(async (api) => {180 const reFungibleCollectionCount = await getCreatedCollectionCount(api);181 await transferExpectFailure(reFungibleCollectionCount + 1, 1, alice, bob, 1);182 });183 });184185 it('[nft] Transfer with deleted collection_id', async () => {186 const nftCollectionId = await createCollectionExpectSuccess();187 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');188 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId);189 await destroyCollectionExpectSuccess(nftCollectionId);190 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);191 });192193 it('[fungible] Transfer with deleted collection_id', async () => {194 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});195 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');196 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);197 await destroyCollectionExpectSuccess(fungibleCollectionId);198 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);199 });200201 it('[refungible] Transfer with deleted collection_id', async function() {202 await requirePallets(this, [Pallets.ReFungible]);203204 const reFungibleCollectionId = await205 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});206 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');207 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);208 await destroyCollectionExpectSuccess(reFungibleCollectionId);209 await transferExpectFailure(210 reFungibleCollectionId,211 newReFungibleTokenId,212 alice,213 bob,214 1,215 );216 });217218 it('[nft] Transfer with not existed item_id', async () => {219 const nftCollectionId = await createCollectionExpectSuccess();220 await transferExpectFailure(nftCollectionId, 2, alice, bob, 1);221 });222223 it('[fungible] Transfer with not existed item_id', async () => {224 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});225 await transferExpectFailure(fungibleCollectionId, 2, alice, bob, 1);226 });227228 it('[refungible] Transfer with not existed item_id', async function() {229 await requirePallets(this, [Pallets.ReFungible]);230231 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});232 await transferExpectFailure(233 reFungibleCollectionId,234 2,235 alice,236 bob,237 1,238 );239 });240241 it('[nft] Transfer with deleted item_id', async () => {242 const nftCollectionId = await createCollectionExpectSuccess();243 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');244 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);245 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);246 });247248 it('[fungible] Transfer with deleted item_id', async () => {249 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});250 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');251 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);252 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);253 });254255 it('[refungible] Transfer with deleted item_id', async function() {256 await requirePallets(this, [Pallets.ReFungible]);257258 const reFungibleCollectionId = await259 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});260 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');261 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);262 await transferExpectFailure(263 reFungibleCollectionId,264 newReFungibleTokenId,265 alice,266 bob,267 1,268 );269 });270271 it('[nft] Transfer with recipient that is not owner', async () => {272 const nftCollectionId = await createCollectionExpectSuccess();273 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');274 await transferExpectFailure(nftCollectionId, newNftTokenId, charlie, bob, 1);275 });276277 it('[fungible] Transfer with recipient that is not owner', async () => {278 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});279 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');280 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, charlie, bob, 1);281 });282283 it('[refungible] Transfer with recipient that is not owner', async function() {284 await requirePallets(this, [Pallets.ReFungible]);285286 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});287 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');288 await transferExpectFailure(289 reFungibleCollectionId,290 newReFungibleTokenId,291 charlie,292 bob,293 1,294 );295 });296});297298describe('Zero value transfer(From)', () => {299 before(async () => {300 await usingApi(async (api, privateKeyWrapper) => {301 alice = privateKeyWrapper('//Alice');302 bob = privateKeyWrapper('//Bob');303 });304 });305306 it('NFT', async () => {307 await usingApi(async (api: ApiPromise) => {308 const nftCollectionId = await createCollectionExpectSuccess();309 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');310311 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), nftCollectionId, newNftTokenId, 0);312 await submitTransactionAsync(alice, transferTx);313 const address = normalizeAccountId(await getTokenOwner(api, nftCollectionId, newNftTokenId));314315 expect(toSubstrateAddress(address)).to.be.equal(alice.address);316 });317 });318319 it('RFT', async function() {320 await requirePallets(this, [Pallets.ReFungible]);321322 await usingApi(async (api: ApiPromise) => {323 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});324 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');325 const balanceBeforeAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);326 const balanceBeforeBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);327328 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0);329 await submitTransactionAsync(alice, transferTx);330331 const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);332 const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);333334 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);335 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);336 });337 });338339 it('Fungible', async () => {340 await usingApi(async (api: ApiPromise) => {341 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});342 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');343 const balanceBeforeAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);344 const balanceBeforeBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);345346 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0);347 await submitTransactionAsync(alice, transferTx);348349 const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);350 const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);351352 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);353 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);354 });355 });356});357358describe('Transfers to self (potentially over substrate-evm boundary)', () => {359 itWeb3('Transfers to self. In case of same frontend', async ({api, privateKeyWrapper}) => {360 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});361 const aliceProxy = subToEth(alice.address);362 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});363 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, 10, 'Fungible');364 const balanceAliceBefore = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);365 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, {Ethereum: aliceProxy}, 10, 'Fungible');366 const balanceAliceAfter = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);367 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);368 });369370 itWeb3('Transfers to self. In case of substrate-evm boundary', async ({api, privateKeyWrapper}) => {371 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});372 const aliceProxy = subToEth(alice.address);373 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});374 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);375 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy} , 10, 'Fungible');376 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, alice, 10, 'Fungible');377 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);378 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);379 });380381 itWeb3('Transfers to self. In case of inside substrate-evm', async ({api, privateKeyWrapper}) => {382 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});383 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});384 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);385 await transferExpectSuccess(collectionId, tokenId, alice, alice , 10, 'Fungible');386 await transferFromExpectSuccess(collectionId, tokenId, alice, alice, alice, 10, 'Fungible');387 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);388 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);389 });390391 itWeb3('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({api, privateKeyWrapper}) => {392 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});393 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});394 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);395 await transferExpectFailure(collectionId, tokenId, alice, alice , 11);396 await transferFromExpectFail(collectionId, tokenId, alice, alice, alice, 11);397 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);398 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);399 });400});