1234567891011121314151617import {ApiPromise} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import {expect} from 'chai';20import {alicesPublicKey, bobsPublicKey} from './accounts';21import getBalance from './substrate/get-balance';22import {default as usingApi, submitTransactionAsync} from './substrate/substrate-api';23import {24 burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess,25 destroyCollectionExpectSuccess,26 findUnusedAddress,27 getCreateCollectionResult,28 getCreateItemResult,29 transferExpectFailure,30 transferExpectSuccess,31 addCollectionAdminExpectSuccess,32 getCreatedCollectionCount,33 toSubstrateAddress,34 getTokenOwner,35 normalizeAccountId,36 getBalance as getTokenBalance,37 transferFromExpectSuccess,38 transferFromExpectFail,39} from './util/helpers';40import {41 subToEth,42 itWeb3, 43} from './eth/util/helpers';4445let alice: IKeyringPair;46let bob: IKeyringPair;47let charlie: IKeyringPair;4849describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {50 it('Balance transfers and check balance', async () => {51 await usingApi(async (api, privateKeyWrapper) => {52 const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);5354 const alicePrivateKey = privateKeyWrapper('//Alice');5556 const transfer = api.tx.balances.transfer(bobsPublicKey, 1n);57 const events = await submitTransactionAsync(alicePrivateKey, transfer);58 const result = getCreateItemResult(events);59 60 expect(result.success).to.be.true;6162 const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);6364 65 expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;66 67 expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;68 });69 });7071 it('Inability to pay fees error message is correct', async () => {72 await usingApi(async (api) => {73 74 const pk = await findUnusedAddress(api);7576 const badTransfer = api.tx.balances.transfer(bobsPublicKey, 1n);77 78 const badTransaction = async () => {79 const events = await submitTransactionAsync(pk, badTransfer);80 const result = getCreateCollectionResult(events);81 82 expect(result.success).to.be.false;83 };84 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');85 });86 });8788 it('User can transfer owned token', async () => {89 await usingApi(async (api, privateKeyWrapper) => {90 const alice = privateKeyWrapper('//Alice');91 const bob = privateKeyWrapper('//Bob');92 93 const nftCollectionId = await createCollectionExpectSuccess();94 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');95 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 'NFT');96 97 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});98 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');99 await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob, 1, 'Fungible');100 101 const reFungibleCollectionId = await102 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});103 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');104 await transferExpectSuccess(105 reFungibleCollectionId,106 newReFungibleTokenId,107 alice,108 bob,109 100,110 'ReFungible',111 );112 });113 });114115 it('Collection admin can transfer owned token', async () => {116 await usingApi(async (api, privateKeyWrapper) => {117 const alice = privateKeyWrapper('//Alice');118 const bob = privateKeyWrapper('//Bob');119 120 const nftCollectionId = await createCollectionExpectSuccess();121 await addCollectionAdminExpectSuccess(alice, nftCollectionId, bob.address);122 const newNftTokenId = await createItemExpectSuccess(bob, nftCollectionId, 'NFT', bob.address);123 await transferExpectSuccess(nftCollectionId, newNftTokenId, bob, alice, 1, 'NFT');124 125 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});126 await addCollectionAdminExpectSuccess(alice, fungibleCollectionId, bob.address);127 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible', bob.address);128 await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, 1, 'Fungible');129 130 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});131 await addCollectionAdminExpectSuccess(alice, reFungibleCollectionId, bob.address);132 const newReFungibleTokenId = await createItemExpectSuccess(bob, reFungibleCollectionId, 'ReFungible', bob.address);133 await transferExpectSuccess(134 reFungibleCollectionId,135 newReFungibleTokenId,136 bob,137 alice,138 100,139 'ReFungible',140 );141 });142 });143});144145describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => {146 before(async () => {147 await usingApi(async (api, privateKeyWrapper) => {148 alice = privateKeyWrapper('//Alice');149 bob = privateKeyWrapper('//Bob');150 charlie = privateKeyWrapper('//Charlie');151 });152 });153 it('Transfer with not existed collection_id', async () => {154 await usingApi(async (api) => {155 156 const nftCollectionCount = await getCreatedCollectionCount(api);157 await transferExpectFailure(nftCollectionCount + 1, 1, alice, bob, 1);158 159 const fungibleCollectionCount = await getCreatedCollectionCount(api);160 await transferExpectFailure(fungibleCollectionCount + 1, 0, alice, bob, 1);161 162 const reFungibleCollectionCount = await getCreatedCollectionCount(api);163 await transferExpectFailure(reFungibleCollectionCount + 1, 1, alice, bob, 1);164 });165 });166 it('Transfer with deleted collection_id', async () => {167 168 const nftCollectionId = await createCollectionExpectSuccess();169 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');170 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId);171 await destroyCollectionExpectSuccess(nftCollectionId);172 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);173 174 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});175 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');176 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);177 await destroyCollectionExpectSuccess(fungibleCollectionId);178 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);179 180 const reFungibleCollectionId = await181 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});182 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');183 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);184 await destroyCollectionExpectSuccess(reFungibleCollectionId);185 await transferExpectFailure(186 reFungibleCollectionId,187 newReFungibleTokenId,188 alice,189 bob,190 1,191 );192 });193 it('Transfer with not existed item_id', async () => {194 195 const nftCollectionId = await createCollectionExpectSuccess();196 await transferExpectFailure(nftCollectionId, 2, alice, bob, 1);197 198 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});199 await transferExpectFailure(fungibleCollectionId, 2, alice, bob, 1);200 201 const reFungibleCollectionId = await202 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});203 await transferExpectFailure(204 reFungibleCollectionId,205 2,206 alice,207 bob,208 1,209 );210 });211 it('Transfer with deleted item_id', async () => {212 213 const nftCollectionId = await createCollectionExpectSuccess();214 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');215 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);216 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);217 218 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});219 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');220 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);221 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);222 223 const reFungibleCollectionId = await224 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});225 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');226 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);227 await transferExpectFailure(228 reFungibleCollectionId,229 newReFungibleTokenId,230 alice,231 bob,232 1,233 );234 });235 it('Transfer with recipient that is not owner', async () => {236 237 const nftCollectionId = await createCollectionExpectSuccess();238 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');239 await transferExpectFailure(nftCollectionId, newNftTokenId, charlie, bob, 1);240 241 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});242 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');243 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, charlie, bob, 1);244 245 const reFungibleCollectionId = await246 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});247 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');248 await transferExpectFailure(249 reFungibleCollectionId,250 newReFungibleTokenId,251 charlie,252 bob,253 1,254 );255 });256});257258describe('Zero value transfer(From)', () => {259 before(async () => {260 await usingApi(async (api, privateKeyWrapper) => {261 alice = privateKeyWrapper('//Alice');262 bob = privateKeyWrapper('//Bob');263 });264 });265266 it('NFT', async () => {267 await usingApi(async (api: ApiPromise) => {268 const nftCollectionId = await createCollectionExpectSuccess();269 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');270271 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), nftCollectionId, newNftTokenId, 0);272 await submitTransactionAsync(alice, transferTx);273 const address = normalizeAccountId(await getTokenOwner(api, nftCollectionId, newNftTokenId));274275 expect(toSubstrateAddress(address)).to.be.equal(alice.address);276 });277 });278279 it('RFT', async () => {280 await usingApi(async (api: ApiPromise) => {281 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});282 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');283 const balanceBeforeAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);284 const balanceBeforeBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);285286 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0);287 await submitTransactionAsync(alice, transferTx);288289 const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);290 const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);291292 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);293 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);294 });295 });296297 it('Fungible', async () => {298 await usingApi(async (api: ApiPromise) => {299 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});300 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');301 const balanceBeforeAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);302 const balanceBeforeBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);303304 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0);305 await submitTransactionAsync(alice, transferTx);306307 const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);308 const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);309310 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);311 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);312 });313 });314});315316describe('Transfers to self (potentially over substrate-evm boundary)', () => {317 itWeb3('Transfers to self. In case of same frontend', async ({api, privateKeyWrapper}) => {318 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});319 const alice = privateKeyWrapper('//Alice');320 const aliceProxy = subToEth(alice.address);321 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});322 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, 10, 'Fungible');323 const balanceAliceBefore = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);324 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, {Ethereum: aliceProxy}, 10, 'Fungible');325 const balanceAliceAfter = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);326 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);327 });328329 itWeb3('Transfers to self. In case of substrate-evm boundary', async ({api, privateKeyWrapper}) => {330 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});331 const alice = privateKeyWrapper('//Alice');332 const aliceProxy = subToEth(alice.address);333 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});334 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);335 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy} , 10, 'Fungible');336 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, alice, 10, 'Fungible');337 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);338 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);339 });340341 itWeb3('Transfers to self. In case of inside substrate-evm', async ({api, privateKeyWrapper}) => {342 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});343 const alice = privateKeyWrapper('//Alice');344 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});345 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);346 await transferExpectSuccess(collectionId, tokenId, alice, alice , 10, 'Fungible');347 await transferFromExpectSuccess(collectionId, tokenId, alice, alice, alice, 10, 'Fungible');348 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);349 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);350 });351352 itWeb3('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({api, privateKeyWrapper}) => {353 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});354 const alice = privateKeyWrapper('//Alice');355 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});356 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);357 await transferExpectFailure(collectionId, tokenId, alice, alice , 11);358 await transferFromExpectFail(collectionId, tokenId, alice, alice, alice, 11);359 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);360 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);361 });362});