1234567891011121314151617import {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 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 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 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 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 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 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 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 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 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 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 227 228 229 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 269 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 295 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 329 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));330 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;331 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 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 347 const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);348 expect(originalFlipperBalance > 0n).to.be.true;349350 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 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 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 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 398 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 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});