0

I'm writing unit tests for a solidity contract in a Hardhat project and I can't seem to find a selfdestruct matcher. Solidity Chai Matchers are a set of helper functions that make testing solidity smart contracts easier. They provide useful functions like emit and reverted allowing for really clear testing code of the form

expect(await contract.someFunction()).to.be.reverted();
expect(await contract.someFunction()).to.emit(contract, "EventName");
...

However I couldn't find any matchers for testing the selfdetruction of a contract. I would've expected there to be something along the lines of:

expect(await contract.die()).to.selfdestruct();

Does anybody know if there is in fact something like this, or if not, how to test for contract's self destruction?

2 Answers 2

1
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.18;

import '@openzeppelin/contracts/access/Ownable.sol';

contract Greeter is Ownable {

  event Transfer(address to, uint256 balance);

  constructor() payable {}

  function transferBalance() public onlyOwner {
    selfdestruct(payable(msg.sender));
    emit Transfer(msg.sender, address(this).balance);
  }
}

in your test.js:

const { expect } = require("chai");

describe("Greeter", function() {
  it("Should successfully update contract state after selfdestruct", async function() {
    const {deployer, signer2} = await ethers.getSigners();
    const Greeter = await ethers.getContractFactory("Greeter");
    // deploy contract and send ethers
    const greeter = await Greeter.deploy({value: 1000, from: deployer});
    await greeter.deployed();
    
    // connect the deployer to the contract
    await greeter.connect(deployer);

    // ensure deployer is owner
    expect(await greeter.owner()).to.equal(deployer)
    // check the contract balance
    expect(await ethers.provider.getBalance(greeter.address)).to.equal(1000)
    // check contract get the right code
    expect(await ethers.provider.getCode(greeter.address)).to.not.equal("0x")
    
     await greeter.transferBalance();
    // signer2 can't call the function
    expect(greeter.transferBalance()).to.be.revertedWith('Ownable: not owner')
    expect(await ethers.provider.getBalance(deplyer.address)).to.equal(1000)
    expect(await ethers.provider.getBalance(greeter.address)).to.equal(0)
    expect(await ethers.provider.getCode(greeter.address)).to.equal("0x")
    expect(greeter.transferBalance()).to.emit(Greeter, 'Transfer').withArgs(deployer, 1000)
  });
});

I believe this is the only way to test selfdestruct, that the contract code is deleted from blockchain.

Please note as per SolidityLang Docs selfdestruct will be deprecated starting v0.8.18 https://docs.soliditylang.org/en/latest/introduction-to-smart-contracts.html#deactivate-and-self-destruct

I hope this helped a bit.

0

Not sure if this is the idiomatic way to do this but I managed to accomplish what I was looking for by checking that the contract's code gets deleted after self-destruction:

await contract.die();
expect(await ethers.provider.getCode(contract.address)).to.equal("0x");

And to confirm that the contract's balance gets transferred to the specified destination you can just use changeEtherBalances

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.