Abigen implements a docker-based golang abigen tool that can be ran directly from go without solc.
Usage (from flattened file):
package generate
//go:generate go run github.com/synapsecns/sanguine/tools/abigen generate --sol /path/to/flattened.sol --pkg pkgname --sol-version 0.6.12 --filename filename
Usage (from etherscan):
package generate
//go:generate go run github.com/synapsecns/sanguine/tools/abigen generate-from-etherscan --address=0x6b175474e89094c44da98b954eedeac495271d0f --chainID 1 --pkg dai --sol-version 0.5.12 --filename dai
Using abigen this way can occasionally cause you to run into the following error string: missing go.sum entry for module providing package
. This is well documented in several projects made to be run from go-generate. If this is the case, you can create a package exclusively for updating the go.mod.
To do this, create a package called internal/dev
. This should not be imported from anywhere. Add the following string to it for each missing import:
// Package dev contains dev dependencies required for running developer tasks (coverage testutils, etc)
// that are not required by the project itself. In order to enforce this constraint, this module panics upon
// being imported. Dependencies here are not included in produced binaries and won't affect the dev build
package dev
import (
_ "github.com/path/to/missing/package"
"github.com/synapsecns/sanguine/core"
)
func init() {
if !core.IsTest() {
panic("could not import dev package: this package is meant to define dependencies, not be imported.")
}
}
Right now, interfaces are not automatically generated, which would be useful for testing via mockery. The closest we can do is something like the following sequence:
In a helpers.go
file, we create an IBridge object. This uses types generated below:
// IBridge wraps the generated bridge interface code.
type IBridge interface {
ISynapseBridgeCaller
ISynapseBridgeFilterer
ISynapseBridgeTransactor
}
In a generate.go
, we add the following for generation:
// generate the contract:
//go:generate go run github.com/synapsecns/sanguine/agents/tools/abigen generate --sol ../../external/contracts/build/SynapseBridge.sol --pkg bridge --sol-version 0.6.12 --filename bridge
// using the contract, generate an interface for each type generated by abigen. This is always [contract]Caller, [contract]Transactor, [contract]Filterer
//go:generate go run github.com/vburenin/ifacemaker -f bridge.abigen.go -s SynapseBridgeCaller -i ISynapseBridgeCaller -p bridge -o icaller_generated.go -c "autogenerated file"
//go:generate go run github.com/vburenin/ifacemaker -f bridge.abigen.go -s SynapseBridgeTransactor -i ISynapseBridgeTransactor -p bridge -o itransactor_generated.go -c "autogenerated file"
//go:generate go run github.com/vburenin/ifacemaker -f bridge.abigen.go -s SynapseBridgeFilterer -i ISynapseBridgeFilterer -p bridge -o filterer_generated.go -c "autogenerated file"
// seperately, combine these into a mock
//go:generate go run github.com/vektra/mockery/v2 --name IBridge --output ./mocks --case=underscore
// ignore this line: go:generate cannot be the last line of a file
By convention, every contract handle we use implements vm.ContractRef
which allows a BoundContract
to return it’s current address. This is currently accomplished by adding the following helpers.go
file to the package we generate the file in:
package ecdsafactory
import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)
// ECDSAFactoryRef is a bound synapse bridge contract that returns the address of the contract.
//nolint: golint
type ECDSAFactoryRef struct {
*ECDSAFactory
address common.Address
}
// Address is the contract address.
func (s ECDSAFactoryRef) Address() common.Address {
return s.address
}
// NewECDSAFactoryRef creates a new ecdsa factory with a contract ref.
func NewECDSAFactoryRef(address common.Address, backend bind.ContractBackend) (*ECDSAFactoryRef, error) {
ECDSAFactory, err := NewECDSAFactory(address, backend)
if err != nil {
return nil, err
}
return &ECDSAFactoryRef{
ECDSAFactory: ECDSAFactory,
address: address,
}, nil
}
var _ vm.ContractRef = &ECDSAFactoryRef{}
This should be handled automatically.
Building in Golangci support: Right now go fmt
is run programatically on the output of the program. This does miss some things that are automatically fixed by golangci-lint run --config [config file]
. This could be run programatically in the same way go fmt is now.
sol-merger
. Currently, this package relies on sol-merger
. This package handles C3 linearization (removing duplicate abstracts, etc) and dependency resolution in the way that go native solidity flatteners (e.g. flattener) don’t yet. It’d be nice to be able to do this in abigen
so we could skip the merge step.doc.go
if one doesn’t exist documenting the packageIf you are using a Mac with Apple Silicon, you might encounter issues running AMD64 Docker images due to the Rosetta translation layer. Rosetta is a dynamic binary translator that allows applications compiled for Intel processors to run on Apple Silicon. However, it may not always work seamlessly with Docker images designed for AMD64 architecture.
To resolve this issue, you can:
softwareupdate --install-rosetta
Update Docker: Ensure your Docker Desktop is up-to-date by navigating to Settings > Software Update > Check for Updates.
For a detailed guide on fixing this issue, refer to this blog post.
To mitigate these issues, we plan to implement a fallback mechanism that downloads solc
directly, bypassing the need for Docker-based solutions on incompatible architectures. For more details on this planned improvement, see issue #3366.