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 Answers1

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

Michael Amadi
  • 376
  • 1
  • 5
  • 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 (https://ethereum.stackexchange.com/questions/6354/how-do-i-construct-a-call-to-another-contract-using-inline-assembly). Why is it that it is slightly different in Huff? – JSON_Derulo Nov 09 '23 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? – JSON_Derulo Nov 09 '23 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? – JSON_Derulo Nov 09 '23 at 16:33