difftreelog
add test `Gas price boundaries`
in: master
1 file changed
tests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {EthUniqueHelper} from './util/playgrounds/unique.dev';19import {itEth, expect, SponsoringMode, usingEthPlaygrounds} from './util';20import {usingPlaygrounds} from '../util';21import {CompiledContract} from './util/playgrounds/types';2223describe('Sponsoring EVM contracts', () => {24 let donor: IKeyringPair;25 let nominal: bigint;2627 before(async () => {28 await usingPlaygrounds(async (helper, privateKey) => {29 donor = await privateKey({url: import.meta.url});30 nominal = helper.balance.getOneTokenNominal();31 });32 });3334 itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => {35 const owner = await helper.eth.createAccountWithBalance(donor);36 const flipper = await helper.eth.deployFlipper(owner);37 const helpers = await helper.ethNativeContract.contractHelpers(owner);3839 // 1. owner can set selfSponsoring:40 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;41 const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner});42 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;4344 // 1.1 Can get sponsor using methods.sponsor:45 const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();46 expect(actualSponsorOpt.status).to.be.true;47 const actualSponsor = actualSponsorOpt.value;48 expect(actualSponsor.eth).to.eq(flipper.options.address);49 expect(actualSponsor.sub).to.eq('0');5051 // 2. Events should be:52 const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events);53 expect(ethEvents).to.be.deep.equal([54 {55 address: flipper.options.address,56 event: 'ContractSponsorSet',57 args: {58 contractAddress: flipper.options.address,59 sponsor: flipper.options.address,60 },61 },62 {63 address: flipper.options.address,64 event: 'ContractSponsorshipConfirmed',65 args: {66 contractAddress: flipper.options.address,67 sponsor: flipper.options.address,68 },69 },70 ]);71 });7273 itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {74 const owner = await helper.eth.createAccountWithBalance(donor);75 const notOwner = await helper.eth.createAccountWithBalance(donor);76 const helpers = await helper.ethNativeContract.contractHelpers(owner);77 const flipper = await helper.eth.deployFlipper(owner);7879 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;80 await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');81 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;82 });8384 itEth('Sponsoring can be set by the address that has deployed the contract', async ({helper}) => {85 const owner = await helper.eth.createAccountWithBalance(donor);86 const helpers = await helper.ethNativeContract.contractHelpers(owner);87 const flipper = await helper.eth.deployFlipper(owner);8889 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;90 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});91 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;92 });9394 itEth('Sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {95 const owner = await helper.eth.createAccountWithBalance(donor);96 const notOwner = await helper.eth.createAccountWithBalance(donor);97 const helpers = await helper.ethNativeContract.contractHelpers(owner);98 const flipper = await helper.eth.deployFlipper(owner);99100 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;101 await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission');102 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;103 });104105 itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => {106 const owner = await helper.eth.createAccountWithBalance(donor);107 const sponsor = await helper.eth.createAccountWithBalance(donor);108 const helpers = await helper.ethNativeContract.contractHelpers(owner);109 const flipper = await helper.eth.deployFlipper(owner);110111 // 1. owner can set a sponsor:112 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;113 const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send();114 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;115116 // 2. Events should be:117 const events = helper.eth.normalizeEvents(result.events);118 expect(events).to.be.deep.equal([119 {120 address: flipper.options.address,121 event: 'ContractSponsorSet',122 args: {123 contractAddress: flipper.options.address,124 sponsor: sponsor,125 },126 },127 ]);128 });129130 itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => {131 const owner = await helper.eth.createAccountWithBalance(donor);132 const sponsor = await helper.eth.createAccountWithBalance(donor);133 const notOwner = await helper.eth.createAccountWithBalance(donor);134 const helpers = await helper.ethNativeContract.contractHelpers(owner);135 const flipper = await helper.eth.deployFlipper(owner);136137 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;138 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission');139 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;140 });141142 itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => {143 const owner = await helper.eth.createAccountWithBalance(donor);144 const sponsor = await helper.eth.createAccountWithBalance(donor);145 const helpers = await helper.ethNativeContract.contractHelpers(owner);146 const flipper = await helper.eth.deployFlipper(owner);147148 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;149 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();150151 // 1. sponsor can confirm sponsorship:152 const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});153 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;154155 // 1.1 Can get sponsor using methods.sponsor:156 const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();157 expect(actualSponsorOpt.status).to.be.true;158 const actualSponsor = actualSponsorOpt.value;159 expect(actualSponsor.eth).to.eq(sponsor);160 expect(actualSponsor.sub).to.eq('0');161162 // 2. Events should be:163 const events = helper.eth.normalizeEvents(result.events);164 expect(events).to.be.deep.equal([165 {166 address: flipper.options.address,167 event: 'ContractSponsorshipConfirmed',168 args: {169 contractAddress: flipper.options.address,170 sponsor: sponsor,171 },172 },173 ]);174 });175176 itEth('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({helper}) => {177 const owner = await helper.eth.createAccountWithBalance(donor);178 const sponsor = await helper.eth.createAccountWithBalance(donor);179 const notSponsor = await helper.eth.createAccountWithBalance(donor);180 const helpers = await helper.ethNativeContract.contractHelpers(owner);181 const flipper = await helper.eth.deployFlipper(owner);182183 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;184 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;185 await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission');186 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;187 });188189 itEth('Sponsorship can not be confirmed by the address that not set as sponsor', async ({helper}) => {190 const owner = await helper.eth.createAccountWithBalance(donor);191 const notSponsor = await helper.eth.createAccountWithBalance(donor);192 const helpers = await helper.ethNativeContract.contractHelpers(owner);193 const flipper = await helper.eth.deployFlipper(owner);194195 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;196 await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor');197 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;198 });199200 itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => {201 const owner = await helper.eth.createAccountWithBalance(donor);202 const sponsor = await helper.eth.createAccountWithBalance(donor);203 const helpers = await helper.ethNativeContract.contractHelpers(owner);204 const flipper = await helper.eth.deployFlipper(owner);205206 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;207 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();208 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});209 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;210 // 1. Can remove sponsor:211 const result = await helpers.methods.removeSponsor(flipper.options.address).send();212 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;213214 // 2. Events should be:215 const events = helper.eth.normalizeEvents(result.events);216 expect(events).to.be.deep.equal([217 {218 address: flipper.options.address,219 event: 'ContractSponsorRemoved',220 args: {221 contractAddress: flipper.options.address,222 },223 },224 ]);225226 // TODO: why call method reverts?227 // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();228 // expect(actualSponsor.eth).to.eq(sponsor);229 // expect(actualSponsor.sub).to.eq('0');230 });231232 itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => {233 const owner = await helper.eth.createAccountWithBalance(donor);234 const notOwner = await helper.eth.createAccountWithBalance(donor);235 const sponsor = await helper.eth.createAccountWithBalance(donor);236 const helpers = await helper.ethNativeContract.contractHelpers(owner);237 const flipper = await helper.eth.deployFlipper(owner);238239 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;240 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();241 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});242 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;243244 await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');245 await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected;246 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;247 });248249 itEth('In generous mode, non-allowlisted user transaction will be sponsored', async ({helper}) => {250 const owner = await helper.eth.createAccountWithBalance(donor);251 const sponsor = await helper.eth.createAccountWithBalance(donor);252 const caller = await helper.eth.createAccountWithBalance(donor);253 const helpers = await helper.ethNativeContract.contractHelpers(owner);254 const flipper = await helper.eth.deployFlipper(owner);255256 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();257 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});258259 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});260 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});261262 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));263 const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));264265 await flipper.methods.flip().send({from: caller});266 expect(await flipper.methods.getValue().call()).to.be.true;267268 // Balance should be taken from sponsor instead of caller269 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));270 const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));271 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;272 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);273 });274275 itEth('In generous mode, non-allowlisted user transaction will be self sponsored', async ({helper}) => {276 const owner = await helper.eth.createAccountWithBalance(donor);277 const caller = await helper.eth.createAccountWithBalance(donor);278 const helpers = await helper.ethNativeContract.contractHelpers(owner);279 const flipper = await helper.eth.deployFlipper(owner);280281 await helpers.methods.selfSponsoredEnable(flipper.options.address).send();282283 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});284 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});285286 await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);287288 const contractBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));289 const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));290291 await flipper.methods.flip().send({from: caller});292 expect(await flipper.methods.getValue().call()).to.be.true;293294 // Balance should be taken from sponsor instead of caller295 const contractBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));296 const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));297 expect(contractBalanceAfter < contractBalanceBefore).to.be.true;298 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);299 });300301 [302 {balance: 0n, label: '0'},303 {balance: 10n, label: '10'},304 ].map(testCase => {305 itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => {306 const owner = await helper.eth.createAccountWithBalance(donor);307 const sponsor = await helper.eth.createAccountWithBalance(donor);308 const caller = helper.eth.createAccount();309 await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance);310 const helpers = await helper.ethNativeContract.contractHelpers(owner);311 const flipper = await helper.eth.deployFlipper(owner);312313 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});314 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});315316 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});317 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});318319 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();320 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});321322 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));323 expect(sponsorBalanceBefore > 0n).to.be.true;324325 await flipper.methods.flip().send({from: caller});326 expect(await flipper.methods.getValue().call()).to.be.true;327328 // Balance should be taken from flipper instead of caller329 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));330 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;331 // Caller's balance does not change:332 const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));333 expect(callerBalanceAfter).to.eq(testCase.balance * nominal);334 });335 });336337 itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => {338 const owner = await helper.eth.createAccountWithBalance(donor);339 const caller = helper.eth.createAccount();340 const contractHelpers = await helper.ethNativeContract.contractHelpers(owner);341342 // Deploy flipper and send some tokens:343 const flipper = await helper.eth.deployFlipper(owner);344 await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);345 expect(await flipper.methods.getValue().call()).to.be.false;346 // flipper address has some tokens:347 const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);348 expect(originalFlipperBalance > 0n).to.be.true;349350 // Set Allowlisted sponsoring mode. caller is not in allow list:351 await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});352 await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});353 await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});354355 // 1. Caller has no UNQ and is not in allow list. So he cannot flip:356 await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/);357 expect(await flipper.methods.getValue().call()).to.be.false;358359 // Flipper's balance does not change:360 const balanceAfter = await helper.balance.getEthereum(flipper.options.address);361 expect(balanceAfter).to.be.equal(originalFlipperBalance);362 });363364 itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => {365 const owner = await helper.eth.createAccountWithBalance(donor);366 const sponsor = await helper.eth.createAccountWithBalance(donor);367 const caller = await helper.eth.createAccountWithBalance(donor);368 const helpers = await helper.ethNativeContract.contractHelpers(owner);369 const flipper = await helper.eth.deployFlipper(owner);370371 const originalCallerBalance = await helper.balance.getEthereum(caller);372 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});373 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});374375 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});376 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner});377378 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();379 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});380381 const originalFlipperBalance = await helper.balance.getEthereum(sponsor);382 expect(originalFlipperBalance > 0n).to.be.true;383384 await flipper.methods.flip().send({from: caller});385 expect(await flipper.methods.getValue().call()).to.be.true;386 expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance);387388 const newFlipperBalance = await helper.balance.getEthereum(sponsor);389 expect(newFlipperBalance).to.be.not.equal(originalFlipperBalance);390391 await flipper.methods.flip().send({from: caller});392 // todo:playgrounds fails rarely (expected 99893341659775672580n to equal 99912598679356033129n) (again, 99893341659775672580n)393 expect(await helper.balance.getEthereum(sponsor)).to.be.equal(newFlipperBalance);394 expect(await helper.balance.getEthereum(caller)).to.be.not.equal(originalCallerBalance);395 });396397 // TODO: Find a way to calculate default rate limit398 itEth('Default rate limit equal 7200', async ({helper}) => {399 const owner = await helper.eth.createAccountWithBalance(donor);400 const helpers = await helper.ethNativeContract.contractHelpers(owner);401 const flipper = await helper.eth.deployFlipper(owner);402403 expect(await helpers.methods.sponsoringRateLimit(flipper.options.address).call()).to.be.equal('7200');404 });405});406407describe('Sponsoring Fee Limit', () => {408 let donor: IKeyringPair;409 let alice: IKeyringPair;410 let testContract: CompiledContract;411412 async function compileTestContract(helper: EthUniqueHelper) {413 if (!testContract) {414 testContract = await helper.ethContract.compile(415 'TestContract',416 `417 // SPDX-License-Identifier: MIT418 pragma solidity ^0.8.0;419420 contract TestContract {421 event Result(bool);422423 function test(uint32 cycles) public {424 uint256 counter = 0;425 while(true) {426 counter ++;427 if (counter > cycles){428 break;429 }430 }431 emit Result(true);432 }433 }434 `,435 );436 }437 return testContract;438 }439440 async function deployTestContract(helper: EthUniqueHelper, owner: string) {441 const compiled = await compileTestContract(helper);442 return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object);443 }444445 before(async () => {446 await usingEthPlaygrounds(async (helper, privateKey) => {447 donor = await privateKey({url: import.meta.url});448 [alice] = await helper.arrange.createAccounts([100n], donor);449 });450 });451452 itEth('Default fee limit', async ({helper}) => {453 const owner = await helper.eth.createAccountWithBalance(donor);454 const helpers = await helper.ethNativeContract.contractHelpers(owner);455 const flipper = await helper.eth.deployFlipper(owner);456457 expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('115792089237316195423570985008687907853269984665640564039457584007913129639935');458 });459460 itEth('Set fee limit', async ({helper}) => {461 const owner = await helper.eth.createAccountWithBalance(donor);462 const helpers = await helper.ethNativeContract.contractHelpers(owner);463 const flipper = await helper.eth.deployFlipper(owner);464465 await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send();466 expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('100');467 });468469 itEth('Negative test - set fee limit by non-owner', async ({helper}) => {470 const owner = await helper.eth.createAccountWithBalance(donor);471 const stranger = await helper.eth.createAccountWithBalance(donor);472 const helpers = await helper.ethNativeContract.contractHelpers(owner);473 const flipper = await helper.eth.deployFlipper(owner);474475 await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected;476 });477478 itEth('Negative test - check that eth transactions exceeding fee limit are not executed', async ({helper}) => {479 const owner = await helper.eth.createAccountWithBalance(donor);480 const sponsor = await helper.eth.createAccountWithBalance(donor);481 const user = await helper.eth.createAccountWithBalance(donor);482 const helpers = await helper.ethNativeContract.contractHelpers(owner);483484 const testContract = await deployTestContract(helper, owner);485486 await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});487 await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});488489 await helpers.methods.setSponsor(testContract.options.address, sponsor).send();490 await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});491492 const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());493494 await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();495496 const originalUserBalance = await helper.balance.getEthereum(user);497 await testContract.methods.test(100).send({from: user, gas: 2_000_000});498 expect(await helper.balance.getEthereum(user)).to.be.equal(originalUserBalance);499500 await testContract.methods.test(100).send({from: user, gas: 2_100_000});501 expect(await helper.balance.getEthereum(user)).to.not.be.equal(originalUserBalance);502 });503504 itEth('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({helper}) => {505 const owner = await helper.eth.createAccountWithBalance(donor);506 const sponsor = await helper.eth.createAccountWithBalance(donor);507 const helpers = await helper.ethNativeContract.contractHelpers(owner);508509 const testContract = await deployTestContract(helper, owner);510511 await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});512 await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});513514 await helpers.methods.setSponsor(testContract.options.address, sponsor).send();515 await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});516517 const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());518519 await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();520521 const originalAliceBalance = await helper.balance.getSubstrate(alice.address);522523 await helper.eth.sendEVM(524 alice,525 testContract.options.address,526 testContract.methods.test(100).encodeABI(),527 '0',528 2_000_000,529 );530 // expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance);531 expect(await helper.balance.getSubstrate(alice.address)).to.be.equal(originalAliceBalance);532533 await helper.eth.sendEVM(534 alice,535 testContract.options.address,536 testContract.methods.test(100).encodeABI(),537 '0',538 2_100_000,539 );540 expect(await helper.balance.getSubstrate(alice.address)).to.not.be.equal(originalAliceBalance);541 });542});1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {EthUniqueHelper} from './util/playgrounds/unique.dev';19import {itEth, expect, SponsoringMode, usingEthPlaygrounds} from './util';20import {usingPlaygrounds} from '../util';21import {CompiledContract} from './util/playgrounds/types';2223describe('Sponsoring EVM contracts', () => {24 let donor: IKeyringPair;25 let nominal: bigint;2627 before(async () => {28 await usingPlaygrounds(async (helper, privateKey) => {29 donor = await privateKey({url: import.meta.url});30 nominal = helper.balance.getOneTokenNominal();31 });32 });3334 itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => {35 const owner = await helper.eth.createAccountWithBalance(donor);36 const flipper = await helper.eth.deployFlipper(owner);37 const helpers = await helper.ethNativeContract.contractHelpers(owner);3839 // 1. owner can set selfSponsoring:40 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;41 const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner});42 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;4344 // 1.1 Can get sponsor using methods.sponsor:45 const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();46 expect(actualSponsorOpt.status).to.be.true;47 const actualSponsor = actualSponsorOpt.value;48 expect(actualSponsor.eth).to.eq(flipper.options.address);49 expect(actualSponsor.sub).to.eq('0');5051 // 2. Events should be:52 const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events);53 expect(ethEvents).to.be.deep.equal([54 {55 address: flipper.options.address,56 event: 'ContractSponsorSet',57 args: {58 contractAddress: flipper.options.address,59 sponsor: flipper.options.address,60 },61 },62 {63 address: flipper.options.address,64 event: 'ContractSponsorshipConfirmed',65 args: {66 contractAddress: flipper.options.address,67 sponsor: flipper.options.address,68 },69 },70 ]);71 });7273 itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {74 const owner = await helper.eth.createAccountWithBalance(donor);75 const notOwner = await helper.eth.createAccountWithBalance(donor);76 const helpers = await helper.ethNativeContract.contractHelpers(owner);77 const flipper = await helper.eth.deployFlipper(owner);7879 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;80 await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');81 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;82 });8384 itEth('Sponsoring can be set by the address that has deployed the contract', async ({helper}) => {85 const owner = await helper.eth.createAccountWithBalance(donor);86 const helpers = await helper.ethNativeContract.contractHelpers(owner);87 const flipper = await helper.eth.deployFlipper(owner);8889 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;90 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});91 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;92 });9394 itEth('Sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {95 const owner = await helper.eth.createAccountWithBalance(donor);96 const notOwner = await helper.eth.createAccountWithBalance(donor);97 const helpers = await helper.ethNativeContract.contractHelpers(owner);98 const flipper = await helper.eth.deployFlipper(owner);99100 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;101 await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission');102 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;103 });104105 itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => {106 const owner = await helper.eth.createAccountWithBalance(donor);107 const sponsor = await helper.eth.createAccountWithBalance(donor);108 const helpers = await helper.ethNativeContract.contractHelpers(owner);109 const flipper = await helper.eth.deployFlipper(owner);110111 // 1. owner can set a sponsor:112 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;113 const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send();114 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;115116 // 2. Events should be:117 const events = helper.eth.normalizeEvents(result.events);118 expect(events).to.be.deep.equal([119 {120 address: flipper.options.address,121 event: 'ContractSponsorSet',122 args: {123 contractAddress: flipper.options.address,124 sponsor: sponsor,125 },126 },127 ]);128 });129130 itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => {131 const owner = await helper.eth.createAccountWithBalance(donor);132 const sponsor = await helper.eth.createAccountWithBalance(donor);133 const notOwner = await helper.eth.createAccountWithBalance(donor);134 const helpers = await helper.ethNativeContract.contractHelpers(owner);135 const flipper = await helper.eth.deployFlipper(owner);136137 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;138 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission');139 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;140 });141142 itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => {143 const owner = await helper.eth.createAccountWithBalance(donor);144 const sponsor = await helper.eth.createAccountWithBalance(donor);145 const helpers = await helper.ethNativeContract.contractHelpers(owner);146 const flipper = await helper.eth.deployFlipper(owner);147148 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;149 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();150151 // 1. sponsor can confirm sponsorship:152 const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});153 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;154155 // 1.1 Can get sponsor using methods.sponsor:156 const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();157 expect(actualSponsorOpt.status).to.be.true;158 const actualSponsor = actualSponsorOpt.value;159 expect(actualSponsor.eth).to.eq(sponsor);160 expect(actualSponsor.sub).to.eq('0');161162 // 2. Events should be:163 const events = helper.eth.normalizeEvents(result.events);164 expect(events).to.be.deep.equal([165 {166 address: flipper.options.address,167 event: 'ContractSponsorshipConfirmed',168 args: {169 contractAddress: flipper.options.address,170 sponsor: sponsor,171 },172 },173 ]);174 });175176 itEth('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({helper}) => {177 const owner = await helper.eth.createAccountWithBalance(donor);178 const sponsor = await helper.eth.createAccountWithBalance(donor);179 const notSponsor = await helper.eth.createAccountWithBalance(donor);180 const helpers = await helper.ethNativeContract.contractHelpers(owner);181 const flipper = await helper.eth.deployFlipper(owner);182183 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;184 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;185 await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission');186 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;187 });188189 itEth('Sponsorship can not be confirmed by the address that not set as sponsor', async ({helper}) => {190 const owner = await helper.eth.createAccountWithBalance(donor);191 const notSponsor = await helper.eth.createAccountWithBalance(donor);192 const helpers = await helper.ethNativeContract.contractHelpers(owner);193 const flipper = await helper.eth.deployFlipper(owner);194195 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;196 await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor');197 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;198 });199200 itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => {201 const owner = await helper.eth.createAccountWithBalance(donor);202 const sponsor = await helper.eth.createAccountWithBalance(donor);203 const helpers = await helper.ethNativeContract.contractHelpers(owner);204 const flipper = await helper.eth.deployFlipper(owner);205206 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;207 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();208 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});209 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;210 // 1. Can remove sponsor:211 const result = await helpers.methods.removeSponsor(flipper.options.address).send();212 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;213214 // 2. Events should be:215 const events = helper.eth.normalizeEvents(result.events);216 expect(events).to.be.deep.equal([217 {218 address: flipper.options.address,219 event: 'ContractSponsorRemoved',220 args: {221 contractAddress: flipper.options.address,222 },223 },224 ]);225226 // TODO: why call method reverts?227 // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();228 // expect(actualSponsor.eth).to.eq(sponsor);229 // expect(actualSponsor.sub).to.eq('0');230 });231232 itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => {233 const owner = await helper.eth.createAccountWithBalance(donor);234 const notOwner = await helper.eth.createAccountWithBalance(donor);235 const sponsor = await helper.eth.createAccountWithBalance(donor);236 const helpers = await helper.ethNativeContract.contractHelpers(owner);237 const flipper = await helper.eth.deployFlipper(owner);238239 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;240 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();241 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});242 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;243244 await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');245 await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected;246 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;247 });248249 itEth('In generous mode, non-allowlisted user transaction will be sponsored', async ({helper}) => {250 const owner = await helper.eth.createAccountWithBalance(donor);251 const sponsor = await helper.eth.createAccountWithBalance(donor);252 const caller = await helper.eth.createAccountWithBalance(donor);253 const helpers = await helper.ethNativeContract.contractHelpers(owner);254 const flipper = await helper.eth.deployFlipper(owner);255256 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();257 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});258259 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});260 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});261262 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));263 const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));264265 await flipper.methods.flip().send({from: caller});266 expect(await flipper.methods.getValue().call()).to.be.true;267268 // Balance should be taken from sponsor instead of caller269 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));270 const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));271 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;272 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);273 });274275 itEth('In generous mode, non-allowlisted user transaction will be self sponsored', async ({helper}) => {276 const owner = await helper.eth.createAccountWithBalance(donor);277 const caller = await helper.eth.createAccountWithBalance(donor);278 const helpers = await helper.ethNativeContract.contractHelpers(owner);279 const flipper = await helper.eth.deployFlipper(owner);280281 await helpers.methods.selfSponsoredEnable(flipper.options.address).send();282283 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});284 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});285286 await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);287288 const contractBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));289 const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));290291 await flipper.methods.flip().send({from: caller});292 expect(await flipper.methods.getValue().call()).to.be.true;293294 // Balance should be taken from sponsor instead of caller295 const contractBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));296 const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));297 expect(contractBalanceAfter < contractBalanceBefore).to.be.true;298 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);299 });300301 [302 {balance: 0n, label: '0'},303 {balance: 10n, label: '10'},304 ].map(testCase => {305 itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => {306 const owner = await helper.eth.createAccountWithBalance(donor);307 const sponsor = await helper.eth.createAccountWithBalance(donor);308 const caller = helper.eth.createAccount();309 await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance);310 const helpers = await helper.ethNativeContract.contractHelpers(owner);311 const flipper = await helper.eth.deployFlipper(owner);312313 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});314 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});315316 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});317 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});318319 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();320 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});321322 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));323 expect(sponsorBalanceBefore > 0n).to.be.true;324325 await flipper.methods.flip().send({from: caller});326 expect(await flipper.methods.getValue().call()).to.be.true;327328 // Balance should be taken from flipper instead of caller329 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));330 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;331 // Caller's balance does not change:332 const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));333 expect(callerBalanceAfter).to.eq(testCase.balance * nominal);334 });335 });336337 itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => {338 const owner = await helper.eth.createAccountWithBalance(donor);339 const caller = helper.eth.createAccount();340 const contractHelpers = await helper.ethNativeContract.contractHelpers(owner);341342 // Deploy flipper and send some tokens:343 const flipper = await helper.eth.deployFlipper(owner);344 await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);345 expect(await flipper.methods.getValue().call()).to.be.false;346 // flipper address has some tokens:347 const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);348 expect(originalFlipperBalance > 0n).to.be.true;349350 // Set Allowlisted sponsoring mode. caller is not in allow list:351 await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});352 await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});353 await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});354355 // 1. Caller has no UNQ and is not in allow list. So he cannot flip:356 await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/);357 expect(await flipper.methods.getValue().call()).to.be.false;358359 // Flipper's balance does not change:360 const balanceAfter = await helper.balance.getEthereum(flipper.options.address);361 expect(balanceAfter).to.be.equal(originalFlipperBalance);362 });363364 itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => {365 const owner = await helper.eth.createAccountWithBalance(donor);366 const sponsor = await helper.eth.createAccountWithBalance(donor);367 const caller = await helper.eth.createAccountWithBalance(donor);368 const helpers = await helper.ethNativeContract.contractHelpers(owner);369 const flipper = await helper.eth.deployFlipper(owner);370371 const originalCallerBalance = await helper.balance.getEthereum(caller);372 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});373 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});374375 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});376 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner});377378 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();379 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});380381 const originalFlipperBalance = await helper.balance.getEthereum(sponsor);382 expect(originalFlipperBalance > 0n).to.be.true;383384 await flipper.methods.flip().send({from: caller});385 expect(await flipper.methods.getValue().call()).to.be.true;386 expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance);387388 const newFlipperBalance = await helper.balance.getEthereum(sponsor);389 expect(newFlipperBalance).to.be.not.equal(originalFlipperBalance);390391 await flipper.methods.flip().send({from: caller});392 // todo:playgrounds fails rarely (expected 99893341659775672580n to equal 99912598679356033129n) (again, 99893341659775672580n)393 expect(await helper.balance.getEthereum(sponsor)).to.be.equal(newFlipperBalance);394 expect(await helper.balance.getEthereum(caller)).to.be.not.equal(originalCallerBalance);395 });396397 // TODO: Find a way to calculate default rate limit398 itEth('Default rate limit equal 7200', async ({helper}) => {399 const owner = await helper.eth.createAccountWithBalance(donor);400 const helpers = await helper.ethNativeContract.contractHelpers(owner);401 const flipper = await helper.eth.deployFlipper(owner);402403 expect(await helpers.methods.sponsoringRateLimit(flipper.options.address).call()).to.be.equal('7200');404 });405406 itEth('Gas price boundaries', async ({helper}) => {407 const owner = await helper.eth.createAccountWithBalance(donor);408 const sponsor = await helper.eth.createAccountWithBalance(donor);409 const caller = await helper.eth.createAccountWithBalance(donor);410 const helpers = await helper.ethNativeContract.contractHelpers(owner);411 const flipper = await helper.eth.deployFlipper(owner);412413 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();414 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});415416 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});417 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});418419 let sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));420 let callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));421422 let expectValue = await flipper.methods.getValue().call();423424 const flip = async (gasPrice: bigint, shouldPass = true) => {425 await flipper.methods.flip().send({from: caller, gasPrice: gasPrice});426 expectValue = !expectValue;427 expect(await flipper.methods.getValue().call()).to.be.eq(expectValue);428 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));429 const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));430 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.eq(shouldPass);431 expect(callerBalanceAfter === callerBalanceBefore).to.be.eq(shouldPass);432 sponsorBalanceBefore = sponsorBalanceAfter;433 callerBalanceBefore = callerBalanceAfter;434 };435436 const gasPrice = BigInt(await helper.eth.getGasPrice());437 await flip(gasPrice);438 await flip(gasPrice * 2n);439 await flip(gasPrice * 21n / 10n);440 await flip(gasPrice * 22n / 10n, false);441 });442});443444describe('Sponsoring Fee Limit', () => {445 let donor: IKeyringPair;446 let alice: IKeyringPair;447 let testContract: CompiledContract;448449 async function compileTestContract(helper: EthUniqueHelper) {450 if (!testContract) {451 testContract = await helper.ethContract.compile(452 'TestContract',453 `454 // SPDX-License-Identifier: MIT455 pragma solidity ^0.8.0;456457 contract TestContract {458 event Result(bool);459460 function test(uint32 cycles) public {461 uint256 counter = 0;462 while(true) {463 counter ++;464 if (counter > cycles){465 break;466 }467 }468 emit Result(true);469 }470 }471 `,472 );473 }474 return testContract;475 }476477 async function deployTestContract(helper: EthUniqueHelper, owner: string) {478 const compiled = await compileTestContract(helper);479 return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object);480 }481482 before(async () => {483 await usingEthPlaygrounds(async (helper, privateKey) => {484 donor = await privateKey({url: import.meta.url});485 [alice] = await helper.arrange.createAccounts([100n], donor);486 });487 });488489 itEth('Default fee limit', async ({helper}) => {490 const owner = await helper.eth.createAccountWithBalance(donor);491 const helpers = await helper.ethNativeContract.contractHelpers(owner);492 const flipper = await helper.eth.deployFlipper(owner);493494 expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('115792089237316195423570985008687907853269984665640564039457584007913129639935');495 });496497 itEth('Set fee limit', async ({helper}) => {498 const owner = await helper.eth.createAccountWithBalance(donor);499 const helpers = await helper.ethNativeContract.contractHelpers(owner);500 const flipper = await helper.eth.deployFlipper(owner);501502 await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send();503 expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('100');504 });505506 itEth('Negative test - set fee limit by non-owner', async ({helper}) => {507 const owner = await helper.eth.createAccountWithBalance(donor);508 const stranger = await helper.eth.createAccountWithBalance(donor);509 const helpers = await helper.ethNativeContract.contractHelpers(owner);510 const flipper = await helper.eth.deployFlipper(owner);511512 await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected;513 });514515 itEth('Negative test - check that eth transactions exceeding fee limit are not executed', async ({helper}) => {516 const owner = await helper.eth.createAccountWithBalance(donor);517 const sponsor = await helper.eth.createAccountWithBalance(donor);518 const user = await helper.eth.createAccountWithBalance(donor);519 const helpers = await helper.ethNativeContract.contractHelpers(owner);520521 const testContract = await deployTestContract(helper, owner);522523 await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});524 await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});525526 await helpers.methods.setSponsor(testContract.options.address, sponsor).send();527 await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});528529 const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());530531 await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();532533 const originalUserBalance = await helper.balance.getEthereum(user);534 await testContract.methods.test(100).send({from: user, gas: 2_000_000});535 expect(await helper.balance.getEthereum(user)).to.be.equal(originalUserBalance);536537 await testContract.methods.test(100).send({from: user, gas: 2_100_000});538 expect(await helper.balance.getEthereum(user)).to.not.be.equal(originalUserBalance);539 });540541 itEth('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({helper}) => {542 const owner = await helper.eth.createAccountWithBalance(donor);543 const sponsor = await helper.eth.createAccountWithBalance(donor);544 const helpers = await helper.ethNativeContract.contractHelpers(owner);545546 const testContract = await deployTestContract(helper, owner);547548 await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});549 await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});550551 await helpers.methods.setSponsor(testContract.options.address, sponsor).send();552 await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});553554 const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());555556 await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();557558 const originalAliceBalance = await helper.balance.getSubstrate(alice.address);559560 await helper.eth.sendEVM(561 alice,562 testContract.options.address,563 testContract.methods.test(100).encodeABI(),564 '0',565 2_000_000,566 );567 // expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance);568 expect(await helper.balance.getSubstrate(alice.address)).to.be.equal(originalAliceBalance);569570 await helper.eth.sendEVM(571 alice,572 testContract.options.address,573 testContract.methods.test(100).encodeABI(),574 '0',575 2_100_000,576 );577 expect(await helper.balance.getSubstrate(alice.address)).to.not.be.equal(originalAliceBalance);578 });579});