0

I'm writing a Smart Contract that individuals can donate to, and a specific address can withdraw all ether from the contract. I'm trying to write a test that validates that the withdrawer receives all money stored in the contract, but my asserts are failing when checking the expected final balance. I'm trying to adjust for gas used on all transactions. The actual is always slight less than the expected so I'm assuming I'm missing some gas cost somewhere. I'm using hardhat for testing.

contract:

pragma solidity ^0.8.0;

// A donation campaign being run
contract Campaign {
  
  // the owner of the contract
  address payable public immutable owner;

  // the person running the donation campaign
  // this person may also be the beneficiary
  address payable public immutable runner;

  // the beneficiary of the campaign
  // this person may also be the campaign runner
  address payable public immutable beneficiary;

  // initializes owner, the runner of the campaign, and the contract beneficiary.
  // beneficiary and runner may have the same value
  constructor(address payable _runner, address payable _beneficiary) {
    owner = payable(msg.sender);
    runner = _runner;
    beneficiary = _beneficiary;
  }

  // accepts donation payment
  function donate() public payable {
    // nothing to do here... yet...
  }

  // allows either the owner or the beneficiary to withdraw the donations
  function withdraw() public {
    require(msg.sender == beneficiary || msg.sender == owner, "only beneficiary or owner can withdraw");
    payable(msg.sender).transfer(address(this).balance);
  }
}

test function:

async function assertWithdrawSucceeds(donator, withdrawer) {
  amount = ethers.utils.parseEther("2");
  // load contract with money
  txResp = await deployedCampaign.connect(donator).donate({value: amount});
  txReceipt = await txResp.wait();
  actuallyDonated = amount.sub(txReceipt.gasUsed);

  // store original balance
  originalWithdrawerBalance = await ethers.provider.getBalance(withdrawer.address);
  
  // withdraw
  txResp = await deployedCampaign.connect(withdrawer).withdraw();
  txReceipt = await txResp.wait();
  withdrawGas = ethers.BigNumber.from(txReceipt.gasUsed);
  
  // balances to check
  contractBalance = await ethers.provider.getBalance(deployedCampaign.address);
  withdrawerFinalBalance = await ethers.provider.getBalance(withdrawer.address);
  
  // asserts
  expect(contractBalance).to.equal(0);

  console.log("original withdraw balance: " + originalWithdrawerBalance);
  console.log("final withdraw balance: " + withdrawerFinalBalance);
  console.log("calculated final withdraw balance: " + originalWithdrawerBalance.add(actuallyDonated).sub(withdrawGas));

  expect(
    originalWithdrawerBalance.add(actuallyDonated).sub(withdrawGas)
    .eq(withdrawerFinalBalance)
  ).is.true();
}

hardhat.config.js

require("@nomiclabs/hardhat-waffle");

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.0",
  networks: {
    hardhat: {
      accounts: {
        accountsBalance: '10500000000000000000' // 10.5 ether
      }
    }
  }
};
1
  • if the contract call transaction is going to release some storage, your gas consumption will be lower because gas will be refunded at the end of the transaction. so you will never be able to estimate the exact amount of gas to use.
    – Nulik
    Commented Sep 21, 2023 at 20:35

1 Answer 1

2

receipt.gasUsed returns, as the name says, the used gas, not the Ether spent to consume that gas. If you think about it, what you are doing is wrong from a dimensional point of view: you are subtracting originalWithdrawerBalance (Ether) and gasUsed (a pure number).

To get the spent Ether you must multiply the gasUsed by the gas price of your transaction.

receipt.gasUsed.mul(receipt.effectiveGasPrice)
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.