Documentation ¶
Overview ¶
Package ethhelpers provides helper methods for the Go Ethereum client library.
Index ¶
- func BigIntAsInt64(v *big.Int) (int64, bool)
- func BigIntAsInt64OrDefaultValue(v *big.Int, defaultValue int64) int64
- func BigIntAsInt64OrZero(v *big.Int) int64
- func BigIntAsUint64(v *big.Int) (uint64, bool)
- func BigIntAsUint64OrDefaultValue(v *big.Int, defaultValue uint64) uint64
- func BigIntAsUint64OrZero(v *big.Int) uint64
- func BigIntAsUint64OrZeroIfNil(v *big.Int) (uint64, bool)
- func ContextWithClient(ctx context.Context, client Client) context.Context
- func ContextWithClientsFromRPCClient(ctx context.Context, rpcClient *rpc.Client) context.Context
- func ContextWithConfig(ctx context.Context, config Config) context.Context
- func ContextWithRPCClient(ctx context.Context, rpcClient *rpc.Client) context.Context
- func DefaultErrorHandlerWithMessages(msgHandler func(txHash common.Hash, msg string)) func(txHash common.Hash, err error) error
- func RPCClientFromContext(ctx context.Context) (*rpc.Client, bool)
- func RetryIfTemporaryError(unknownError func(context.Context, error) error) func(context.Context, func(context.Context) error) error
- func SubscribeFilterLogsWithHTTP(callerCtx context.Context, opts *HTTPSubscriberOptions) (ethereum.Subscription, error)
- func WaitForTransactionReceipt(ctx context.Context, options WaitForTransactionReceiptOptions) (<-chan ReceiptOrError, func())
- type BlockNumber
- type BlockNumberReader
- type BlockNumberTicker
- func NewPeriodicBlockNumberTicker(ctx context.Context, client BlockNumberReader, interval time.Duration) BlockNumberTicker
- func NewPeriodicBlockNumberTickerFromBlock(ctx context.Context, client BlockNumberReader, interval time.Duration, ...) BlockNumberTicker
- func NewPeriodicBlockNumberTickerFromBlockWithWindowSize(ctx context.Context, client BlockNumberReader, interval time.Duration, ...) BlockNumberTicker
- func NewPeriodicBlockNumberTickerWithWindowSize(ctx context.Context, client BlockNumberReader, interval time.Duration, ...) BlockNumberTicker
- type ChainReaderAndTransactionSender
- type Client
- type ClientCaller
- type Config
- type Contract
- type ContractContainer
- func (c *ContractContainer) Delete(key interface{})
- func (c *ContractContainer) Get(key interface{}) (Contract, bool)
- func (c *ContractContainer) GetOrNil(key interface{}) Contract
- func (c *ContractContainer) MustPut(key interface{}, value Contract)
- func (c *ContractContainer) Put(key interface{}, value Contract) bool
- type FilterLogsReader
- type HTTPSubscriberClient
- type HTTPSubscriberOptions
- type ReceiptOrError
- type WaitForTransactionReceiptOptions
- type WaitTransactionReceipts
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BigIntAsInt64OrZero ¶
func BigIntAsUint64OrZero ¶
func BigIntAsUint64OrZeroIfNil ¶ added in v0.1.1
func ContextWithClient ¶
ContextWithClient creates a new context which contains an ethhelpers.Client.
The context will return the client when calling ClientFromContext and other compatible methods.
Note that there can only be one client type in addition to the RPC client stored in the context, other client interface variants are stored using the same context key.
func ContextWithClientsFromRPCClient ¶
ContextWithClients creates a new context which contains both the RPC client and a newly created ethclient client.
The context will return the clients when calling ClientFromContext and RPCClientFromContext.
func ContextWithConfig ¶
ContextWithConfig creates a new context which contains an ethhelpers config.
The context will return the config when calling ConfigFromContext.
func ContextWithRPCClient ¶
ContextWithRPCClient creates a new context which contains an RPC client.
The context will return the client when calling RPCClientFromContext.
func DefaultErrorHandlerWithMessages ¶
func DefaultErrorHandlerWithMessages(msgHandler func(txHash common.Hash, msg string)) func(txHash common.Hash, err error) error
DefaultErrorHandlerWithLogger is used by e.g. WaitForTransactionReceipt to decide if the error is a temporary connection / receipt not found issue and it should retry.
This is a work-in-progress.
func RPCClientFromContext ¶
RPCClientFromContext retrieves an *rpc.Client from the context, if any.
func RetryIfTemporaryError ¶ added in v0.1.2
func SubscribeFilterLogsWithHTTP ¶ added in v0.1.1
func SubscribeFilterLogsWithHTTP(callerCtx context.Context, opts *HTTPSubscriberOptions) (ethereum.Subscription, error)
The context argument cancels the RPC request that sets up the subscription but has no effect on the subscription after Subscribe has returned.
Subscribers should be using the same underlying go-ethereum rpc client connection as the block number ticker to ensure there are no race-conditions.
To conform to the go-ethereum SubscriberFilterLogs api the ticker should only return current, and not historic, block numbers.
The current block number is requested before the subscription is returned.
func WaitForTransactionReceipt ¶
func WaitForTransactionReceipt(ctx context.Context, options WaitForTransactionReceiptOptions) (<-chan ReceiptOrError, func())
WaitForTransactionReceipt
The result channel always sends either the receipt or error, and does not close.
The caller must ensure that either the context is canceled or the returned cancel function is called.
Example ¶
ctx := context.Background() sim, closeSim := newExampleDefaultSimulatedBackend() defer closeSim() signedTx, err := sendTestTransaction(ctx, sim) if err != nil { log.Fatal(err) } resultChan, cancel := ethhelpers.WaitForTransactionReceipt(ctx, ethhelpers.WaitForTransactionReceiptOptions{ Client: sim.Backend, TxHash: signedTx.Hash(), ErrorHandler: ethhelpers.DefaultErrorHandlerWithMessages(func(txHash common.Hash, msg string) { }), }) defer cancel() sim.Backend.Commit() result := <-resultChan if result.Error != nil { log.Fatal(result.Error) } _ = result.Receipt
Output:
Types ¶
type BlockNumber ¶ added in v0.1.3
type BlockNumberReader ¶ added in v0.1.1
type BlockNumberTicker ¶ added in v0.1.1
type BlockNumberTicker interface { // Wait returns a channel that emits BlockNumber, and must be called before // each read. // // Reading from previously returned channels is not supported, and may in // some edge cases cause the new channel to skip a tick. // // To make sure the block number returned is up-to-date, call Wait rather // than reuse an unread channel. // // Truncated in the result is true if it was truncated by the window size. Wait() <-chan BlockNumber // Err returns a channel that emits errors that occur while waiting for block numbers. Err() <-chan error // CloneFromBlock creates a new ticker that starts from the given block number. CloneFromBlock(fromBlock uint64) BlockNumberTicker // Stop stops the ticker. Stop() }
BlockNumberTicker is a ticker that emits block numbers.
func NewPeriodicBlockNumberTicker ¶ added in v0.1.1
func NewPeriodicBlockNumberTicker(ctx context.Context, client BlockNumberReader, interval time.Duration) BlockNumberTicker
NewPeriodicBlockNumberTicker creates a new block number ticker that ticks at a fixed time interval, starting from the current block number.
func NewPeriodicBlockNumberTickerFromBlock ¶ added in v0.1.2
func NewPeriodicBlockNumberTickerFromBlock(ctx context.Context, client BlockNumberReader, interval time.Duration, fromBlock uint64) BlockNumberTicker
NewPeriodicBlockNumberTickerFromBlock creates a new block number ticker that ticks at a fixed time interval, starting from the given block number.
The ticker continues to make BlockNumber request calls after calling Wait if fromBlock was not reached. Therefor the ticker should be manually stopped and/or not used with fromBlock values that are not imminient.
Example ¶
package main import ( "context" "fmt" "math/big" "time" "github.com/ethereum/go-ethereum/core" "github.com/rakshasa/go-ethereum-helpers/ethhelpers" "github.com/rakshasa/go-ethereum-helpers/ethtesting" ethlog "github.com/ethereum/go-ethereum/log" ) func newExampleDefaultSimulatedBackend() (*ethtesting.SimulatedBackendWithAccounts, func()) { oldHandler := ethlog.Root().GetHandler() ethlog.Root().SetHandler(ethlog.DiscardHandler()) sim := ethtesting.NewSimulatedBackendWithAccounts( ethtesting.GenesisAccountWithPrivateKey{ PrivateKey: ethtesting.MockPrivateKey1, GenesisAccount: core.GenesisAccount{ Balance: big.NewInt(10_000_000_000_000_000), }, }, ethtesting.GenesisAccountWithPrivateKey{ PrivateKey: ethtesting.MockPrivateKey2, }, ) return sim, func() { sim.Backend.Close() ethlog.Root().SetHandler(oldHandler) } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() sim, closeSim := newExampleDefaultSimulatedBackend() defer closeSim() fromBlock := uint64(3) ticker := ethhelpers.NewPeriodicBlockNumberTickerFromBlock(ctx, ethtesting.NewSimulatedClient(sim.Backend), 200*time.Millisecond, fromBlock) defer ticker.Stop() go func() { time.Sleep(50 * time.Millisecond) for { select { case <-time.After(100 * time.Millisecond): sim.Backend.Commit() case <-ctx.Done(): } } }() var currentBlock uint64 for { select { case bn := <-ticker.Wait(): currentBlock = bn.BlockNumber fmt.Printf("currentBlock: %d\n", currentBlock) if currentBlock == 5 { time.Sleep(500 * time.Millisecond) } if currentBlock < 12 { continue } case err := <-ticker.Err(): fmt.Printf("err: %v\n", err) // if check_if_temporary_error { // ticker.Reset(currentBlock) // continue // } } return } }
Output: currentBlock: 3 currentBlock: 5 currentBlock: 10 currentBlock: 11 currentBlock: 13
func NewPeriodicBlockNumberTickerFromBlockWithWindowSize ¶ added in v0.1.3
func NewPeriodicBlockNumberTickerFromBlockWithWindowSize(ctx context.Context, client BlockNumberReader, interval time.Duration, fromBlock uint64, windowSize uint64) BlockNumberTicker
func NewPeriodicBlockNumberTickerWithWindowSize ¶ added in v0.1.3
func NewPeriodicBlockNumberTickerWithWindowSize(ctx context.Context, client BlockNumberReader, interval time.Duration, windowSize uint64) BlockNumberTicker
type ChainReaderAndTransactionSender ¶ added in v0.1.2
type ChainReaderAndTransactionSender interface { ethereum.TransactionSender ethereum.ChainReader }
type Client ¶
type Client interface { // ethereum.ChainReader BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) // ethereum.ChainStateReader BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) // ethereum.ChainSyncReader SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) // ethereum.ContractCaller CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) // ethereum.GasEstimator EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) // ethereum.GasPricer SuggestGasPrice(ctx context.Context) (*big.Int, error) // ethereum.LogFilterer FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) // ethereum.PendingContractCaller PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) // ethereum.PendingStateReader PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) PendingTransactionCount(ctx context.Context) (uint, error) // ethereum.TransactionReader TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) // ethereum.TransactionSender SendTransaction(ctx context.Context, tx *types.Transaction) error // BlockNumberReader BlockNumber(ctx context.Context) (uint64, error) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) ChainID(ctx context.Context) (*big.Int, error) // TODO: Remove Close? Close() FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) NetworkID(ctx context.Context) (*big.Int, error) PeerCount(ctx context.Context) (uint64, error) SuggestGasTipCap(ctx context.Context) (*big.Int, error) }
Client is an interface that holds the same methods as ethclient.Client.
func ClientFromContext ¶
ClientFromContext retrieves an interface implementing ethhelpers.Client from the context, if any.
func NewClientWithDefaultHandler ¶ added in v0.1.2
func NewClientWithDefaultHandler(defaultHandler func(context.Context, ClientCaller) error) Client
NewClientWithHandlers creates a new client with custom handlers.
The handlers cannot modify the content of the arguments or results, except for overriding the error returned.
Handlers should return nil if it has not changed the error.
type ClientCaller ¶ added in v0.1.2
type ClientCaller struct {
// contains filtered or unexported fields
}
func (ClientCaller) Args ¶ added in v0.1.2
func (c ClientCaller) Args() []interface{}
func (ClientCaller) Call ¶ added in v0.1.2
func (c ClientCaller) Call(ctx context.Context, client Client) error
func (ClientCaller) Name ¶ added in v0.1.2
func (c ClientCaller) Name() string
type Config ¶
type Config struct { Endpoint string ChainId *big.Int // TODO: ChainID Contracts ContractContainer }
type Contract ¶
func ContractFromConfigInContext ¶
ContractFromConfigInContext retrieves a Contract from the ContractContainer in the Config stored in the context, if any.
func ContractOrNilFromConfigInContext ¶
Same as ContractFromConfigInContext, except it returns a nil object if not present.
contractHelper, ok := ethhelpers.ContractOrNilFromConfigInContext(ctx, MyContractKey{}).(*MyContract) if !ok { return fmt.Errorf("missing my contract in context") } contract, err := contractHelper.ContractFromContext(ctx) if err != nil { return fmt.Errorf("failed to create my contract: %v", err) }
type ContractContainer ¶
type ContractContainer struct {
// contains filtered or unexported fields
}
ContractContainer uses a sync.Map to hold Contract instances.
It is recommended that the ContractContainer is created and populated at the same time as the config and client are created and added to the context.
When suitable, for the key it is recommended to use an empty instance of the bound contract.
For variants of generic contracts it is possible to use a struct type with a member variable to differentiate contract instances.
func NewContractContainer ¶
func NewContractContainer() ContractContainer
func (*ContractContainer) Delete ¶
func (c *ContractContainer) Delete(key interface{})
func (*ContractContainer) Get ¶
func (c *ContractContainer) Get(key interface{}) (Contract, bool)
func (*ContractContainer) GetOrNil ¶
func (c *ContractContainer) GetOrNil(key interface{}) Contract
func (*ContractContainer) MustPut ¶
func (c *ContractContainer) MustPut(key interface{}, value Contract)
func (*ContractContainer) Put ¶
func (c *ContractContainer) Put(key interface{}, value Contract) bool
type FilterLogsReader ¶ added in v0.1.1
type HTTPSubscriberClient ¶ added in v0.1.3
type HTTPSubscriberClient interface { BlockNumberReader FilterLogsReader }
type HTTPSubscriberOptions ¶ added in v0.1.3
type HTTPSubscriberOptions struct { Client HTTPSubscriberClient // CreateContext returns a context that is used for the subscription, or // context.Background() if nil. CreateContext func() (context.Context, context.CancelFunc) // CreateTicker is a function that creates a block number ticker. // // The context is canceled when a subscription is unsubscribed or encounters // an error, or if the context passed to the function creating the // HTTPSubscriber function is canceled before returning a valid // subscription. // // The method is called only once per subscription. CreateTicker func(ctx context.Context, fromBlock uint64) (BlockNumberTicker, error) FilterQuery ethereum.FilterQuery Logs chan<- types.Log }
type ReceiptOrError ¶
TODO: Move to types.
type WaitForTransactionReceiptOptions ¶
type WaitForTransactionReceiptOptions struct { // TODO: Add TransactionReaderFromContext, use it if Client is nil. Client ethereum.TransactionReader TxHash common.Hash // ErrorHandler is called when Client.TransactionReceipt returns a non-nil // error. The handler is not called if the main context was canceled. // // If the handler returns a non-nil error then the error is sent to the // result channel and no further attempts are made. ErrorHandler func(txHash common.Hash, err error) error }
type WaitTransactionReceipts ¶
type WaitTransactionReceipts struct {
// contains filtered or unexported fields
}
Example ¶
ctx := context.Background() sim, closeSim := newExampleDefaultSimulatedBackend() defer closeSim() waiter := ethhelpers.NewWaitTransactionReceipts(ctx, func(ctx context.Context, txHash common.Hash) (<-chan ethhelpers.ReceiptOrError, func()) { return ethhelpers.WaitForTransactionReceipt(ctx, ethhelpers.WaitForTransactionReceiptOptions{ Client: sim.Backend, TxHash: txHash, ErrorHandler: ethhelpers.DefaultErrorHandlerWithMessages(func(txHash common.Hash, msg string) { }), }) }) defer waiter.Stop() signedTx1, err := sendTestTransaction(ctx, sim) if err != nil { log.Fatal(err) } waiter.Add(signedTx1.Hash()) signedTx2, err := sendTestTransaction(ctx, sim) if err != nil { log.Fatal(err) } waiter.Add(signedTx2.Hash()) sim.Backend.Commit() result := <-waiter.Result() if result.Error != nil { log.Fatal(result.Error) } _ = result.Receipt // waiter.Result() will not return any more results.
Output:
func NewWaitTransactionReceipts ¶
func NewWaitTransactionReceipts(ctx context.Context, addFn func(context.Context, common.Hash) (<-chan ReceiptOrError, func())) *WaitTransactionReceipts
func (*WaitTransactionReceipts) Add ¶
func (w *WaitTransactionReceipts) Add(txHash common.Hash)
TODO: Add different different WatchFor* functions that allow us to use either pooling TransactionReceipt or a websocket subscription.
func (*WaitTransactionReceipts) Result ¶
func (w *WaitTransactionReceipts) Result() <-chan ReceiptOrError
Only read channel once.
func (*WaitTransactionReceipts) Stop ¶
func (w *WaitTransactionReceipts) Stop()