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

abigen: Does not include all structs in binding #20148

Closed
yondonfu opened this issue Oct 2, 2019 · 10 comments
Closed

abigen: Does not include all structs in binding #20148

yondonfu opened this issue Oct 2, 2019 · 10 comments
Assignees

Comments

@yondonfu
Copy link
Contributor

yondonfu commented Oct 2, 2019

System information

OS & Version: OSX
Commit hash : df89233

I installed abigen by cloning go-ethereum at the above commit hash and running make devtools.

Expected behaviour

This is the relevant contract code:

pragma solidity ^0.5.11;
pragma experimental ABIEncoderV2;


contract Foo {
  struct Apple {
    uint256 a;
    uint256 b;
  }

  struct Pear {
    uint256 c;
    uint256 d;
  }

  function test() public view returns (Apple memory apple, Pear memory pear) {
    apple.a = 5;
    apple.b = 6;
    pear.c = 7;
    pear.d = 8;
  }
}

The Go binding should contain:

  • A Go struct corresponding to the Solidity Apple type
  • A Go struct corresponding to the Solidity Pear type
  • A Test() method that returns an anonymous struct that wraps the Go struct corresponding to the Solidity Apple type and the Go struct corresponding to the Solidity Pear type

Actual behaviour

The generated Go binding contains:

  • A Go struct Struct0 corresponding to the Solidity Apple type
  • A Test() method that returns an anonymous struct that includes Struct0 twice

The binding is missing a Go struct corresponding to the Solidity Pear type and the anonymous struct returned by the Test() method does not contain the correct fields.

This is what the Go binding looks like in the section of the code with struct definitions and the Test() method:

// Struct0 is an auto generated low-level Go binding around an user-defined struct.
type Struct0 struct {
	A *big.Int
	B *big.Int
}

// Test is a free data retrieval call binding the contract method 0xf8a8fd6d.
//
// Solidity: function test() constant returns(Struct0 apple, Struct0 pear)
func (_Foo *FooCaller) Test(opts *bind.CallOpts) (struct {
	Apple Struct0
	Pear  Struct0
}, error) {
	ret := new(struct {
		Apple Struct0
		Pear  Struct0
	})
	out := ret
	err := _Foo.contract.Call(opts, out, "test")
	return *ret, err
}

// Test is a free data retrieval call binding the contract method 0xf8a8fd6d.
//
// Solidity: function test() constant returns(Struct0 apple, Struct0 pear)
func (_Foo *FooSession) Test() (struct {
	Apple Struct0
	Pear  Struct0
}, error) {
	return _Foo.Contract.Test(&_Foo.CallOpts)
}

// Test is a free data retrieval call binding the contract method 0xf8a8fd6d.
//
// Solidity: function test() constant returns(Struct0 apple, Struct0 pear)
func (_Foo *FooCallerSession) Test() (struct {
	Apple Struct0
	Pear  Struct0
}, error) {
	return _Foo.Contract.Test(&_Foo.CallOpts)
}

I suspect this behavior is because abigen is not able to handle the internal types introduced into tuple types of contract ABIs as of solc 0.5.11.

Steps to reproduce the behaviour

  1. Compile the contract Foo.sol using solc 0.5.11 and store the ABI in Foo.json
  2. Run abigen --abi Foo.json --pkg main --type Foo --out foo.go
  3. Observe that the generated binding includes Struct0 that corresponds to the Solidity type Apple, but it does not include a Go struct that corresponds to the Solidity type Pear. Also, observe that the Test() method in the binding returns an anonymous struct with Struct0 nested inside twice.
@0x234
Copy link

0x234 commented Oct 4, 2019

I observe a similar issue when attempting to create bindings for https://github.com/omisego/plasma-contracts.

Solidity version: 0.5.11

abigen doesn't have a version flag or output information and I can't remember how I installed it.

abigen --abi PlasmaFramework.json --pkg PlasmaFramework > PlasmaFramework.go

Failed to generate ABI binding: .:9:11068: expected ';', found 'IDENT' compiler (and 1758 more errors)

@jeffprestes
Copy link
Contributor

@yondonfu Is the ABIEncoderV2 officially supported by Solidity devs yet?

@JBunCE I think your situation is different. Are you trying to generate go file from a ABI file, am I right? I think abigen work better when your source is a Solidity file.

@yondonfu
Copy link
Contributor Author

@jeffprestes ABIEncoderV2 is still an experimental feature and needs to be turned on via a pragma statement, but usage is becoming increasingly common. Some examples:

abigen works fine for contracts that use ABIEncoderV2 and solc < 0.5.11. But, as of solc 0.5.11, internal types are added to tuple types in a contract's ABI which abigen does not know how to handle yet.

@rjl493456442
Copy link
Member

@yondonfu Basically abigen uses all abi information to generate bindings.

This is the compiler output for abi

[{
	"constant":true,
	"inputs":[],
	"name":"test",
	"outputs":[
		{
			"components":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"apple","type":"tuple"
		},
		{
			"components":[{"name":"c","type":"uint256"},{"name":"d","type":"uint256"}],"name":"pear","type":"tuple"
		}
	],
	"payable":false,
	"stateMutability":"pure",
	"type":"function"
}]

You can find the description of return parameter type is "components":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}], there is no struct TYPE name in abi. So we can't distinguish Apple and Pear because in abi level, they are just SAME.

So only Struct0 is defined in binding.

@rjl493456442
Copy link
Member

For user experience wise, maybe it's not enough since user needs to convert Struct0 to user-defined structs Apple and Pear. But we can't do more here

@yondonfu
Copy link
Contributor Author

yondonfu commented Oct 16, 2019

When compiling using solc 0.5.11, the ABI output is:

[
	{
		"constant": true,
		"inputs": [],
		"name": "test",
		"outputs": [
			{
				"components": [
					{
						"internalType": "uint256",
						"name": "a",
						"type": "uint256"
					},
					{
						"internalType": "uint256",
						"name": "b",
						"type": "uint256"
					}
				],
				"internalType": "struct Foo.Apple",
				"name": "apple",
				"type": "tuple"
			},
			{
				"components": [
					{
						"internalType": "uint256",
						"name": "c",
						"type": "uint256"
					},
					{
						"internalType": "uint256",
						"name": "d",
						"type": "uint256"
					}
				],
				"internalType": "struct Foo.Pear",
				"name": "pear",
				"type": "tuple"
			}
		],
		"payable": false,
		"stateMutability": "view",
		"type": "function"
	}
]

Since each function output now also includes the internalType field, I think it should be possible for abigen to use the value specified for that field to generate separate Go structs for the Apple and Pear Solidity types even if Apple and Pear are composed of the same elementary uint256 types.

@rjl493456442
Copy link
Member

@yondonfu Wow, this is wonderful. Haven't tried the new version compiler before.

With internalType information, I think it's feasible to do more things. Okay I'll check the new documentation and see how much I can do here.

Thanks for the report!

@rjl493456442
Copy link
Member

@yondonfu Could you please try #20179?

@yondonfu
Copy link
Contributor Author

yondonfu commented Oct 29, 2019

@rjl493456442 abigen built from #20179 seems to be generating named structs using the contract ABI's internalType fields!

The binding generated for the example contract Foo mentioned in the OP contains the following Go structs that correspond to the Apple and Pear Solidity structs:

type FooApple struct {
    A *big.Int
    B *big.Int
}

type FooPear struct {
    C *big.Int
    D *big.Int
}

Having struct names derived from the contract instead of Struct0, Struct1, etc is great.

@rjl493456442
Copy link
Member

Fixed by #20179

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

No branches or pull requests

4 participants