git.delta.rocks / unique-network / refs/commits / d0d6ce422c83

difftreelog

source

tests/src/eth/payable.test.ts12.2 KiBsourcehistory
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';1819import {itEth, expect, usingEthPlaygrounds, EthUniqueHelper} from './util';20import {makeNames} from '../util';2122const {dirname} = makeNames(import.meta.url);2324describe('EVM payable contracts', () => {25  let donor: IKeyringPair;2627  before(async function() {28    await usingEthPlaygrounds(async (_, privateKey) => {29      donor = await privateKey({url: import.meta.url});30    });31  });3233  itEth('Evm contract can receive wei from eth account', async ({helper}) => {34    const deployer = await helper.eth.createAccountWithBalance(donor);35    const contract = await helper.eth.deployCollectorContract(deployer);3637    const web3 = helper.getWeb3();3839    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS});4041    expect(await contract.methods.getCollected().call()).to.be.equal('10000');42  });4344  itEth('Evm contract can receive wei from substrate account', async ({helper}) => {45    const deployer = await helper.eth.createAccountWithBalance(donor);46    const contract = await helper.eth.deployCollectorContract(deployer);47    const [alice] = await helper.arrange.createAccounts([40n], donor);4849    const weiCount = '10000';5051    // Transaction fee/value will be payed from subToEth(sender) evm balance,52    // which is backed by evmToAddress(subToEth(sender)) substrate balance53    await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n);545556    await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);5758    expect(await contract.methods.getCollected().call()).to.be.equal(weiCount);59  });6061  // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible62  itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => {63    const deployer = await helper.eth.createAccountWithBalance(donor);64    const contract = await helper.eth.deployCollectorContract(deployer);65    const [alice] = await helper.arrange.createAccounts([10n], donor);6667    const weiCount = 10_000n;6869    await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false);7071    expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString());72  });7374  itEth('Balance can be retrieved from evm contract', async({helper}) => {75    const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal();76    const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal();7778    const deployer = await helper.eth.createAccountWithBalance(donor);79    const contract = await helper.eth.deployCollectorContract(deployer);80    const [alice] = await helper.arrange.createAccounts([20n], donor);8182    const web3 = helper.getWeb3();8384    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});8586    const [receiver] = await helper.arrange.createAccounts([0n], donor);8788    // First receive balance on eth balance of bob89    {90      const ethReceiver = helper.address.substrateToEth(receiver.address);91      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0');92      await contract.methods.withdraw(ethReceiver).send({from: deployer});93      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString());94    }9596    // Some balance is required to pay fee for evm.withdraw call97    await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE);98    // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString());99100    // Withdraw balance from eth to substrate101    {102      const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address);103      await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true);104      const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address);105106      expect(finalReceiverBalance > initialReceiverBalance).to.be.true;107    }108  });109});110111describe('EVM transaction fees', () => {112  let donor: IKeyringPair;113114  before(async function() {115    await usingEthPlaygrounds(async (_, privateKey) => {116      donor = await privateKey({url: import.meta.url});117    });118  });119120  itEth('Fee is withdrawn from the user', async({helper}) => {121    const deployer = await helper.eth.createAccountWithBalance(donor);122    const caller = await helper.eth.createAccountWithBalance(donor);123    const contract = await helper.eth.deployFlipper(deployer);124125    const initialCallerBalance = await helper.balance.getEthereum(caller);126    await contract.methods.flip().send({from: caller});127    const finalCallerBalance = await helper.balance.getEthereum(caller);128    expect(finalCallerBalance < initialCallerBalance).to.be.true;129  });130131  itEth('Fee for nested calls is withdrawn from the user', async({helper}) => {132    const deployer = await helper.eth.createAccountWithBalance(donor);133    const caller = await helper.eth.createAccountWithBalance(donor);134    const contract = await deployProxyContract(helper, deployer);135136    const initialCallerBalance = await helper.balance.getEthereum(caller);137    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);138    await contract.methods.flip().send({from: caller});139    const finalCallerBalance = await helper.balance.getEthereum(caller);140    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);141    expect(finalCallerBalance < initialCallerBalance).to.be.true;142    expect(finalContractBalance == initialContractBalance).to.be.true;143  });144145  itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => {146    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();147148    const deployer = await helper.eth.createAccountWithBalance(donor);149    const caller = await helper.eth.createAccountWithBalance(donor);150    const contract = await deployProxyContract(helper, deployer);151152    const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;153    const initialCallerBalance = await helper.balance.getEthereum(caller);154    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);155    await contract.methods.mintNftToken(collectionAddress).send({from: caller});156    const finalCallerBalance = await helper.balance.getEthereum(caller);157    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);158    expect(finalCallerBalance < initialCallerBalance).to.be.true;159    expect(finalContractBalance == initialContractBalance).to.be.true;160  });161162  itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => {163    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();164    const deployer = await helper.eth.createAccountWithBalance(donor);165    const caller = await helper.eth.createAccountWithBalance(donor);166    const contract = await deployProxyContract(helper, deployer);167168    const initialCallerBalance = await helper.balance.getEthereum(caller);169    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);170    await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});171    const finalCallerBalance = await helper.balance.getEthereum(caller);172    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);173    expect(finalCallerBalance < initialCallerBalance).to.be.true;174    expect(finalContractBalance == initialContractBalance).to.be.true;175  });176177  itEth('Negative test: call createNFTCollection with wrong fee', async({helper}) => {178    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();179    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();180    const caller = await helper.eth.createAccountWithBalance(donor);181    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);182183    await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');184    await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');185  });186187  itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => {188    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();189    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();190    const caller = await helper.eth.createAccountWithBalance(donor);191    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);192193    await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');194    await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');195  });196197  itEth('Get collection creation fee', async({helper}) => {198    const deployer = await helper.eth.createAccountWithBalance(donor);199    expect(await helper.eth.getCollectionCreationFee(deployer)).to.be.equal(String(2n * helper.balance.getOneTokenNominal()));200  });201202  async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {203    return await helper.ethContract.deployByCode(204      deployer,205      'ProxyContract',206      `207      // SPDX-License-Identifier: UNLICENSED208      pragma solidity ^0.8.6;209210      import {CollectionHelpers} from "../api/CollectionHelpers.sol";211      import {UniqueNFT} from "../api/UniqueNFT.sol";212213      error Value(uint256 value);214215      contract ProxyContract {216        bool value = false;217        address innerContract;218219        event CollectionCreated(address collection);220        event TokenMinted(uint256 tokenId);221222        receive() external payable {}223224        constructor() {225          innerContract = address(new InnerContract());226        }227228        function flip() public {229          value = !value;230          InnerContract(innerContract).flip();231        }232233        function createNFTCollection() external payable {234          address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;235		      address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C");236          emit CollectionCreated(nftCollection);237        }238239        function mintNftToken(address collectionAddress) external  {240          UniqueNFT collection = UniqueNFT(collectionAddress);241          uint256 tokenId = collection.mint(msg.sender);242          emit TokenMinted(tokenId);243        }244245        function getValue() external view returns (bool) {246          return InnerContract(innerContract).getValue();247        }248      }249250      contract InnerContract {251        bool value = false;252        function flip() external {253          value = !value;254        }255        function getValue() external view returns (bool) {256          return value;257        }258      }259      `,260      [261        {262          solPath: 'api/CollectionHelpers.sol',263          fsPath: `${dirname}/api/CollectionHelpers.sol`,264        },265        {266          solPath: 'api/UniqueNFT.sol',267          fsPath: `${dirname}/api/UniqueNFT.sol`,268        },269      ],270    );271  }272});