Skip to content

Instantly share code, notes, and snippets.

@darkerego
Last active September 1, 2023 22:00

Revisions

  1. darkerego renamed this gist Sep 1, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. darkerego revised this gist Sep 1, 2023. 1 changed file with 53 additions and 52 deletions.
    105 changes: 53 additions & 52 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,74 +1,75 @@
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "contracts/simulateCalls/ViewStorageAccessible.sol";
    contract StaticCallCheck {
    uint256 public stateVariable = 0;
    uint256 public fakeTimestamp = 0;
    uint256 public lol = 100;
    uint public oldLol;
    bool public lastSuccess ;
    bytes4 constant CHANGE_LOL_SEL = bytes4(keccak256(bytes('changeLol()')));


    contract TestStateChange {
    uint public originalValue = 123;
    uint public oldValue;
    uint public incrementer = 0;
    bytes4 public constant CHANGE_SEL = bytes4(keccak256(bytes('changeState(uint256)')));
    bytes4 public constant WRAP_CHANGE_SEL = bytes4(keccak256(bytes('wrapChangeState(uint256)')));
    error CallFailedError(string reason,address msgSender,address txOrigin);
    event CallFailed(string indexed reason);
    event CallSuccess(string indexed reason);

    function changeLol() external returns(uint256 _lol) {
    oldLol = lol;
    require(msg.sender == address(this) && tx.origin != address(0), "?origin");
    lol += lol;
    require(lol > oldLol, "Change not updated!");
    _lol = lol;

    }
    function _getRevertReason(bytes memory returnData) internal pure returns(string memory) {
    // If the length is less than 68, then the transaction failed silently (without a revert message)
    if (returnData.length < 68) return "Transaction reverted silently";

    function bytesToUintAssembly(bytes memory input) public pure returns (uint256 result) {
    bytes memory revertData = new bytes(returnData.length - 68);
    // The first 4 bytes of returnData are the function hash and the next 64 bytes are the
    // length of the revert reason. The actual revert reason starts from the 68th byte.
    assembly {
    result := mload(add(input, 32))
    revertData := add(0x40, add(returnData, 0x24))
    }

    return abi.decode(revertData, (string));
    }

    function stateChangingFunx() external returns(bool success) {
    bytes memory currentLol;
    (success, currentLol) = address(this).call{value: 0}(abi.encodeWithSelector(CHANGE_LOL_SEL));
    require(success, "The call failed!");
    lastSuccess = success;
    lol += lol;
    uint _currentLol = bytesToUintAssembly(currentLol);
    if (lol == _currentLol) {
    revert("The state did not change!");
    } else {

    function changeState(uint256 _value) external returns(bool success, string memory reason) {
    require(_value != originalValue, "Choose any other number");
    oldValue = originalValue;
    originalValue = _value;
    if (oldValue != originalValue) {
    success = true;
    reason = 'OK';
    emit CallSuccess(reason);
    } else {
    reason = "The value did not change!";
    //revert CallFailedError(reason, msg.sender, tx.origin);
    emit CallFailed(reason);
    }

    }

    function setStateVariable(uint256 newValue) public {
    uint256 initialGas = gasleft();

    // Try to change state
    stateVariable = newValue * 2;
    function testFunc() public returns(bool success) {
    require(msg.sender == tx.origin, "Function cannot be called with staticCall or web3.eth.call()");
    incrementer += 1;
    success = true;
    }

    uint256 finalGas = gasleft();
    uint256 gasConsumed = initialGas - finalGas;


    // Mimic a timestamp change
    fakeTimestamp = block.timestamp;

    // Check if state changes were effective
    if (stateVariable != newValue * 2 || fakeTimestamp != block.timestamp) {
    revert("Possible staticcall detected via state changing variables");
    }
    function wrapChangeState(uint _value) public returns(bool success, bytes memory ret) {
    // require(msg.sender == tx.origin, "Sender!Origin");
    require(_value != originalValue, "Choose any other number");
    uint currentValue = originalValue;
    bytes memory data = abi.encodeWithSelector(CHANGE_SEL, _value);
    (success, ret) = address(this).call{value: 0}(data);
    require(success, "Call Failed!");
    require(currentValue != originalValue, "State did not change!");
    success = true;
    }

    // Use gas consumption to further infer the type of call
    if (gasConsumed == 0) {
    revert("Possible staticcall detected via gas consumed");
    }
    function wrapChangeStateView(uint _value) public view returns(bool success) {
    bytes memory returnData;
    (success, returnData) = address(this).staticcall(abi.encodeWithSelector(WRAP_CHANGE_SEL, _value));
    if (! success) {
    string memory _reason = _getRevertReason(returnData);
    revert CallFailedError(_reason, msg.sender, tx.origin);

    if (msg.sender == address(0) || tx.origin == address(0)) {
    revert("Possible staticcall detected via send/origin");
    }

    stateVariable = newValue;
    require(this.stateChangingFunx(), "Static call!");
    }
    }

    }
  3. darkerego created this gist Sep 1, 2023.
    74 changes: 74 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "contracts/simulateCalls/ViewStorageAccessible.sol";
    contract StaticCallCheck {
    uint256 public stateVariable = 0;
    uint256 public fakeTimestamp = 0;
    uint256 public lol = 100;
    uint public oldLol;
    bool public lastSuccess ;
    bytes4 constant CHANGE_LOL_SEL = bytes4(keccak256(bytes('changeLol()')));



    function changeLol() external returns(uint256 _lol) {
    oldLol = lol;
    require(msg.sender == address(this) && tx.origin != address(0), "?origin");
    lol += lol;
    require(lol > oldLol, "Change not updated!");
    _lol = lol;

    }

    function bytesToUintAssembly(bytes memory input) public pure returns (uint256 result) {
    assembly {
    result := mload(add(input, 32))
    }
    }

    function stateChangingFunx() external returns(bool success) {
    bytes memory currentLol;
    (success, currentLol) = address(this).call{value: 0}(abi.encodeWithSelector(CHANGE_LOL_SEL));
    require(success, "The call failed!");
    lastSuccess = success;
    lol += lol;
    uint _currentLol = bytesToUintAssembly(currentLol);
    if (lol == _currentLol) {
    revert("The state did not change!");
    } else {
    success = true;
    }

    }

    function setStateVariable(uint256 newValue) public {
    uint256 initialGas = gasleft();

    // Try to change state
    stateVariable = newValue * 2;

    uint256 finalGas = gasleft();
    uint256 gasConsumed = initialGas - finalGas;

    // Mimic a timestamp change
    fakeTimestamp = block.timestamp;

    // Check if state changes were effective
    if (stateVariable != newValue * 2 || fakeTimestamp != block.timestamp) {
    revert("Possible staticcall detected via state changing variables");
    }

    // Use gas consumption to further infer the type of call
    if (gasConsumed == 0) {
    revert("Possible staticcall detected via gas consumed");
    }

    if (msg.sender == address(0) || tx.origin == address(0)) {
    revert("Possible staticcall detected via send/origin");
    }

    stateVariable = newValue;
    require(this.stateChangingFunx(), "Static call!");
    }
    }