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

difftreelog

source

js-packages/tests/eth/payable.test.ts12.4 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 type {IKeyringPair} from '@polkadot/types/types';1819import {itEth, expect, usingEthPlaygrounds} from '@unique/test-utils/eth/util.js';20import {EthUniqueHelper} from '@unique/test-utils/eth/index.js';21import {makeNames} from '@unique/test-utils/util.js';2223const {dirname} = makeNames(import.meta.url);2425describe('EVM payable contracts', () => {26  let donor: IKeyringPair;2728  before(async function() {29    await usingEthPlaygrounds(async (_, privateKey) => {30      donor = await privateKey({url: import.meta.url});31    });32  });3334  itEth('Evm contract can receive wei from eth account', async ({helper}) => {35    const deployer = await helper.eth.createAccountWithBalance(donor);36    const contract = await helper.eth.deployCollectorContract(deployer);3738    const web3 = helper.getWeb3();3940    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS});4142    expect(await contract.methods.getCollected().call()).to.be.equal('10000');43  });4445  itEth('Evm contract can receive wei from substrate account', async ({helper}) => {46    const deployer = await helper.eth.createAccountWithBalance(donor);47    const contract = await helper.eth.deployCollectorContract(deployer);48    const [alice] = await helper.arrange.createAccounts([40n], donor);4950    const weiCount = '10000';5152    // Transaction fee/value will be payed from subToEth(sender) evm balance,53    // which is backed by evmToAddress(subToEth(sender)) substrate balance54    await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n);555657    await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);5859    expect(await contract.methods.getCollected().call()).to.be.equal(weiCount);60  });6162  // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible63  itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => {64    const deployer = await helper.eth.createAccountWithBalance(donor);65    const contract = await helper.eth.deployCollectorContract(deployer);66    const [alice] = await helper.arrange.createAccounts([10n], donor);6768    const weiCount = 10_000n;6970    await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false);7172    expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString());73  });7475  itEth('Balance can be retrieved from evm contract', async({helper}) => {76    const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal();77    const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal();7879    const deployer = await helper.eth.createAccountWithBalance(donor);80    const contract = await helper.eth.deployCollectorContract(deployer);81    const [alice] = await helper.arrange.createAccounts([20n], donor);8283    const web3 = helper.getWeb3();8485    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});8687    const [receiver] = await helper.arrange.createAccounts([0n], donor);8889    // First receive balance on eth balance of bob90    {91      const ethReceiver = helper.address.substrateToEth(receiver.address);92      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0');93      await contract.methods.withdraw(ethReceiver).send({from: deployer});94      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString());95    }9697    // Some balance is required to pay fee for evm.withdraw call98    await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE);99    // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString());100101    // Withdraw balance from eth to substrate102    {103      const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address);104      await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true);105      const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address);106107      expect(finalReceiverBalance > initialReceiverBalance).to.be.true;108    }109  });110});111112describe('EVM transaction fees', () => {113  let donor: IKeyringPair;114115  before(async function() {116    await usingEthPlaygrounds(async (_, privateKey) => {117      donor = await privateKey({url: import.meta.url});118    });119  });120121  itEth('Fee is withdrawn from the user', async({helper}) => {122    const deployer = await helper.eth.createAccountWithBalance(donor);123    const caller = await helper.eth.createAccountWithBalance(donor);124    const contract = await helper.eth.deployFlipper(deployer);125126    const initialCallerBalance = await helper.balance.getEthereum(caller);127    await contract.methods.flip().send({from: caller});128    const finalCallerBalance = await helper.balance.getEthereum(caller);129    expect(finalCallerBalance < initialCallerBalance).to.be.true;130  });131132  itEth('Fee for nested calls is withdrawn from the user', async({helper}) => {133    const deployer = await helper.eth.createAccountWithBalance(donor);134    const caller = await helper.eth.createAccountWithBalance(donor);135    const contract = await deployProxyContract(helper, deployer);136137    const initialCallerBalance = await helper.balance.getEthereum(caller);138    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);139    await contract.methods.flip().send({from: caller});140    const finalCallerBalance = await helper.balance.getEthereum(caller);141    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);142    expect(finalCallerBalance < initialCallerBalance).to.be.true;143    expect(finalContractBalance == initialContractBalance).to.be.true;144  });145146  itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => {147    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();148149    const deployer = await helper.eth.createAccountWithBalance(donor);150    const caller = await helper.eth.createAccountWithBalance(donor);151    const contract = await deployProxyContract(helper, deployer);152153    const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;154    const initialCallerBalance = await helper.balance.getEthereum(caller);155    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);156    await contract.methods.mintNftToken(collectionAddress).send({from: caller});157    const finalCallerBalance = await helper.balance.getEthereum(caller);158    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);159    expect(finalCallerBalance < initialCallerBalance).to.be.true;160    expect(finalContractBalance == initialContractBalance).to.be.true;161  });162163  itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => {164    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();165    const deployer = await helper.eth.createAccountWithBalance(donor);166    const caller = await helper.eth.createAccountWithBalance(donor);167    const contract = await deployProxyContract(helper, deployer);168169    const initialCallerBalance = await helper.balance.getEthereum(caller);170    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);171    await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});172    const finalCallerBalance = await helper.balance.getEthereum(caller);173    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);174    expect(finalCallerBalance < initialCallerBalance).to.be.true;175    expect(finalContractBalance == initialContractBalance).to.be.true;176  });177178  itEth('Negative test: call createNFTCollection with wrong fee', async({helper}) => {179    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();180    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();181    const caller = await helper.eth.createAccountWithBalance(donor);182    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);183184    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)');185    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)');186  });187188  itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => {189    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();190    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();191    const caller = await helper.eth.createAccountWithBalance(donor);192    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);193194    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)');195    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)');196  });197198  itEth('Get collection creation fee', async({helper}) => {199    const deployer = await helper.eth.createAccountWithBalance(donor);200    expect(await helper.eth.getCollectionCreationFee(deployer)).to.be.equal(String(2n * helper.balance.getOneTokenNominal()));201  });202203  async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {204    return await helper.ethContract.deployByCode(205      deployer,206      'ProxyContract',207      `208      // SPDX-License-Identifier: UNLICENSED209      pragma solidity ^0.8.6;210211      import {CollectionHelpers} from "../api/CollectionHelpers.sol";212      import {UniqueNFT} from "../api/UniqueNFT.sol";213214      error Value(uint256 value);215216      contract ProxyContract {217        bool value = false;218        address innerContract;219220        event CollectionCreated(address collection);221        event TokenMinted(uint256 tokenId);222223        receive() external payable {}224225        constructor() {226          innerContract = address(new InnerContract());227        }228229        function flip() public {230          value = !value;231          InnerContract(innerContract).flip();232        }233234        function createNFTCollection() external payable {235          address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;236		      address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C");237          emit CollectionCreated(nftCollection);238        }239240        function mintNftToken(address collectionAddress) external  {241          UniqueNFT collection = UniqueNFT(collectionAddress);242          uint256 tokenId = collection.mint(msg.sender);243          emit TokenMinted(tokenId);244        }245246        function getValue() external view returns (bool) {247          return InnerContract(innerContract).getValue();248        }249      }250251      contract InnerContract {252        bool value = false;253        function flip() external {254          value = !value;255        }256        function getValue() external view returns (bool) {257          return value;258        }259      }260      `,261      [262        {263          solPath: 'api/CollectionHelpers.sol',264          fsPath: `${dirname}/../../evm-abi/api/CollectionHelpers.sol`,265        },266        {267          solPath: 'api/UniqueNFT.sol',268          fsPath: `${dirname}/../../evm-abi/api/UniqueNFT.sol`,269        },270      ],271    );272  }273});