Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

abi.JSON ERROR:unsupported arg type: INameService #30266

Closed
chen4903 opened this issue Aug 5, 2024 · 7 comments · Fixed by #30315
Closed

abi.JSON ERROR:unsupported arg type: INameService #30266

chen4903 opened this issue Aug 5, 2024 · 7 comments · Fixed by #30315

Comments

@chen4903
Copy link
Contributor

chen4903 commented Aug 5, 2024

System information

github.com/ethereum/go-ethereum v1.13.14

Expected behaviour

Success to unmarshal the ABI

Actual behaviour

ERROR: unsupported arg type: INameService

Steps to reproduce the behaviour

You could find this ABI from Etherscan

func TestUnmarshalABI(t *testing.T){
	jsonData := `[{"inputs":[{"components":[{"internalType":"uint256","name":"dailyLimit","type":"uint256"},{"internalType":"uint256","name":"txLimit","type":"uint256"},{"internalType":"uint256","name":"accountDailyLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"bool","name":"onlyWhitelisted","type":"bool"}],"internalType":"struct IMessagePassingBridge.BridgeLimits","name":"bridgeLimits","type":"tuple"},{"components":[{"internalType":"uint256","name":"lastTransferReset","type":"uint256"},{"internalType":"uint256","name":"bridged24Hours","type":"uint256"}],"internalType":"struct IMessagePassingBridge.AccountLimit","name":"accountDailyLimit","type":"tuple"},{"components":[{"internalType":"uint256","name":"lastTransferReset","type":"uint256"},{"internalType":"uint256","name":"bridged24Hours","type":"uint256"}],"internalType":"struct IMessagePassingBridge.BridgeDailyLimit","name":"bridgeDailyLimit","type":"tuple"},{"internalType":"contract INameService","name":"nameService","type":"INameService"},{"internalType":"bool","name":"isClosed","type":"bool"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"canBridge","outputs":[{"internalType":"bool","name":"isWithinLimit","type":"bool"},{"internalType":"string","name":"error","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeFrom18ToTokenDecimals","outputs":[{"internalType":"uint256","name":"normalized","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeFromTokenTo18Decimals","outputs":[{"internalType":"uint256","name":"normalized","type":"uint256"}],"stateMutability":"pure","type":"function"}]`
	result, err := abi.JSON(strings.NewReader(jsonData))
	if err != nil {
		t.Fatal("ERROR:", err)
	}
	fmt.Println("result:", result)
}

Is there a way to directly deserialize this kind of ABI from Etherscan? It seems that the ABI obtained from Etherscan is missing some information.
Related PR: #24572, #27551

@jwasinger
Copy link
Contributor

So I've manually downloaded all the contracts/deps from etherscan and created a combined json abi from them with solc. And this error results from trying to parse the combined abi with abigen.

So it does seem to be a bug with abigen.

@holiman
Copy link
Contributor

holiman commented Aug 9, 2024

It's not so much a bug in abigen. Here's the ABI - spec: https://docs.soliditylang.org/en/latest/abi-spec.html
We expect things like "type":"uint256", but not things like "type":"INameService". The former is using a set of predefined basic datatypes, the latter is using non-basic structs.

The use of custom structs (or interfaces) in ABI was not originally present, and it was never implemented in the go-ethereum abi parser.

@fjl
Copy link
Contributor

fjl commented Aug 15, 2024

I think we could work around this by treating any type with internalType: "contract ..." as address. https://docs.soliditylang.org/en/latest/abi-spec.html#mapping-solidity-to-abi-types

@naman1402
Copy link

Can I work on this, if yes someone explain what is wrong ?

@MariusVanDerWijden
Copy link
Member

MariusVanDerWijden commented Aug 15, 2024

It's kinda weird, solidity seems to usually resolve these to an address:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract Test {

    IERC20 p;

    constructor(IERC20 erc) {
        p = erc;
    }
}

ABI:

[
	{
		"inputs": [
			{
				"internalType": "contract IERC20",
				"name": "erc",
				"type": "address"
			}
		],
		"stateMutability": "nonpayable",
		"type": "constructor"
	}
]

Edit: I can't find a repro for it where it doesn't resolve to address, maybe etherscan does something weird?

@chen4903
Copy link
Contributor Author

chen4903 commented Aug 15, 2024

I found that the compiler cannot handle contract information imported externally, resulting in the ABI referencing the external contract's name, and the name is not a type supported by the ABI.

I only made the following changes, and then the tests passed:

"internalType": "contract INameService",
"name": "nameService",
"type": "INameService"  =>  "type": "address" 

Testing:

package main

import (
	"fmt"
	"strings"
	"testing"

	"github.com/ethereum/go-ethereum/accounts/abi"
)

func TestUnmarshalABI(t *testing.T){
	jsonData := `[ { "inputs": [ { "components": [ { "internalType": "uint256", "name": "dailyLimit", "type": "uint256" }, { "internalType": "uint256", "name": "txLimit", "type": "uint256" }, { "internalType": "uint256", "name": "accountDailyLimit", "type": "uint256" }, { "internalType": "uint256", "name": "minAmount", "type": "uint256" }, { "internalType": "bool", "name": "onlyWhitelisted", "type": "bool" } ], "internalType": "struct IMessagePassingBridge.BridgeLimits", "name": "bridgeLimits", "type": "tuple" }, { "components": [ { "internalType": "uint256", "name": "lastTransferReset", "type": "uint256" }, { "internalType": "uint256", "name": "bridged24Hours", "type": "uint256" } ], "internalType": "struct IMessagePassingBridge.AccountLimit", "name": "accountDailyLimit", "type": "tuple" }, { "components": [ { "internalType": "uint256", "name": "lastTransferReset", "type": "uint256" }, { "internalType": "uint256", "name": "bridged24Hours", "type": "uint256" } ], "internalType": "struct IMessagePassingBridge.BridgeDailyLimit", "name": "bridgeDailyLimit", "type": "tuple" }, { "internalType": "contract INameService", "name": "nameService", "type": "address" }, { "internalType": "bool", "name": "isClosed", "type": "bool" }, { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "canBridge", "outputs": [ { "internalType": "bool", "name": "isWithinLimit", "type": "bool" }, { "internalType": "string", "name": "error", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "uint8", "name": "decimals", "type": "uint8" } ], "name": "normalizeFrom18ToTokenDecimals", "outputs": [ { "internalType": "uint256", "name": "normalized", "type": "uint256" } ], "stateMutability": "pure", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "uint8", "name": "decimals", "type": "uint8" } ], "name": "normalizeFromTokenTo18Decimals", "outputs": [ { "internalType": "uint256", "name": "normalized", "type": "uint256" } ], "stateMutability": "pure", "type": "function" } ]`
	result, err := abi.JSON(strings.NewReader(jsonData))
	if err != nil {
		t.Fatal("ERROR:", err)
	}
	fmt.Println("result:", result)
}

@fjl
Copy link
Contributor

fjl commented Aug 15, 2024

Yeah. So as I wrote, the correct fix on our end is to treat all types with "internalType": "contract ..." as if they had "type": "address". We need to apply this transform somewhere in package abi.

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

Successfully merging a pull request may close this issue.

7 participants