1234567891011121314151617import {expect} from 'chai';18import {19 contractHelpers,20 createEthAccountWithBalance,21 transferBalanceToEth,22 deployFlipper,23 itWeb3,24 SponsoringMode,25 createEthAccount,26 collectionIdToAddress,27 GAS_ARGS,28 normalizeEvents,29 subToEth,30 executeEthTxOnSub,31 evmCollectionHelpers,32 getCollectionAddressFromResult,33 evmCollection,34 ethBalanceViaSub,35} from './util/helpers';36import {37 addCollectionAdminExpectSuccess,38 createCollectionExpectSuccess,39 getDetailedCollectionInfo,40 transferBalanceTo,41} from '../util/helpers';42import nonFungibleAbi from './nonFungibleAbi.json';43import getBalance from '../substrate/get-balance';44import {evmToAddress} from '@polkadot/util-crypto';4546describe('Sponsoring EVM contracts', () => {47 itWeb3('Sponsoring can be set by the address that has deployed the contract', async ({api, web3}) => {48 const owner = await createEthAccountWithBalance(api, web3);49 const flipper = await deployFlipper(web3, owner);50 const helpers = contractHelpers(web3, owner);51 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;52 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});53 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;54 });5556 itWeb3('Sponsoring cannot be set by the address that did not deployed the contract', async ({api, web3}) => {57 const owner = await createEthAccountWithBalance(api, web3);58 const notOwner = await createEthAccountWithBalance(api, web3);59 const flipper = await deployFlipper(web3, owner);60 const helpers = contractHelpers(web3, owner);61 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;62 await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).send({from: notOwner})).to.rejected;63 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;64 });6566 itWeb3('In generous mode, non-allowlisted user transaction will be sponsored', async ({api, web3, privateKeyWrapper}) => {67 const alice = privateKeyWrapper('//Alice');6869 const owner = await createEthAccountWithBalance(api, web3);70 const caller = await createEthAccountWithBalance(api, web3);7172 const flipper = await deployFlipper(web3, owner);7374 const helpers = contractHelpers(web3, owner);7576 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;77 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});78 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});79 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;8081 await transferBalanceToEth(api, alice, flipper.options.address);8283 const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);84 expect(originalFlipperBalance).to.be.not.equal('0');8586 await flipper.methods.flip().send({from: caller});87 expect(await flipper.methods.getValue().call()).to.be.true;8889 90 const balanceAfter = await web3.eth.getBalance(flipper.options.address);91 expect(+balanceAfter).to.be.lessThan(+originalFlipperBalance);92 });9394 itWeb3('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should decrease (allowlisted)', async ({api, web3, privateKeyWrapper}) => {95 const alice = privateKeyWrapper('//Alice');9697 const owner = await createEthAccountWithBalance(api, web3);98 const caller = createEthAccount(web3);99100 const flipper = await deployFlipper(web3, owner);101102 const helpers = contractHelpers(web3, owner);103 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});104 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});105106 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;107 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});108 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});109 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;110111 await transferBalanceToEth(api, alice, flipper.options.address);112113 const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);114 expect(originalFlipperBalance).to.be.not.equal('0');115116 await flipper.methods.flip().send({from: caller});117 expect(await flipper.methods.getValue().call()).to.be.true;118119 120 const balanceAfter = await web3.eth.getBalance(flipper.options.address);121 expect(+balanceAfter).to.be.lessThan(+originalFlipperBalance);122 });123124 itWeb3('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should not decrease (non-allowlisted)', async ({api, web3, privateKeyWrapper}) => {125 const alice = privateKeyWrapper('//Alice');126127 const owner = await createEthAccountWithBalance(api, web3);128 const caller = createEthAccount(web3);129130 const flipper = await deployFlipper(web3, owner);131132 const helpers = contractHelpers(web3, owner);133134 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;135 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});136 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});137 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;138139 await transferBalanceToEth(api, alice, flipper.options.address);140141 const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);142 expect(originalFlipperBalance).to.be.not.equal('0');143144 await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/InvalidTransaction::Payment/);145 expect(await flipper.methods.getValue().call()).to.be.false;146147 148 const balanceAfter = await web3.eth.getBalance(flipper.options.address);149 expect(+balanceAfter).to.be.equals(+originalFlipperBalance);150 });151152 itWeb3('Sponsoring is set, an address that has UNQ can send a transaction and it works. User balance should not change', async ({api, web3, privateKeyWrapper}) => {153 const alice = privateKeyWrapper('//Alice');154155 const owner = await createEthAccountWithBalance(api, web3);156 const caller = await createEthAccountWithBalance(api, web3);157 const originalCallerBalance = await web3.eth.getBalance(caller);158159 const flipper = await deployFlipper(web3, owner);160161 const helpers = contractHelpers(web3, owner);162 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});163 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});164165 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;166 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});167 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});168 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;169170 await transferBalanceToEth(api, alice, flipper.options.address);171172 const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);173 expect(originalFlipperBalance).to.be.not.equal('0');174175 await flipper.methods.flip().send({from: caller});176 expect(await flipper.methods.getValue().call()).to.be.true;177178 expect(await web3.eth.getBalance(caller)).to.be.equals(originalCallerBalance);179 });180181 itWeb3('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({api, web3, privateKeyWrapper}) => {182 const alice = privateKeyWrapper('//Alice');183184 const owner = await createEthAccountWithBalance(api, web3);185 const caller = await createEthAccountWithBalance(api, web3);186 const originalCallerBalance = await web3.eth.getBalance(caller);187188 const flipper = await deployFlipper(web3, owner);189190 const helpers = contractHelpers(web3, owner);191 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});192 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});193194 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;195 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});196 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner});197 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;198199 await transferBalanceToEth(api, alice, flipper.options.address);200201 const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);202 expect(originalFlipperBalance).to.be.not.equal('0');203204 await flipper.methods.flip().send({from: caller});205 expect(await flipper.methods.getValue().call()).to.be.true;206 expect(await web3.eth.getBalance(caller)).to.be.equals(originalCallerBalance);207208 const newFlipperBalance = await web3.eth.getBalance(flipper.options.address);209 expect(newFlipperBalance).to.be.not.equals(originalFlipperBalance);210211 await flipper.methods.flip().send({from: caller});212 expect(await web3.eth.getBalance(flipper.options.address)).to.be.equal(newFlipperBalance);213 expect(await web3.eth.getBalance(caller)).to.be.not.equals(originalCallerBalance);214 });215216 217 itWeb3('Default rate limit equals 7200', async ({api, web3}) => {218 const owner = await createEthAccountWithBalance(api, web3);219 const flipper = await deployFlipper(web3, owner);220 const helpers = contractHelpers(web3, owner);221 expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200');222 });223224 itWeb3('Sponsoring collection from evm address via access list', async ({api, web3}) => {225 const owner = await createEthAccountWithBalance(api, web3);226 const collectionHelpers = evmCollectionHelpers(web3, owner);227 let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();228 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);229 const sponsor = await createEthAccountWithBalance(api, web3);230 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);231 result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});232 let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;233 expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;234 expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));235 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');236237 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});238 collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;239 expect(collectionSub.sponsorship.isConfirmed).to.be.true;240 expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));241242 const user = createEthAccount(web3);243 let nextTokenId = await collectionEvm.methods.nextTokenId().call();244 expect(nextTokenId).to.be.equal('1');245246 const oldPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();247 expect(oldPermissions.mintMode).to.be.false;248 expect(oldPermissions.access).to.be.equal('Normal');249250 251 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});252 await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});253 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});254255 const newPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();256 expect(newPermissions.mintMode).to.be.true;257 expect(newPermissions.access).to.be.equal('AllowList');258259 const ownerBalanceBefore = await ethBalanceViaSub(api, owner);260 const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);261262 nextTokenId = await collectionEvm.methods.nextTokenId().call({from: user});263 expect(nextTokenId).to.be.equal('1');264 result = await collectionEvm.methods.mintWithTokenURI(265 user,266 nextTokenId,267 'Test URI',268 ).send({from: user});269 const events = normalizeEvents(result.events);270 events[0].address = events[0].address.toLocaleLowerCase();271272 expect(events).to.be.deep.equal([273 {274 address: collectionIdAddress.toLocaleLowerCase(),275 event: 'Transfer',276 args: {277 from: '0x0000000000000000000000000000000000000000',278 to: user,279 tokenId: nextTokenId,280 },281 },282 ]);283284 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');285286 const ownerBalanceAfter = await ethBalanceViaSub(api, owner);287 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);288 const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);289 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;290 });291292 itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3}) => {293 const owner = await createEthAccountWithBalance(api, web3);294 const collectionHelpers = evmCollectionHelpers(web3, owner);295 let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();296 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);297 const sponsor = await createEthAccountWithBalance(api, web3);298 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);299 result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();300 let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;301 expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;302 expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));303 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');304 const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);305 await sponsorCollection.methods.confirmCollectionSponsorship().send();306 collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;307 expect(collectionSub.sponsorship.isConfirmed).to.be.true;308 expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));309310 const user = createEthAccount(web3);311 await collectionEvm.methods.addCollectionAdmin(user).send();312 313 const ownerBalanceBefore = await ethBalanceViaSub(api, owner);314 const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);315 316 const userCollectionEvm = evmCollection(web3, user, collectionIdAddress);317 const nextTokenId = await userCollectionEvm.methods.nextTokenId().call();318 expect(nextTokenId).to.be.equal('1');319 result = await userCollectionEvm.methods.mintWithTokenURI(320 user,321 nextTokenId,322 'Test URI',323 ).send();324325 const events = normalizeEvents(result.events);326 const address = collectionIdToAddress(collectionId);327328 expect(events).to.be.deep.equal([329 {330 address,331 event: 'Transfer',332 args: {333 from: '0x0000000000000000000000000000000000000000',334 to: user,335 tokenId: nextTokenId,336 },337 },338 ]);339 expect(await userCollectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');340 341 const ownerBalanceAfter = await ethBalanceViaSub(api, owner);342 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);343 const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);344 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;345 });346});