2

I am trying to make an external function call, with arguments, to a contract written in Solidity from a contract written in Huff. I am able to successfully call an external contract when the function does not accept any arguments, however when it does accept arguments, the fallback function is always triggered and caused the transaction to revert. I know this because if I will add a simple fallback function that does nothing, then the transaction doesn't revert anymore. I have created a set of files to reproduce the issue and please note that I am using Foundry and foundry-huff for development.

Counter.sol:

// SPDX-License-Identifier: BSL-1.1
pragma solidity 0.8.19;

contract Counter {
    uint256 public count;

    constructor() {}

    // fallback() external {} // uncomment to stop increase from reverting

    function increment() public {
        count++;
    }

    function increase(uint256 amount_) public {
        count += amount_;
    }
}

HuffCounter.sol:

/// @title HuffCounter
/// @notice SPDX-License-Identifier: BSL-1.1

/* Interface */
#define function increment(address) nonpayable returns ()
#define function increase(address, uint256) nonpayable returns ()

/* Constructor */
#define macro CONSTRUCTOR() = takes (0) returns (0) {}

/* Methods */
#define macro INCREMENT_COUNT() = takes(0) returns (0) {
    __FUNC_SIG("increment()") 0x00 mstore

    0x00                // [ret_size]
    0x00                // [ret_offset, ret_size]
    0x04                // [args_size, ret_offset, ret_size]
    0x1C                // [args_offset, args_size, ret_offset, ret_size]
    0x00                // [value, args_offset, args_size, ret_offset, ret_size]
    0x04 calldataload   // [counter_addr, value, args_offset, args_size, ret_offset, ret_size]
    gas                 // [gas, counter_addr, value, args_offset, args_size, ret_offset, ret_size]
    call                // [successs]

    0x00 eq err jumpi
    cont jump

    err:
        0x00 0x00 revert
    cont:
        0x00 0x00 return
}

#define macro INCREASE_COUNT() = takes(0) returns (0) {
    __FUNC_SIG("increase(uint256)") 0x00 mstore
    0x24 calldataload
    0x04 mstore

    0x00                // [ret_size]
    0x00                // [ret_offset, ret_size]
    0x24                // [args_size, ret_offset, ret_size]
    0x20                // [args_offset, args_size, ret_offset, ret_size]
    0x00                // [value, args_offset, args_size, ret_offset, ret_size]
    0x04 calldataload   // [counter_addr, value, args_offset, args_size, ret_offset, ret_size]
    gas                 // [gas, counter_addr, value, args_offset, args_size, ret_offset, ret_size]
    call                // [successs]

    0x00 eq err jumpi
    cont jump

    err:
        0x00 0x00 revert
    cont:
        0x00 0x00 return
}

#define macro MAIN() = takes (0) returns (0) {
    // Identify which function is being called.
    0x00 calldataload 0xE0 shr
    
    dup1 __FUNC_SIG(increment) eq increment jumpi
    dup1 __FUNC_SIG(increase) eq increase jumpi

    0x00 0x00 revert

    increment:
        INCREMENT_COUNT()
    increase:
        INCREASE_COUNT()
}

HuffCounter.t.sol:

// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.15;

import "forge-std/Test.sol";
import "foundry-huff/HuffDeployer.sol";
import "forge-std/console.sol";

import {Counter} from "src/Counter.sol";

contract BundleExecutorTest is Test {
    Counter public counter;
    IHuffCounter public huffCounter;

    function setUp() public {
        counter = new Counter();
        huffCounter = IHuffCounter(HuffDeployer.deploy("HuffCounter"));
    }

    function testIncrement() public {
        huffCounter.increment(address(counter));

        uint256 count_ = counter.count();

        console.log(count_);
    }

    function testIncrease() public {
        huffCounter.increase(address(counter), 3);

        uint256 count_ = counter.count();

        console.log(count_);
    }
}

interface IHuffCounter {
    function increment(address) external;
    function increase(address, uint256) external;
}
1
  • @Michael Amadi could you please take a look at the comments below when you have a chance? Commented Nov 12, 2023 at 12:19

1 Answer 1

3

"increment(uint256)" is abi encoded a bit incorrectly

The function sig is stored at mem[0x1c:0x20] since __FUNC_SIG() returns hex that are left padded The uint256 var should be stored at offset 0x20 and not 0x04

Also the args_offset should be 0x1C not 0x20

So something like this should work

    __FUNC_SIG("increase(uint256)") 0x00 mstore
    0x24 calldataload
    0x20 mstore

    0x00                // [ret_size]
    0x00                // [ret_offset, ret_size]
    0x24                // [args_size, ret_offset, ret_size]
    0x1C                // [args_offset, args_size, ret_offset, ret_size]
    0x00                // [value, args_offset, args_size, ret_offset, ret_size]
    0x04 calldataload   // [counter_addr, value, args_offset, args_size, ret_offset, ret_size]
    gas                 // [gas, counter_addr, value, args_offset, args_size, ret_offset, ret_size]
    call                // [successs]

    0x00 eq err jumpi
    cont jump

    err:
        0x00 0x00 revert
    cont:
        0x00 0x00 return
3
  • Thanks so much for the help. From looking around to see how this would be done using inline Solidity assembly, it seemed that the arguments should go immediately next to the function signature (ethereum.stackexchange.com/questions/6354/…). Why is it that it is slightly different in Huff? Commented Nov 9, 2023 at 16:27
  • And then wouldn't the args_size be 0x40 now since we use 32 bytes for the signature and 32 bytes for the argument? Commented Nov 9, 2023 at 16:29
  • One last question, what is the reason we set the args_offset so 0x1C? Would this value change if we had more than one argument? Commented Nov 9, 2023 at 16:33

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.