1import { readFile } from 'fs/promises';2import { getBalanceSingle, transferBalanceExpectSuccess } from '../../substrate/get-balance';3import privateKey from '../../substrate/privateKey';4import { createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, queryNftOwner, transferExpectSuccess, transferFromExpectSuccess } from '../../util/helpers';5import { collectionIdToAddress, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase } from '../util/helpers';6import { evmToAddress } from '@polkadot/util-crypto';7import nonFungibleAbi from '../nonFungibleAbi.json';8import fungibleAbi from '../fungibleAbi.json';9import waitNewBlocks from '../../substrate/wait-new-blocks';10import { expect } from 'chai';1112const PRICE = 2000n;1314describe('Matcher contract usage', () => {15 itWeb3('With UNQ', async ({api, web3}) => {16 const matcherOwner = await createEthAccountWithBalance(api, web3);17 const Matcher = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.abi`)).toString()), undefined, {18 from: matcherOwner,19 ...GAS_ARGS,20 });21 const matcher = await Matcher.deploy({ data: (await readFile(`${__dirname}/MarketPlaceUNQ.bin`)).toString() }).send({ from: matcherOwner });2223 const alice = privateKey('//Alice');24 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});25 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), { from: matcherOwner });2627 const seller = privateKey('//Bob');28 29 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);3031 32 await transferExpectSuccess(collectionId, tokenId, seller, {ethereum: subToEth(seller.address)});33 34 await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);35 await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);36 await waitNewBlocks(api, 1);3738 39 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(seller.address) });4041 42 {43 await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));44 await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, '0x0000000000000000000000000000000000000000', evmCollection.options.address, tokenId, 1));45 }46 47 48 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: matcher.options.address.toLowerCase() });4950 51 {52 const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);53 54 await executeEthTxOnSub(api, alice, matcher, m => m['buy(address,uint256)'](evmCollection.options.address, tokenId), { value: PRICE });55 expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);56 }5758 59 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(alice.address) });6061 62 63 await transferFromExpectSuccess(collectionId, tokenId, alice, { ethereum: subToEth(alice.address) }, { substrate: alice.address });64 65 66 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ substrate: alice.address });67 });6869 itWeb3('With custom ERC20', async ({api, web3}) => {70 const matcherOwner = await createEthAccountWithBalance(api, web3);71 const Matcher = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.abi`)).toString()), undefined, {72 from: matcherOwner,73 ...GAS_ARGS,74 });75 const matcher = await Matcher.deploy({ data: (await readFile(`${__dirname}/MarketPlaceUNQ.bin`)).toString() }).send({ from: matcherOwner });7677 const alice = privateKey('//Alice');78 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});79 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), { from: matcherOwner });8081 const fungibleId = await createCollectionExpectSuccess({ mode: { type: 'Fungible', decimalPoints: 0 } });82 const evmFungible = new web3.eth.Contract(fungibleAbi as any, collectionIdToAddress(fungibleId), { from: matcherOwner, ...GAS_ARGS });83 await createFungibleItemExpectSuccess(alice, fungibleId, { Value: PRICE }, { ethereum: subToEth(alice.address) });8485 const seller = privateKey('//Bob');86 87 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);8889 90 await transferExpectSuccess(collectionId, tokenId, seller, {ethereum: subToEth(seller.address)});91 92 await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);93 await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);94 await waitNewBlocks(api, 1);9596 97 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(seller.address) });9899 100 {101 await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));102 await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, evmFungible.options.address, evmCollection.options.address, tokenId, 1));103 }104 105 106 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: matcher.options.address.toLowerCase() });107108 109 {110 const sellerBalanceBeforePurchase = BigInt(await evmFungible.methods.balanceOf(subToEth(seller.address)).call());111 const buyerBalanceBeforePurchase = BigInt(await evmFungible.methods.balanceOf(subToEth(alice.address)).call());112113 await executeEthTxOnSub(api, alice, evmFungible, m => m.approve(matcher.options.address, PRICE));114 115 await executeEthTxOnSub(api, alice, matcher, m =>116 m['buy(address,uint256,address,uint256)'](evmCollection.options.address, tokenId, evmFungible.options.address, PRICE));117118 119 expect(BigInt(await evmFungible.methods.balanceOf(subToEth(seller.address)).call()) - sellerBalanceBeforePurchase === PRICE);120 expect(buyerBalanceBeforePurchase - BigInt(await evmFungible.methods.balanceOf(subToEth(alice.address)).call()) === PRICE);121 }122123 124 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(alice.address) });125126 127 128 await transferFromExpectSuccess(collectionId, tokenId, alice, { ethereum: subToEth(alice.address) }, { substrate: alice.address });129 130 131 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ substrate: alice.address });132 });133134 itWeb3('With escrow', async ({ api, web3 }) => {135 const matcherOwner = await createEthAccountWithBalance(api, web3);136 const escrow = await createEthAccountWithBalance(api, web3);137 const Matcher = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceKSM.abi`)).toString()), undefined, {138 from: matcherOwner,139 ...GAS_ARGS,140 });141 const matcher = await Matcher.deploy({ data: (await readFile(`${__dirname}/MarketPlaceKSM.bin`)).toString(), arguments: [escrow] }).send({ from: matcherOwner });142 143 const ksmToken = 11;144145 const alice = privateKey('//Alice');146 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});147 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), { from: matcherOwner });148149 const seller = privateKey('//Bob');150 151 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);152153 154 await transferExpectSuccess(collectionId, tokenId, seller, {ethereum: subToEth(seller.address)});155 156 await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);157 await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);158 await waitNewBlocks(api, 1);159160 161 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(seller.address) });162163 164 {165 await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));166 await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, ksmToken, evmCollection.options.address, tokenId, 1));167 }168 169 170 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: matcher.options.address.toLowerCase() });171172 173 await matcher.methods.deposit(PRICE, ksmToken, subToEth(alice.address)).send({ from: escrow });174 await waitNewBlocks(api, 1);175 176 177 {178 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal('0');179 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal(PRICE.toString());180181 await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId));182 await waitNewBlocks(api, 1);183184 185 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal('0');186 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal(PRICE.toString());187 }188189 190 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ ethereum: subToEthLowercase(alice.address) });191 192 193 await transferFromExpectSuccess(collectionId, tokenId, alice, { ethereum: subToEth(alice.address) }, { substrate: alice.address });194 195 196 expect(await queryNftOwner(api, collectionId, tokenId)).to.be.deep.equal({ substrate: alice.address });197 });198});