Skip to content

disasm: Names for Tuple Components are not set #148

Description

@yohamta0

Problem

When inferring ABIs from bytecode, WhatsABI currently omits names for tuple components that are likely derived from Solidity structs. While the ABI specification technically allows unnamed tuple components, Solidity always generates names for struct members when creating the ABI. This discrepancy causes compatibility issues with libraries like go-ethereum that expect these names to be present when working with structs (See https://github.com/ethereum/go-ethereum/blob/v1.14.11/accounts/abi/type.go#L181).

Example contract:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract TestC {

    struct MyTuple {
        uint256 num;
        string str;
        bool flag;
    }

    function getTuple() external pure returns (MyTuple memory) {
        return MyTuple(123, "hello", true);
    }

    function setTuple(MyTuple calldata inputTuple) external pure returns (uint256, string memory, bool) {
        return (inputTuple.num, inputTuple.str, inputTuple.flag);
    }
}

ABI Generated by Solidity Compiler:

{
  "contracts": {
    "test.sol:TestC": {
      "abi": [
        {
          "inputs": [],
          "name": "getTuple",
          "outputs": [
            {
              "components": [
                {
                  "internalType": "uint256",
                  "name": "num",
                  "type": "uint256"
                },
                {
                  "internalType": "string",
                  "name": "str",
                  "type": "string"
                },
                {
                  "internalType": "bool",
                  "name": "flag",
                  "type": "bool"
                }
              ],
              "internalType": "struct TestC.MyTuple",
              "name": "",
              "type": "tuple"
            }
          ],
          "stateMutability": "pure",
          "type": "function"
        },
        {
          "inputs": [
            {
              "components": [
                {
                  "internalType": "uint256",
                  "name": "num",
                  "type": "uint256"
                },
                {
                  "internalType": "string",
                  "name": "str",
                  "type": "string"
                },
                {
                  "internalType": "bool",
                  "name": "flag",
                  "type": "bool"
                }
              ],
              "internalType": "struct TestC.MyTuple",
              "name": "inputTuple",
              "type": "tuple"
            }
          ],
          "name": "setTuple",
          "outputs": [
            {
              "internalType": "uint256",
              "name": "",
              "type": "uint256"
            },
            {
              "internalType": "string",
              "name": "",
              "type": "string"
            },
            {
              "internalType": "bool",
              "name": "",
              "type": "bool"
            }
          ],
          "stateMutability": "pure",
          "type": "function"
        }
      ]
    }
  },
  "version": "0.8.25+commit.b61c2a91.Darwin.appleclang"
}

Proposed solution

Fill in the component names with _n when they're empty.

Fill in the component names with field${n} when they're empty.

Example:

{
   "type": "function",
   "selector": "0x95d376d7",
   "payable": false,
   "stateMutability": "payable",
   "inputs": [
     {
       "type": "tuple",
       "name": "",
       "components": [
         { "type": "uint32", "name": "" },
         { "type": "bytes", "name": "" },
         { "type": "bytes32", "name": "" },
         { "type": "uint64", "name": "" },
         { "type": "address", "name": "" }
       ]
     },
     { "type": "bytes", "name": "" }
   ],
   "sig": "assignJob((uint32,bytes,bytes32,uint64,address),bytes)",
   "name": "assignJob",
   "constant": false
}

To be:

{
  "type": "function",
  "selector": "0x95d376d7",
  "payable": false,
  "stateMutability": "payable",
  "inputs": [
    {
      "type": "tuple",
      "name": "",
      "components": [
        { "type": "uint32", "name": "field0" },
        { "type": "bytes", "name": "field1" },
        { "type": "bytes32", "name": "field2" },
        { "type": "uint64", "name": "field3" },
        { "type": "address", "name": "field4" }
      ]
    },
    { "type": "bytes", "name": "" }
  ],
  "sig": "assignJob((uint32,bytes,bytes32,uint64,address),bytes)",
  "name": "assignJob",
  "constant": false
}

EDIT: Adjusted the placeholder format that should be supported by geth.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions