Closed gakonst closed 2 years ago
Weird, there seems to be an inconsistency of ABI generated for a library
vs a contract
. Specifically if you s/library/contract
then it generates an ABI with:
[
{
"inputs": [
{
"internalType": "enum ContractTest.TestEnum",
"name": "test",
"type": "uint8"
}
],
"name": "foo",
"outputs": [],
"stateMutability": "view",
"type": "function"
}
]
Can we assume that custom names that are not tuples are enums? If so, it could make sense to do
_ => ParamType::Uint(8)
.
I think that's fine. However, I think we should first check if there are any other custom types that affect the type
field in this way. Some that come to mind:
Structs get converted to tuple
I believe, so they are caught earlier in the match statement.
User defined value types are zero-cost, so they all compile down to the native type (e.g. type A is uint256
) generates uint256 in the ABI.
I think we might be able to get away with this - cc @mattsse
If so, it could make sense to do _ => ParamType::Uint(8).
this seems appropriate, but then every unknown identifier will be treated as uint8
.
does it make sense to make two modes, like lenient and strict? as it's already used for the tokenziers
Yeah I guess the question is: Are there unknown identifiers which may NOT be uint8s?
Are there unknown identifiers which may NOT be uint8s?
My worry was that something like this:
pragma solidity >=0.8.10;
library L {
type T is uint256
function test(T t) public view {}
}
Would generate an ABI with:
[
{
"inputs": [
{
"internalType": "Test.Type",
"name": "t",
"type": "Test.Type"
}
],
"name": "test",
"outputs": [],
"stateMutability": "view",
"type": "function"
}
]
cc @chriseth maybe can help here
from the abi-spec docs about the type
field, which is "the canonical type of the parameter"
The canonical type is determined until a tuple type is reached and the string description up to that point is stored in type prefix with the word tuple
https://docs.soliditylang.org/en/v0.8.10/abi-spec.html#handling-tuple-types
which explains why structs are always type: tuple
, and user defined value types (type A is B
) are merely aliases so their canonical type is their actual type (B
).
but this doesn't explain why an enum is type: uint8
in contracts and type: <name>
in libraries. judging by the docs it seems like it should be always type: <name>
but since they're uint8 it would make more sense to always resolve them to uint8.
This feels like an inconsistency on solc's part.
Personally fine with us moving forward with assuming it's all u8's, think the fastest way to close the loop here is to make the change and find the edge downstream, if it exists. Any outstanding concerns on your end, @nlordell ?
Public library functions are not meant to be called "from outside". This means the calling convention of public library functions is considered to be solidity-internal, in other words, they actually don't even have an ABI.
The effect is that the function hashes of library functions differ from function hashes of contract functions. IIRC the main differences are:
the same applies recursively for compound types
Problem
It seems that ethabi cannot parse the ABI of a library contract with a
public
function (i.e. one that's intended to be linked)This was originally reported in https://github.com/gakonst/foundry/issues/349.
Repro
solc contract.sol --abi
generates:And then doing
panics with:
Diagnosis
The error originates in this match branch. Custom type names are seemingly not supported.
Solution
Can we assume that custom names that are not tuples are enums? If so, it could make sense to do
_ => ParamType::Uint(8)
. I'm not sure that assumption is correct. It'd be nice if we could access theinternalType
insideReader
, since it contains the wordenum
.