Skip to content

Latest commit

 

History

History

abigen

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

ABI Gen

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

Note:

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.")
  }
}

Future Work:

  1. 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
  2. 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.

  3. Test Cases:

    • Mock out an etherscan api & test that
    • Test several different versions of solc
  4. 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.

  5. Golang native 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.

  6. Consider generating a doc.go if one doesn't exist documenting the package

  7. Better vyper support

Note on macOS and Rosetta

If 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:

  1. Install Rosetta: If not already installed, run the following command in your terminal:

    softwareupdate --install-rosetta
  2. Update Docker: Ensure your Docker Desktop is up-to-date by navigating to Settings > Software Update > Check for Updates.

  3. Disable x86_64/amd64 Emulation: In Docker Desktop, go to General settings and disable the x86_64/amd64 emulation using Rosetta.

For a detailed guide on fixing this issue, refer to this blog post.

Future Plans

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.