ethfinalizer

package
v1.41.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 26, 2026 License: LGPL-3.0, MIT Imports: 14 Imported by: 0

README

ethfinalizer

A wallet adapter for guaranteeing transaction inclusion on a specific chain.

This fixes "nonce too low" issues that can happen if reorgs occur or if you trust your node's reported nonces.

Usage

Create a mempool

For demonstration:

mempool := ethfinalizer.NewMemoryMempool[struct{}]()

Here struct{} can be any type for transaction metadata, data that gets persisted with the transaction, but not sent on chain.

For production, implement the Mempool interface and persist to a database instead.

Create a chain provider using ethkit

For EIP-1559 chains:

chain, err := ethfinalizer.NewEthkitChain(ethfinalizer.EthkitChainOptions{
    ChainID:     big.NewInt(1),
    IsEIP1559:   true,
    Provider:    provider,
    Monitor:     monitor,                // must be running
    GasGauge:    nil,                    // not used for EIP-1559 chains
    PriorityFee: big.NewInt(1000000000), // required for EIP-1559 chains
})

For non-EIP-1559 chains:

chain, err := ethfinalizer.NewEthkitChain(ethfinalizer.EthkitChainOptions{
    ChainID:       big.NewInt(56),
    IsEIP1559:     false,
    Provider:      provider,
    Monitor:       monitor,                          // must be running
    GasGauge:      gasGauge,                         // required for non-EIP-1559 chains
    GasGaugeSpeed: ethfinalizer.GasGaugeSpeedDefault // default = fast
    PriorityFee:   nil,                              // not used for non-EIP-1559 chains
})
Create a finalizer for a specific wallet on a specific chain
finalizer, err := ethfinalizer.NewFinalizer(ethfinalizer.FinalizerOptions[struct{}]{
    Wallet:       wallet,
    Chain:        chain,
    Mempool:      mempool,
    Logger:       nil,
    PollInterval: 5 * time.Second,  // period between chain state checks
    PollTimeout:  4 * time.Second,  // time limit for operations while checking chain state
    RetryDelay:   24 * time.Second, // minimum time to wait before retrying a transaction
    FeeMargin:    25,               // percentage added on top of the estimated gas price
    PriceBump:    15,               // go-ethereum requires at least 10% by default
})

The finalizer has a blocking run loop that must be called for it to work:

err := finalizer.Run(ctx)
Subscribe to mining and reorg events
for event := range finalizer.Subscribe(ctx) {
    if event.Added != nil {
        if event.Removed == nil {
            fmt.Println(
                "mined",
                event.Added.Hash(),
                event.Added.Metadata,
            )
        } else {
            fmt.Println(
                "reorged",
                event.Removed.Hash(),
                event.Removed.Metadata,
                "->",
                event.Added.Hash(),
                event.Added.Metadata,
            )
        }
    } else if event.Removed != nil {
        fmt.Println(
            "reorged",
            event.Removed.Hash(),
            event.Removed.Metadata,
        )
    }
}
Send a transaction
signed, err := finalizer.Send(ctx, unsigned, struct{}{})

The struct{}{} argument here is the transaction's metadata. The returned signed transaction may or may not be the final transaction included on chain.

Documentation

Overview

Package ethfinalizer implements a wallet adapter for guaranteeing transaction inclusion on a specific chain.

This fixes "nonce too low" issues that can happen if reorgs occur or if you trust your node's reported nonces.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Chain

type Chain interface {
	ChainID() *big.Int
	IsEIP1559() bool

	LatestNonce(ctx context.Context, address common.Address) (uint64, error)
	PendingNonce(ctx context.Context, address common.Address) (uint64, error)

	Send(ctx context.Context, transaction *types.Transaction) error

	GasPrice(ctx context.Context) (*big.Int, error)
	BaseFee(ctx context.Context) (*big.Int, error)
	PriorityFee(ctx context.Context) (*big.Int, error)

	Subscribe(ctx context.Context) (<-chan Diff, error)
}

Chain is a provider for a chain where transactions will be sent.

func NewEthkitChain

func NewEthkitChain(options EthkitChainOptions) (Chain, error)

NewEthkitChain creates a Chain using ethkit components.

type Diff

type Diff struct {
	Removed, Added map[common.Hash]struct{}
}

type EthkitChainOptions

type EthkitChainOptions struct {
	ChainID   *big.Int
	IsEIP1559 bool

	// Provider is an ethkit Provider, required.
	Provider *ethrpc.Provider
	// Monitor is a running ethkit Monitor, required.
	Monitor *ethmonitor.Monitor
	// GasGauge is a running ethkit GasGauge, required only for non-EIP-1559 chains.
	GasGauge *ethgas.GasGauge
	// GasGaugeSpeed defaults to GasGaugeSpeedDefault (GasGaugeSpeedFast), unused for EIP-1559 chains.
	GasGaugeSpeed GasGaugeSpeed
	// Logger is used to log chain behaviour, optional.
	Logger *slog.Logger

	PriorityFee *big.Int
}

func (EthkitChainOptions) IsValid

func (o EthkitChainOptions) IsValid() error

type Event

type Event[T any] struct {
	// Removed is the transaction that was reorged, nil if this was a mining event.
	Removed *Transaction[T]
	// Added is the transaction that was mined, may be nil if a reorg occurred and the reorged transaction was never replaced.
	Added *Transaction[T]
}

Event describes when a Transaction is mined or reorged.

  • Transaction mined: Removed is nil, Added is not nil.
  • Transaction reorged: Removed is not nil, Added may or may not be nil.

Type parameters:

  • T: transaction metadata type

type Finalizer

type Finalizer[T any] struct {
	FinalizerOptions[T]
	// contains filtered or unexported fields
}

Finalizer is a wallet adapter for guaranteeing transaction inclusion on a specific chain.

Type parameters:

  • T: transaction metadata type

func NewFinalizer

func NewFinalizer[T any](options FinalizerOptions[T]) (*Finalizer[T], error)

NewFinalizer creates a new Finalizer with FinalizerOptions which must be Run.

Type parameters:

  • T: transaction metadata type

func (*Finalizer[T]) IsRunning

func (f *Finalizer[T]) IsRunning() bool

func (*Finalizer[T]) Run

func (f *Finalizer[T]) Run(ctx context.Context) error

func (*Finalizer[T]) Send

func (f *Finalizer[T]) Send(ctx context.Context, transaction *types.Transaction, metadata T) (*types.Transaction, error)

Send sends an unsigned transaction on chain.

metadata is optional transaction data that is stored in the mempool and reappears in events. It is not sent on chain. A signed transaction is returned. This may or may not be the final transaction included on chain.

func (*Finalizer[T]) Subscribe

func (f *Finalizer[T]) Subscribe(ctx context.Context) <-chan Event[T]

Subscribe returns a channel for receiving transaction mining and reorg events.

The finalizer must be running to receive events.

type FinalizerOptions

type FinalizerOptions[T any] struct {
	// Wallet is the wallet to be managed by this finalizer, required.
	Wallet *ethwallet.Wallet
	// Chain is the provider for the chain where transactions will be sent, required.
	// See NewEthkitChain for an implementation using ethkit components.
	Chain Chain
	// Mempool stores transactions that this finalizer creates, required.
	// See NewMemoryMempool for a minimal in-memory implementation.
	Mempool Mempool[T]
	// Logger is used to log finalizer behaviour, optional.
	Logger *slog.Logger

	// PollInterval is the period between chain state checks.
	// Required, must be positive.
	PollInterval time.Duration
	// PollTimeout is the time limit for operations while checking chain state.
	// Required, must be positive.
	PollTimeout time.Duration
	// RetryDelay is the minimum time to wait before retrying a transaction.
	// Required, must be positive.
	RetryDelay time.Duration

	// FeeMargin is the percentage added on top of the estimated gas price.
	FeeMargin int
	// PriceBump is the percentage increase when replacing pending transactions.
	// Recommended to be at least 15, 10 is the default chosen by go-ethereum.
	PriceBump int

	// SubscriptionBuffer is the size of the buffer for transaction events.
	SubscriptionBuffer int
}

FinalizerOptions defines finalizer options.

Type parameters:

  • T: transaction metadata type

func (FinalizerOptions[T]) IsValid

func (o FinalizerOptions[T]) IsValid() error

type GasGaugeSpeed

type GasGaugeSpeed int
const (
	GasGaugeSpeedDefault GasGaugeSpeed = iota
	GasGaugeSpeedSlow
	GasGaugeSpeedStandard
	GasGaugeSpeedFast
	GasGaugeSpeedInstant
)

type Mempool

type Mempool[T any] interface {
	// Nonce is the next available nonce for the wallet on the chain.
	Nonce(ctx context.Context) (uint64, error)

	// Commit persists the signed transaction with its metadata in the store.
	// The transaction must be persisted with a timestamp of the current time.
	// If the transaction already exists in the mempool, the timestamp must be updated.
	Commit(ctx context.Context, transaction *types.Transaction, metadata T) error

	// Transactions returns the transactions for the specified hashes which are signed by this specific wallet for this specific chain.
	Transactions(ctx context.Context, hashes map[common.Hash]struct{}) (map[common.Hash]*Transaction[T], error)

	// PriciestTransactions returns, by nonce, the most expensive transactions signed by this specific wallet for this specific chain, with a minimum nonce and a latest timestamp.
	PriciestTransactions(ctx context.Context, fromNonce uint64, before time.Time) (map[uint64]*Transaction[T], error)
}

Mempool is a complete local store of all transactions signed by a specific wallet for a specific chain that have been sent, but not necessarily included on chain.

func NewMemoryMempool

func NewMemoryMempool[T any]() Mempool[T]

NewMemoryMempool creates a minimal in-memory Mempool.

type Transaction

type Transaction[T any] struct {
	*types.Transaction

	Metadata T
}

Transaction is a transaction with metadata of type T.

Type parameters:

  • T: metadata type

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL