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} from './util/helpers';39import {40 subToEth,41 itWeb3, 42} from './eth/util/helpers';4344let alice: IKeyringPair;45let bob: IKeyringPair;46let charlie: IKeyringPair;4748describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {49 before(async () => {50 await usingApi(async (api, privateKeyWrapper) => {51 alice = privateKeyWrapper('//Alice');52 bob = privateKeyWrapper('//Bob');53 });54 });55 56 it('Balance transfers and check balance', async () => {57 await usingApi(async (api, privateKeyWrapper) => {58 const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alice.address, bob.address]);5960 const transfer = api.tx.balances.transfer(bob.address, 1n);61 const events = await submitTransactionAsync(alice, transfer);62 const result = getCreateItemResult(events);63 64 expect(result.success).to.be.true;6566 const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alice.address, bob.address]);6768 69 expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;70 71 expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;72 });73 });7475 it('Inability to pay fees error message is correct', async () => {76 await usingApi(async (api, privateKeyWrapper) => {77 78 const pk = await findUnusedAddress(api, privateKeyWrapper);7980 const badTransfer = api.tx.balances.transfer(bob.address, 1n);81 82 const badTransaction = async () => {83 const events = await submitTransactionAsync(pk, badTransfer);84 const result = getCreateCollectionResult(events);85 86 expect(result.success).to.be.false;87 };88 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');89 });90 });9192 it('User can transfer owned token', async () => {93 await usingApi(async (api, privateKeyWrapper) => {94 95 const nftCollectionId = await createCollectionExpectSuccess();96 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');97 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 'NFT');98 99 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});100 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');101 await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob, 1, 'Fungible');102 103 const reFungibleCollectionId = await104 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});105 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');106 await transferExpectSuccess(107 reFungibleCollectionId,108 newReFungibleTokenId,109 alice,110 bob,111 100,112 'ReFungible',113 );114 });115 });116117 it('Collection admin can transfer owned token', async () => {118 await usingApi(async (api, privateKeyWrapper) => {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 aliceProxy = subToEth(alice.address);320 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});321 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, 10, 'Fungible');322 const balanceAliceBefore = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);323 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, {Ethereum: aliceProxy}, 10, 'Fungible');324 const balanceAliceAfter = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);325 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);326 });327328 itWeb3('Transfers to self. In case of substrate-evm boundary', async ({api, privateKeyWrapper}) => {329 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});330 const aliceProxy = subToEth(alice.address);331 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});332 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);333 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy} , 10, 'Fungible');334 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, alice, 10, 'Fungible');335 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);336 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);337 });338339 itWeb3('Transfers to self. In case of inside substrate-evm', async ({api, privateKeyWrapper}) => {340 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});341 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});342 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);343 await transferExpectSuccess(collectionId, tokenId, alice, alice , 10, 'Fungible');344 await transferFromExpectSuccess(collectionId, tokenId, alice, alice, alice, 10, 'Fungible');345 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);346 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);347 });348349 itWeb3('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({api, privateKeyWrapper}) => {350 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});351 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});352 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);353 await transferExpectFailure(collectionId, tokenId, alice, alice , 11);354 await transferFromExpectFail(collectionId, tokenId, alice, alice, alice, 11);355 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);356 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);357 });358});