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 privateKey from './substrate/privateKey';23import {default as usingApi, submitTransactionAsync} from './substrate/substrate-api';24import {25 burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess,26 destroyCollectionExpectSuccess,27 findUnusedAddress,28 getCreateCollectionResult,29 getCreateItemResult,30 transferExpectFailure,31 transferExpectSuccess,32 addCollectionAdminExpectSuccess,33 getCreatedCollectionCount,34 toSubstrateAddress,35 getTokenOwner,36 normalizeAccountId,37 getBalance as getTokenBalance,38 transferFromExpectSuccess,39 transferFromExpectFail,40} from './util/helpers';41import {42 subToEth,43 itWeb3, 44} from './eth/util/helpers';4546let alice: IKeyringPair;47let bob: IKeyringPair;48let charlie: IKeyringPair;4950describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {51 it('Balance transfers and check balance', async () => {52 await usingApi(async (api: ApiPromise) => {53 const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);5455 const alicePrivateKey = privateKey('//Alice');5657 const transfer = api.tx.balances.transfer(bobsPublicKey, 1n);58 const events = await submitTransactionAsync(alicePrivateKey, transfer);59 const result = getCreateItemResult(events);60 61 expect(result.success).to.be.true;6263 const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);6465 66 expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;67 68 expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;69 });70 });7172 it('Inability to pay fees error message is correct', async () => {73 await usingApi(async (api) => {74 75 const pk = await findUnusedAddress(api);7677 const badTransfer = api.tx.balances.transfer(bobsPublicKey, 1n);78 79 const badTransaction = async () => {80 const events = await submitTransactionAsync(pk, badTransfer);81 const result = getCreateCollectionResult(events);82 83 expect(result.success).to.be.false;84 };85 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');86 });87 });8889 it('User can transfer owned token', async () => {90 await usingApi(async () => {91 const alice = privateKey('//Alice');92 const bob = privateKey('//Bob');93 94 const nftCollectionId = await createCollectionExpectSuccess();95 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');96 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 'NFT');97 98 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});99 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');100 await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob, 1, 'Fungible');101 102 const reFungibleCollectionId = await103 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});104 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');105 await transferExpectSuccess(106 reFungibleCollectionId,107 newReFungibleTokenId,108 alice,109 bob,110 100,111 'ReFungible',112 );113 });114 });115116 it('Collection admin can transfer owned token', async () => {117 await usingApi(async () => {118 const alice = privateKey('//Alice');119 const bob = privateKey('//Bob');120 121 const nftCollectionId = await createCollectionExpectSuccess();122 await addCollectionAdminExpectSuccess(alice, nftCollectionId, bob.address);123 const newNftTokenId = await createItemExpectSuccess(bob, nftCollectionId, 'NFT', bob.address);124 await transferExpectSuccess(nftCollectionId, newNftTokenId, bob, alice, 1, 'NFT');125 126 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});127 await addCollectionAdminExpectSuccess(alice, fungibleCollectionId, bob.address);128 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible', bob.address);129 await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, 1, 'Fungible');130 131 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});132 await addCollectionAdminExpectSuccess(alice, reFungibleCollectionId, bob.address);133 const newReFungibleTokenId = await createItemExpectSuccess(bob, reFungibleCollectionId, 'ReFungible', bob.address);134 await transferExpectSuccess(135 reFungibleCollectionId,136 newReFungibleTokenId,137 bob,138 alice,139 100,140 'ReFungible',141 );142 });143 });144});145146describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => {147 before(async () => {148 await usingApi(async () => {149 alice = privateKey('//Alice');150 bob = privateKey('//Bob');151 charlie = privateKey('//Charlie');152 });153 });154 it('Transfer with not existed collection_id', async () => {155 await usingApi(async (api) => {156 157 const nftCollectionCount = await getCreatedCollectionCount(api);158 await transferExpectFailure(nftCollectionCount + 1, 1, alice, bob, 1);159 160 const fungibleCollectionCount = await getCreatedCollectionCount(api);161 await transferExpectFailure(fungibleCollectionCount + 1, 0, alice, bob, 1);162 163 const reFungibleCollectionCount = await getCreatedCollectionCount(api);164 await transferExpectFailure(reFungibleCollectionCount + 1, 1, alice, bob, 1);165 });166 });167 it('Transfer with deleted collection_id', async () => {168 169 const nftCollectionId = await createCollectionExpectSuccess();170 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');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 destroyCollectionExpectSuccess(fungibleCollectionId);177 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);178 179 const reFungibleCollectionId = await180 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});181 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');182 await destroyCollectionExpectSuccess(reFungibleCollectionId);183 await transferExpectFailure(184 reFungibleCollectionId,185 newReFungibleTokenId,186 alice,187 bob,188 1,189 );190 });191 it('Transfer with not existed item_id', async () => {192 193 const nftCollectionId = await createCollectionExpectSuccess();194 await transferExpectFailure(nftCollectionId, 2, alice, bob, 1);195 196 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});197 await transferExpectFailure(fungibleCollectionId, 2, alice, bob, 1);198 199 const reFungibleCollectionId = await200 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});201 await transferExpectFailure(202 reFungibleCollectionId,203 2,204 alice,205 bob,206 1,207 );208 });209 it('Transfer with deleted item_id', async () => {210 211 const nftCollectionId = await createCollectionExpectSuccess();212 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');213 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);214 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);215 216 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});217 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');218 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);219 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);220 221 const reFungibleCollectionId = await222 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});223 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');224 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);225 await transferExpectFailure(226 reFungibleCollectionId,227 newReFungibleTokenId,228 alice,229 bob,230 1,231 );232 });233 it('Transfer with recipient that is not owner', async () => {234 235 const nftCollectionId = await createCollectionExpectSuccess();236 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');237 await transferExpectFailure(nftCollectionId, newNftTokenId, charlie, bob, 1);238 239 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});240 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');241 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, charlie, bob, 1);242 243 const reFungibleCollectionId = await244 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});245 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');246 await transferExpectFailure(247 reFungibleCollectionId,248 newReFungibleTokenId,249 charlie,250 bob,251 1,252 );253 });254});255256describe('Zero value transfer(From)', () => {257 before(async () => {258 await usingApi(async () => {259 alice = privateKey('//Alice');260 bob = privateKey('//Bob');261 });262 });263264 it('NFT', async () => {265 await usingApi(async (api: ApiPromise) => {266 const nftCollectionId = await createCollectionExpectSuccess();267 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');268269 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), nftCollectionId, newNftTokenId, 0);270 await submitTransactionAsync(alice, transferTx);271 const address = normalizeAccountId(await getTokenOwner(api, nftCollectionId, newNftTokenId));272273 expect(toSubstrateAddress(address)).to.be.equal(alice.address);274 });275 });276277 it('RFT', async () => {278 await usingApi(async (api: ApiPromise) => {279 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});280 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');281 const balanceBeforeAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);282 const balanceBeforeBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);283284 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0);285 await submitTransactionAsync(alice, transferTx);286287 const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);288 const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);289290 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);291 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);292 });293 });294295 it('Fungible', async () => {296 await usingApi(async (api: ApiPromise) => {297 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});298 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');299 const balanceBeforeAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);300 const balanceBeforeBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);301302 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0);303 await submitTransactionAsync(alice, transferTx);304305 const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);306 const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);307308 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);309 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);310 });311 });312});313314describe('Transfers to self (potentially over substrate-evm boundary)', () => {315 itWeb3('Transfers to self. In case of same frontend', async ({api}) => {316 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});317 const alice = privateKey('//Alice');318 const aliceProxy = subToEth(alice.address);319 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});320 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, 10, 'Fungible');321 const balanceAliceBefore = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);322 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, {Ethereum: aliceProxy}, 10, 'Fungible');323 const balanceAliceAfter = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);324 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);325 });326327 itWeb3('Transfers to self. In case of substrate-evm boundary', async ({api}) => {328 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});329 const alice = privateKey('//Alice');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}) => {340 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});341 const alice = privateKey('//Alice');342 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});343 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);344 await transferExpectSuccess(collectionId, tokenId, alice, alice , 10, 'Fungible');345 await transferFromExpectSuccess(collectionId, tokenId, alice, alice, alice, 10, 'Fungible');346 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);347 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);348 });349350 itWeb3('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({api}) => {351 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});352 const alice = privateKey('//Alice');353 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});354 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);355 await transferExpectFailure(collectionId, tokenId, alice, alice , 11);356 await transferFromExpectFail(collectionId, tokenId, alice, alice, alice, 11);357 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);358 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);359 });360});