What does selfdestruct do exactly?

I read in many places that selfdestruct can remove the contract bytecode on a blockchain and effectively nullifiy the functionalities on a contract at a given address, but I did some hands on tests and found out executing selfdestruct does nothing to a contract except moving the fund in that contract to a designated address. This happens in both a direct call and a delegatecall. See examples below. The contract bytecode length remains unchanged after calling selfdestruct. Wondering why this is the case and if I am missing or misunderstanding anything. Thanks.

contract Target {

    address public target;
    uint256 public val;

    function doIt() external {
        val = block.timestamp;
        selfdestruct(payable(msg.sender));
    }

    function getTimestamp() external view returns (uint256) {
        return block.timestamp;
    }
}

contract TestSelfDestruct {

    address public target;
    uint256 public val;

    constructor(address _target) {
        target = _target;
    }

    function setVal(uint256 _val) external {
        val = _val;
    }

    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }

    function doIt() external {
        bytes memory data = abi.encodeWithSignature("doIt()");
        (bool success, ) = target.delegatecall(data);
        require(success);
    }
}
1 Like

The calls won't fail but the state will be null.

You can read more here:

Thanks for this. This is what I thought. But I did get different results running tests in Foundry. Could this be an issue on the foundry side? Appreciate any help on proof running the script below:

// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "forge-std/Test.sol";

contract SimpleStorageTest is Test {
    SimpleStorage test;

    function setUp() public {
        test = new SimpleStorage();
        test.store(100);
    }

    function testSelfDestruct() public {
        assertEq(test.retrieve(), 100);
        test.close();
        test.store(110);
        assertEq(test.retrieve(), 110);
    }
}

contract SimpleStorage {
    address private owner;
    uint256 number;

    constructor() {
        owner = msg.sender;
    }

    function store(uint256 num) public {
        number = num;
    }

    function retrieve() public view returns (uint256){
        return number;
    }
 
    function close() public { 
        selfdestruct(payable(owner)); 
    }
}

Try to call store() please

The script has been modified with calling store(110). Here are the results.

  [19568] SimpleStorageTest::testSelfDestruct()
    β”œβ”€ [2246] SimpleStorage::retrieve() [staticcall]
    β”‚   └─ ← 100
    β”œβ”€ [7256] SimpleStorage::close()
    β”‚   └─ ← ()
    β”œβ”€ [3134] SimpleStorage::store(110)
    β”‚   └─ ← ()
    β”œβ”€ [246] SimpleStorage::retrieve() [staticcall]
    β”‚   └─ ← 110
    └─ ← ()

It appears to be an issue on Foundry: https://github.com/foundry-rs/foundry/issues/1543

1 Like