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 doesn't support linking of contracts and libs #17010

Closed
vincentserpoul opened this issue Jun 17, 2018 · 7 comments
Closed

abigen doesn't support linking of contracts and libs #17010

vincentserpoul opened this issue Jun 17, 2018 · 7 comments
Assignees

Comments

@vincentserpoul
Copy link
Contributor

vincentserpoul commented Jun 17, 2018

Hi there,

please note that this is an issue tracker reserved for bug reports and feature requests.

For general questions please use the gitter channel or the Ethereum stack exchange at https://ethereum.stackexchange.com.

System information

Geth version: 1.8.11
OS & Version: Linux
Commit hash : (if develop)

Expected behaviour

If a lib is used in a contract, it should be either a param of the Deploy function (probably the best) or the deploy should take care of the deployment of the lib first.

Actual behaviour

Contract deployment just doesn't work, no error.

Steps to reproduce the behaviour

pragma solidity ^0.4.24;

library Math {
    function add(uint a, uint b) public view returns(uint) {
        return a + b;
    }
}

contract Example {
    function add (uint c, uint d) public view returns(uint) {
        return  Math.add(c,d);
    }
}
abigen --sol=test.sol --pkg=testlink --out=./test.go
package testlink

import (
	"log"
	"math/big"
	"os"
	"testing"

	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/crypto"
)


func TestExample(t *testing.T) {

	_, _, exI, err := DeployExample(testAuth, testSimBackend)
	if err != nil {
		t.Fatalf("DeployExample: %v", err)
	}
	testSimBackend.Commit()

	res, errG := exI.Add(&bind.CallOpts{}, big.NewInt(1), big.NewInt(1))
	if errG != nil {
		t.Fatalf("Add: %v", errG)
	}
	if !reflect.DeepEqual(res, big.NewInt(2)) {
		t.Errorf("Expected %d, got %d", big.NewInt(2), res)
	}
}

var testSimBackend *backends.SimulatedBackend
var testAuth *bind.TransactOpts

func TestMain(m *testing.M) {

	testAuthKey, err := crypto.GenerateKey()
	if err != nil {
		log.Fatal(err)
	}
	testAuth = bind.NewKeyedTransactor(testAuthKey)

	testAuth.GasLimit = 4000000

	alloc := make(core.GenesisAlloc)
	alloc[testAuth.From] = core.GenesisAccount{
		Balance: big.NewInt(1337000000000),
	}
	testSimBackend = backends.NewSimulatedBackend(alloc)

	retCode := m.Run()

	os.Exit(retCode)

}
go run test ./...

Add: abi: unmarshalling empty output

@vincentserpoul
Copy link
Contributor Author

replacing the DeployExample by the following

// DeployExample deploys a new Ethereum contract, binding an instance of Example to it.
func DeployExample(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Example, error) {
	addr, _, _, _ := DeployMath(auth, backend)
	linkedContract := LinkBytecode(ExampleBin, "test.sol", "Math", addr)
	parsed, err := abi.JSON(strings.NewReader(ExampleABI))
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(linkedContract), backend)
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	return address, tx, &Example{ExampleCaller: ExampleCaller{contract: contract}, ExampleTransactor: ExampleTransactor{contract: contract}, ExampleFilterer: ExampleFilterer{contract: contract}}, nil
}

// LinkBytecode will link a deployed library to a bytecode
func LinkBytecode(
	byteCode string,
	contractName string,
	libName string,
	libAddr common.Address,
) string {
	lib := contractName + ":" + libName
	if rightPadLen := 40 - len(lib) - 2; rightPadLen >= 2 {
		lib = "__" + lib
		lib += strings.Repeat("_", rightPadLen)
	}
	return strings.Replace(
		byteCode,
		lib,
		libAddr.String()[2:],
		-1,
	)
}

solves the issue

@vincentserpoul
Copy link
Contributor Author

The LinkBytecode is not accurate and should be included in the abigen (I didn't see it yet)

@stevenlcf
Copy link

stevenlcf commented Jun 6, 2019

Got the same issue. Earnestly hope to have this link feature in abigen soon.

@dankins
Copy link
Contributor

dankins commented Jun 6, 2019

@stevenlcf Here's our solution:
https://github.com/joincivil/go-common/blob/master/pkg/eth/utils.go#L69
Inspired by the code from @vincentserpoul earlier in the thread.
Hope it might help until this is officially supported.

@stevenlcf
Copy link

Awesome! Thanks, @dankins !

@stevenlcf
Copy link

stevenlcf commented Jun 13, 2019

Hey, @dankins , just a follow-up, the utils you provided solved this problem wonderfully. Yet I think it could be even better: DeployContractWithLinks() also accepts params ...interface{} just like what DeployContract() does to support contract constructors with arguments.

Updates: I created an issue for this improvement request and another bug report for this utils.go in your repo. I also submitted a pull request to fix the issue.

@gballet
Copy link
Member

gballet commented Jul 8, 2019

Fixed with #19718

@gballet gballet closed this as completed Jul 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants
@dankins @adamschmideg @vincentserpoul @gballet @stevenlcf and others