go-relayer
EIP2771-compatible transaction relayer library for Ethereum. The core of the library is bindings to a compatible forwarder contract.
Components:
cmd
: example CLI that uses examples/minimal_forwarder
examples
: example implementations of forwarder and recipient contracts, as well as Go bindings, required interface implementations, and unit tests
examples/gsn_forwarder
: an implementation using OpenGSN's forwarder contract.
examples/minimal_forwarder
: an implementation using OpenZeppelin's minimal forwarder.
examples/mock_recipient
: an implementation of a EIP2771-compatible recipient contract which can receive calls from a forwarder.
relayer
: functionality to call forwarder contracts
rpc
: rpc server for an end-user to submit txs to, accepts a *relayer.Relayer
Requirements
- go 1.19+
- abigen: can install with
bash scripts/install-abigen.sh
Usage
As an application
See cmd/main.go
for an example app using the examples/minimal_forwarder
package.
The app can be built using make build
.
To run it in dev mode (which auto-deploys a forwarder contract for you):
First, install and run ganache using:
NODE_OPTIONS="--max_old_space_size=8192" ganache --deterministic --accounts=50
Then:
$ ./bin/relayer --dev
2022-09-24T07:43:32.499-0400 INFO cmd cmd/main.go:131 starting relayer with ethereum endpoint http://localhost:8545 and chain ID 1337
2022-09-24T07:43:32.541-0400 INFO cmd cmd/main.go:207 deployed MinimalForwarder.sol to 0x8E7a8d3CAeEbbe9A92faC4db19424218aE6791a3
2022-09-24T07:43:32.542-0400 INFO rpc rpc/server.go:62 starting RPC server on http://localhost:7799
By default, the relayer server runs on port 7799
.
Implementing a custom forwarder
See the forwarder examples in examples/
for a full implementation.
There are three main components needed:
- Go bindings to the forwarder contract (generated by abigen)
- implementing the
Forwarder
interface below (by the forwarder contract)
- implementing the
ForwardRequest
interface below (by the request type contained in the contract)
// Forwarder must be implemented by a forwarder contract used by a *relayer.Relayer.
// These methods are wrappers around the methods auto-generated by abigen.
//
// See `examples/gsn_forwarder/i_forwarder_wrapped.go` or
// `examples/minimal_forwarder/i_minimal_forwarder_wrapped.go`for examples.
type Forwarder interface {
GetNonce(opts *bind.CallOpts, from ethcommon.Address) (*big.Int, error)
Verify(
opts *bind.CallOpts,
req ForwardRequest,
domainSeparator,
requestTypeHash [32]byte,
suffixData,
signature []byte,
) (bool, error)
Execute(
opts *bind.TransactOpts,
req ForwardRequest,
domainSeparator,
requestTypeHash [32]byte,
suffixData,
signature []byte,
) (*types.Transaction, error)
}
// ForwardRequest must be implemented by a request type used by a forwarder contract.
//
// See `examples/gsn_forwarder/request.go` or `examples/minimal_forwarder/request.go`
// for examples.
type ForwardRequest interface {
// FromSubmitTransactionRequest set the type underlying the ForwardRequest
// using a *SubmitTransactionRequest.
//
// Note: not all fields in the *SubmitTransactionRequest need be used depending
// on the implementation.
FromSubmitTransactionRequest(*SubmitTransactionRequest)
// Pack uses ABI encoding to pack the underlying ForwardRequest, appending
// optional `suffixData` to the end.
//
// See examples/gsn_forwarder/IForwarderForwardRequest.Pack() or
// examples/minimal_forwarder/IMinimalForwarderForwardRequest.Pack()
// for details.
Pack(suffixData []byte) ([]byte, error)
}
Additionally, to create a new *relayer.Relayer
, you need to implement a function that returns your ForwardRequest
. For example:
func NewIForwarderForwardRequest() common.ForwardRequest {
return &IForwarderForwardRequest{}
}
cfg := &relayer.Config{
// fields omitted
NewForwardRequestFunc: NewIForwarderForwardRequest,
}
_, _ = relayer.NewRelayer(cfg)
Interacting with the relayer
When running a relayer with an RPC server, it accepts transactions submission requests, returning a transaction hash if the transaction is successfully submitted:
func (s *RelayerService) SubmitTransaction(
_ *http.Request,
req *common.SubmitTransactionRequest,
resp *common.SubmitTransactionResponse,
) error
See go-relayer-client for client library and example usage.