From fea73f40565c1e9b96241d4a5068877abdc7ee80 Mon Sep 17 00:00:00 2001 From: Grigoriy Simonov Date: Fri, 30 Sep 2022 12:52:17 +0000 Subject: [PATCH] test: tests for contract fees --- --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -114,7 +114,7 @@ use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; use sp_core::H160; use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome}; -use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap, collections::btree_set::BTreeSet}; +use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap}; use core::ops::Deref; use codec::{Encode, Decode, MaxEncodedLen}; use scale_info::TypeInfo; --- a/pallets/unique/src/eth/mod.rs +++ b/pallets/unique/src/eth/mod.rs @@ -194,6 +194,7 @@ fn create_nonfungible_collection( &mut self, caller: caller, + value: value, name: string, description: string, token_prefix: string, --- a/tests/src/eth/payable.test.ts +++ b/tests/src/eth/payable.test.ts @@ -16,7 +16,7 @@ import {IKeyringPair} from '@polkadot/types/types'; -import {itEth, expect, usingEthPlaygrounds} from './util/playgrounds'; +import {itEth, expect, usingEthPlaygrounds, EthUniqueHelper} from './util/playgrounds'; describe('EVM payable contracts', () => { let donor: IKeyringPair; @@ -104,3 +104,147 @@ } }); }); + +describe('EVM transaction fees', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = privateKey('//Alice'); + }); + }); + + itEth('Fee is withdrawn from the user', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployFlipper(deployer); + + const initialCallerBalance = await helper.balance.getEthereum(caller); + await contract.methods.flip().send({from: caller}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + }); + + itEth('Fee for nested calls is withdrawn from the user', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await deployProxyContract(helper, deployer); + + const initialCallerBalance = await helper.balance.getEthereum(caller); + const initialContractBalance = await helper.balance.getEthereum(contract.options.address); + await contract.methods.flip().send({from: caller}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + const finalContractBalance = await helper.balance.getEthereum(contract.options.address); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + expect(finalContractBalance == initialContractBalance).to.be.true; + }); + + itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => { + const CONTRACT_BALANCE = 3n * helper.balance.getOneTokenNominal(); + + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await deployProxyContract(helper, deployer); + + const web3 = helper.getWeb3(); + await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS}); + + const collectionAddress = (await contract.methods.createNonfungibleCollection().send({from: caller})).events.CollectionCreated.returnValues.collection; + const initialCallerBalance = await helper.balance.getEthereum(caller); + const initialContractBalance = await helper.balance.getEthereum(contract.options.address); + await contract.methods.mintNftToken(collectionAddress).send({from: caller}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + const finalContractBalance = await helper.balance.getEthereum(contract.options.address); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + expect(finalContractBalance == initialContractBalance).to.be.true; + }); + + itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => { + const CONTRACT_BALANCE = 3n * helper.balance.getOneTokenNominal(); + + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await deployProxyContract(helper, deployer); + + const web3 = helper.getWeb3(); + await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS}); + + const initialCallerBalance = await helper.balance.getEthereum(caller); + const initialContractBalance = await helper.balance.getEthereum(contract.options.address); + await contract.methods.createNonfungibleCollection().send({from: caller}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + const finalContractBalance = await helper.balance.getEthereum(contract.options.address); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + expect(finalContractBalance < initialContractBalance).to.be.true; + }); + + async function deployProxyContract(helper: EthUniqueHelper, deployer: string) { + return await helper.ethContract.deployByCode( + deployer, + 'ProxyContract', + ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + import {CollectionHelpers} from "../api/CollectionHelpers.sol"; + import {UniqueNFT} from "../api/UniqueNFT.sol"; + + contract ProxyContract { + bool value = false; + address flipper; + + event CollectionCreated(address collection); + event TokenMinted(uint256 tokenId); + + receive() external payable {} + + constructor() { + flipper = address(new Flipper()); + } + + function flip() public { + value = !value; + Flipper(flipper).flip(); + } + + function createNonfungibleCollection() public { + address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F; + address nftCollection = CollectionHelpers(collectionHelpers).createNonfungibleCollection("A", "B", "C"); + emit CollectionCreated(nftCollection); + } + + function mintNftToken(address collectionAddress) public { + UniqueNFT collection = UniqueNFT(collectionAddress); + uint256 tokenId = collection.nextTokenId(); + collection.mint(msg.sender, tokenId); + emit TokenMinted(tokenId); + } + + function getValue() public view returns (bool) { + return Flipper(flipper).getValue(); + } + } + + contract Flipper { + bool value = false; + function flip() public { + value = !value; + } + function getValue() public view returns (bool) { + return value; + } + } + `, + [ + { + solPath: 'api/CollectionHelpers.sol', + fsPath: `${__dirname}/api/CollectionHelpers.sol`, + }, + { + solPath: 'api/UniqueNFT.sol', + fsPath: `${__dirname}/api/UniqueNFT.sol`, + }, + ], + ); + } +}); -- gitstuff