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 burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId);172 await destroyCollectionExpectSuccess(nftCollectionId);173 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);174 175 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});176 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');177 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);178 await destroyCollectionExpectSuccess(fungibleCollectionId);179 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);180 181 const reFungibleCollectionId = await182 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});183 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');184 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);185 await destroyCollectionExpectSuccess(reFungibleCollectionId);186 await transferExpectFailure(187 reFungibleCollectionId,188 newReFungibleTokenId,189 alice,190 bob,191 1,192 );193 });194 it('Transfer with not existed item_id', async () => {195 196 const nftCollectionId = await createCollectionExpectSuccess();197 await transferExpectFailure(nftCollectionId, 2, alice, bob, 1);198 199 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});200 await transferExpectFailure(fungibleCollectionId, 2, alice, bob, 1);201 202 const reFungibleCollectionId = await203 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});204 await transferExpectFailure(205 reFungibleCollectionId,206 2,207 alice,208 bob,209 1,210 );211 });212 it('Transfer with deleted item_id', async () => {213 214 const nftCollectionId = await createCollectionExpectSuccess();215 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');216 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);217 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);218 219 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});220 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');221 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);222 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1);223 224 const reFungibleCollectionId = await225 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});226 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');227 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);228 await transferExpectFailure(229 reFungibleCollectionId,230 newReFungibleTokenId,231 alice,232 bob,233 1,234 );235 });236 it('Transfer with recipient that is not owner', async () => {237 238 const nftCollectionId = await createCollectionExpectSuccess();239 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');240 await transferExpectFailure(nftCollectionId, newNftTokenId, charlie, bob, 1);241 242 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});243 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');244 await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, charlie, bob, 1);245 246 const reFungibleCollectionId = await247 createCollectionExpectSuccess({mode: {type: 'ReFungible'}});248 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');249 await transferExpectFailure(250 reFungibleCollectionId,251 newReFungibleTokenId,252 charlie,253 bob,254 1,255 );256 });257});258259describe('Zero value transfer(From)', () => {260 before(async () => {261 await usingApi(async () => {262 alice = privateKey('//Alice');263 bob = privateKey('//Bob');264 });265 });266267 it('NFT', async () => {268 await usingApi(async (api: ApiPromise) => {269 const nftCollectionId = await createCollectionExpectSuccess();270 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');271272 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), nftCollectionId, newNftTokenId, 0);273 await submitTransactionAsync(alice, transferTx);274 const address = normalizeAccountId(await getTokenOwner(api, nftCollectionId, newNftTokenId));275276 expect(toSubstrateAddress(address)).to.be.equal(alice.address);277 });278 });279280 it('RFT', async () => {281 await usingApi(async (api: ApiPromise) => {282 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});283 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');284 const balanceBeforeAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);285 const balanceBeforeBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);286287 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0);288 await submitTransactionAsync(alice, transferTx);289290 const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);291 const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);292293 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);294 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);295 });296 });297298 it('Fungible', async () => {299 await usingApi(async (api: ApiPromise) => {300 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});301 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');302 const balanceBeforeAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);303 const balanceBeforeBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);304305 const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0);306 await submitTransactionAsync(alice, transferTx);307308 const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);309 const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);310311 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);312 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);313 });314 });315});316317describe('Transfers to self (potentially over substrate-evm boundary)', () => {318 itWeb3('Transfers to self. In case of same frontend', async ({api}) => {319 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});320 const alice = privateKey('//Alice');321 const aliceProxy = subToEth(alice.address);322 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});323 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, 10, 'Fungible');324 const balanceAliceBefore = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);325 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, {Ethereum: aliceProxy}, 10, 'Fungible');326 const balanceAliceAfter = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId);327 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);328 });329330 itWeb3('Transfers to self. In case of substrate-evm boundary', async ({api}) => {331 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});332 const alice = privateKey('//Alice');333 const aliceProxy = subToEth(alice.address);334 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});335 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);336 await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy} , 10, 'Fungible');337 await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, alice, 10, 'Fungible');338 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);339 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);340 });341342 itWeb3('Transfers to self. In case of inside substrate-evm', async ({api}) => {343 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});344 const alice = privateKey('//Alice');345 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});346 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);347 await transferExpectSuccess(collectionId, tokenId, alice, alice , 10, 'Fungible');348 await transferFromExpectSuccess(collectionId, tokenId, alice, alice, alice, 10, 'Fungible');349 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);350 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);351 });352353 itWeb3('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({api}) => {354 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});355 const alice = privateKey('//Alice');356 const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address});357 const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);358 await transferExpectFailure(collectionId, tokenId, alice, alice , 11);359 await transferFromExpectFail(collectionId, tokenId, alice, alice, alice, 11);360 const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId);361 expect(balanceAliceBefore).to.be.eq(balanceAliceAfter);362 });363});