0

I'm writing tests for the burn func in ICO.sol

The first two tests are passing, but when I come to test where the tokens are indeed burned, the state of the ICO seems to change. To burn tokens, the ICO must have ended. So I start by checking the ICO state, and up to that point, the state is ended. When I call await ico.burn() the state seems to change and I get the error below.

What could I be doing wrong?

error

 Error: VM Exception while processing transaction: reverted with custom error 'Ico__StateShouldBeEnded()'

burn() func

    function burn() public onlyManager returns (bool) {
        icoState = getState();
        // require(icoState == State.afterEnd);
        if (icoState != State.afterEnd) {
            revert Ico__StateShouldBeEnded();
        }
        //burn
        balances[founder] = 0;
        return true;
    }

test file

describe("burning tokens", () => {
              it("checks that the ico is ended before burning", async () => {
                  const icoState = await ico.getState()
                  assert.equal(icoState, 1) // state [1] = afterEnd on our ICO.sol
              })

              it("only allows manager to call", async () => {
                  const icoInvestor1connected = ico.connect(inverstor1)
                  await expect(icoInvestor1connected.burn()).to.be.rejectedWith("not owner")
              })

              it("should burn tokens", async () => {
                  const icoState = await ico.getState()
                  assert.equal(icoState, 1) // state [1] = afterEnd on our ICO.sol
                  
                  // up to this point the test passes. 
                  // it fails from the code below.

                  await ico.burn()
                  const founderBal = await ico.getFounderBalance()
                  assert.equal(founderBal.toString(), "0")
              })
          })

Repo

https://github.com/kihiuFrank/ICO

2 Answers 2

1

I had a look at your contract on GitHub and you are running into an edge case for your contract.

In the test function you call ico.getState() which is a view function that reads the current state in the current block. Because this is a view function the block.timestamp will not increase and will be the same as what it was when you deployed the contract. Since icoStart is also set to be whatever the block timestamp was the contract was deployed, you have a state where icoStart == block.timestamp.

getState() does not test for equality so it falls to else statement returning State.afterEnd.

When you actually run the burn() function. This is a write transaction that increases the block number and hence the timestamp. Now the value of block.timestamp is icoStart++ so then getState() will return State.running which is why the burn() function reverts.

Your test should warp 3600 seconds for the test to pass since you are testing that the ICO is over.

it("should burn tokens", async () => {
                  await network.provider.send("evm_increaseTime", [3600])
                  const icoState = await ico.getState()
                  assert.equal(icoState, 1)
                  await ico.burn()
                  const founderBal = await ico.getFounderBalance()
                  assert.equal(founderBal.toString(), "0")
              })
4
  • Thanks a lot. This solve it! Should I and an else if on getState() or is it ok in your opinion? Something like; ``` else if (block.timestamp == icoStart) { return State.running; } ``` Edit; I added and test it. I'll leave it there. Thanks again.
    – kihiuFrank
    Commented May 3, 2023 at 15:32
  • I noticed something. After adding the equality condition in the comment above, the state remains running even after increasing the block timestamp by 3600s. It doesn't respect this condition block.timestamp < icoEnd
    – kihiuFrank
    Commented May 3, 2023 at 16:40
  • Yes because no block has been mined after you have increased the time. Because the getState() function is a view function, the value of block.timestamp will not change and will use the latest block (which was the deployment block) as it's value. If you add a non view transaction after you increase the time. Something like await deployer.sendTransaction({to: ethers.constants.AddressZero}) It will update the block.timestamp and the first assert will pass.
    – Blue Alder
    Commented May 4, 2023 at 1:14
  • Thanks a lot. I get it now.
    – kihiuFrank
    Commented May 4, 2023 at 8:00
1

Your contract is failing here, which is exactly what is intended to do:

 Error: VM Exception while processing transaction: reverted with custom error 'Ico__StateShouldBeEnded()'

To test this error, you can do something like:

await expect(ico.burn()).to.be.revertedWithCustomError(contractName, "StateShouldBeEnded");
2
  • Thanks for the input. It gave me a better way to write the first test. But the issue I was facing was testing that the founder token balance is updated to zero after burning. When I call burn() the state seems to be running still. How do I change state to ended so it doesn't throw the Ico__StateShouldBeEnded() error?
    – kihiuFrank
    Commented May 3, 2023 at 12:11
  • If the transaction reverts, it won't make any change, the ico.burn function should succeed in order to have the balance updated
    – Adam Boudj
    Commented May 4, 2023 at 8:46

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.