seth

package module
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2024 License: MIT Imports: 38 Imported by: 19

README

Seth

Reliable and debug-friendly Ethereum client

Decoding tests Tracing tests API tests CLI tests Integration tests (testnets)

Goals

  • Be a thin, debuggable and battle tested wrapper on top of go-ethereum
  • Decode all transaction inputs/outputs/logs for all ABIs you are working with, automatically
  • Simple synchronous API
  • Do not handle nonces on the client side, trust the server
  • Do not wrap bind generated contracts, small set of additional debug API
  • Resilient: should execute transactions even if there is a gas spike or an RPC outage (failover)
  • Well tested: should provide a suite of e2e tests that can be run on testnets to check integration

Examples

Check examples folder

Lib is providing a small amount of helpers for decoding handling that you can use with vanilla go-ethereum generated wrappers

// Decode waits for transaction and decode all the data/errors
Decode(tx *types.Transaction, txErr error) (*DecodedTransaction, error)

// NewTXOpts returns a new sequential transaction options wrapper,
// sets opts.GasPrice and opts.GasLimit from seth.toml or override with options
NewTXOpts(o ...TransactOpt) *bind.TransactOpts

// NewCallOpts returns a new call options wrapper
NewCallOpts(o ...CallOpt) *bind.CallOpts

By default, we are using the root key 0, but you can also use keys from keyfile.toml

// NewCallKeyOpts returns a new sequential call options wrapper from the key N
NewCallKeyOpts(keyNum int, o ...CallOpt) *bind.CallOpts

// NewTXKeyOpts returns a new transaction options wrapper called from the key N
NewTXKeyOpts(keyNum int, o ...TransactOpt) *bind.TransactOpts

Start Geth in a separate terminal, then run the examples

make GethSync
cd examples
go test -v

Setup

We are using nix

Enter the shell

nix develop

Building contracts

We have go-ethereum and foundry tools inside nix shell

make build

Testing

To run tests on a local network, first start it

make AnvilSync

Or use latest Geth

make GethSync

You can use default hardhat key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 to run tests

Run the decode tests

make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test
make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test

Check other params in seth.toml, select any network and use your key for testnets

User facing API tests are here

make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_api
make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_api

CLI tests

make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_cli
make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_cli

Tracing tests

make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_trace
make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_trace

Config

env vars

Some crucial data is stored in env vars, create .envrc and use source .envrc, or use direnv

export SETH_LOG_LEVEL=info # global logger level
export SETH_CONFIG_PATH=seth.toml # path to the toml config
export SETH_KEYFILE_PATH=keyfile.toml # keyfile path for using multiple keys
export NETWORK=Geth # selected network
export ROOT_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 # root private key

alias seth="go run cmd/seth/seth.go" # useful alias for keyfile CLI

If SETH_KEYFILE_PATH is not set then client will create 60 ephemeral keys and won't return any funds

Use SETH_KEYFILE_PATH for testnets/mainnets and ephemeral mode only when testing against simulated network

seth.toml

Set up your ABI directory (relative to seth.toml)

abi_dir = "contracts/abi"

Setup your BIN directory (relative to seth.toml)

bin_dir = "contracts/bin"

You can enable auto-tracing for all transactions, which means that every time you use Decode() we will not only decode the transaction but also trace all calls made within the transaction, together with all inputs, outputs, logs and events

tracing_enabled = true

Additionally you can also enable saving all tracing information to JSON files with:

trace_to_json = true

If you want to check if the RPC is healthy on start, you can enable it with:

check_rpc_health_on_start = false

It will execute a simple check of transfering 10k wei from root key to root key and check if the transaction was successful.

You can add more networks like this:

[[Networks]]
name = "Fuji"
chain_id = "43113"
transaction_timeout = "30s"
# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions)
# gas_limit = 9_000_000
# gas limit for sending funds
transfer_gas_fee = 21_000
# legacy transactions
gas_price = 1_000_000_000
# EIP-1559 transactions
eip_1559_dynamic_fees = true
gas_fee_cap = 25_000_000_000
gas_tip_cap = 1_800_000_000
urls_secret = ["..."]
# if set to true we will check if address has a pending nonce (transaction) and panic if it does
pending_nonce_protection_enabled = false
# if set to true we will dynamically estimate gas for every transaction (explained in more detail below)
gas_price_estimation_enabled = true
# how many last blocks to use, when estimating gas for a transaction
gas_price_estimation_blocks = 1000
# priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor and buffer will be used for gas estimation) [default: "standard"]
gas_price_estimation_tx_priority = "slow"

If you want to save addresses of deployed contracts, you can enable it with:

save_deployed_contracts_map = true

If you want to re-use previously deployed contracts you can indicate file name in seth.toml:

contract_map_file = "deployed_contracts_mumbai.toml"

Both features only work for live networks. Otherwise, they are ignored, and nothing is saved/read from for simulated networks.

CLI

Multiple keys manipulation (keyfile.toml)

To use multiple keys in your tests you can create a keyfile.toml using CLI

Set up the alias, see .envrc configuration above

alias seth="go run cmd/seth/seth.go"

Create a new keyfile with 10 new accounts funded from the root key (KEYS env var)

seth -n Geth keys split -a 10

Run the tests, then return funds back, when needed

seth -n Geth keys return

Update the balances

seth -n Geth keys update

Remove the keyfile

seth -n Geth keys remove
Manual gas price estimation

In order to adjust gas price for a transaction, you can use seth gas command

seth -n Fuji gas -b 10000 -tp 0.99

This will analyze last 10k blocks and give you 25/50/75/99th/Max percentiles for base fees and tip fees

-tp 0.99 requests the 99th tip percentile across all the transaction in one block and calculates 25/50/75/99th/Max across all blocks

Bulk tracing

You can trace multiple transactions at once using seth trace command. Example:

SETH_CONFIG_PATH=seth.toml go run cmd/seth/seth.go -n=Geth trace -f reverted_transactions.json

You need to pass a file with a list of transaction hashes to trace. The file should be a JSON array of transaction hashes, like this:

[
  "0x...",
  "0x...",
  "0x...",
  ...
]

(Note that currently Seth automatically creates reverted_transactions_<network>_<date>.json with all reverted transactions, so you can use this file as input for the trace command.)

Features

  • Decode named inputs
  • Decode named outputs
  • Decode anonymous outputs
  • Decode logs
  • Decode indexed logs
  • Decode old string reverts
  • Decode new typed reverts
  • EIP-1559 support
  • Multi-keys client support
  • CLI to manipulate test keys
  • Simple manual gas price estimation
  • Tuned gas prices for testnets (optimized for fast transaction times)
  • Fail over client logic
  • Decode collided event hashes
  • Tracing support (4byte)
  • Tracing support (callTracer)
  • Tracing support (prestate)
  • Tracing decoding
  • Tracing tests
  • More tests for corner cases of decoding/tracing
  • Saving of deployed contracts mapping (address -> ABI_name) for live networks
  • Reading of deployed contracts mappings for live networks
  • Automatic gas estimator (experimental)
  • Check if address has a pending nonce (transaction) and panic if it does

You can read more about how ABI finding and contract map works here and about contract store here here.

Autmoatic gas estimator

This section explains how to configure and understand the automatic gas estimator, which is crucial for executing transactions on Ethereum-based networks. Here’s what you need to know:

Configuration Requirements

Before using the automatic gas estimator, it's essential to set the default gas-related parameters for your network:

  • Non-EIP-1559 Networks: Set the gas_price to define the cost per unit of gas if your network doesn't support EIP-1559.
  • EIP-1559 Networks: If your network supports EIP-1559, set the following:
    • eip_1559_dynamic_fees: Enables dynamic fee structure.
    • gas_fee_cap: The maximum fee you're willing to pay per gas.
    • gas_tip_cap: An optional tip to prioritize your transaction within a block (although if it's set to 0 there's a high chance your transaction will take longer to execute as it will be less attractive to miners, so do set it).

These settings act as a fallback if the gas estimation fails. Additionally, always specify transfer_gas_fee for the fee associated with token transfers.

If you do not know if your network supports EIP-1559, but you want to give it a try it's recommended that you also set gas_price as a fallback. When we try to use EIP-1559 during gas price estimation, but it fails, we will fallback to using non-EIP-1559 logic. If that one fails as well, we will use hardcoded gas_price value.

How Gas Estimation Works

Gas estimation varies based on whether the network is a private Ethereum Network or a live network.

  • Private Ethereum Networks: no estimation is needed. We always use hardcoded values.

For real networks, the estimation process differs for legacy transactions and those compliant with EIP-1559:

Legacy Transactions
  1. Initial Price: Query the network node for the current suggested gas price.
  2. Priority Adjustment: Modify the initial price based on gas_price_estimation_tx_priority. Higher priority increases the price to ensure faster inclusion in a block.
  3. Congestion Analysis: Examine the last X blocks (as specified by gas_price_estimation_blocks) to determine network congestion, calculating the usage rate of gas in each block and giving recent blocks more weight.
  4. Buffering: Add a buffer to the adjusted gas price to increase transaction reliability during high congestion.
EIP-1559 Transactions
  1. Tip Fee Query: Ask the node for the current recommended tip fee.
  2. Fee History Analysis: Gather the base fee and tip history from recent blocks to establish a fee baseline.
  3. Fee Selection: Use the greater of the node's suggested tip or the historical average tip for upcoming calculations.
  4. Priority and Adjustment: Increase the base and tip fees based on transaction priority (gas_price_estimation_tx_priority), which influences how much you are willing to spend to expedite your transaction.
  5. Final Fee Calculation: Sum the base fee and adjusted tip to set the gas_fee_cap.
  6. Congestion Buffer: Similar to legacy transactions, analyze congestion and apply a buffer to both the fee cap and the tip to secure transaction inclusion.

Understanding and setting these parameters correctly ensures that your transactions are processed efficiently and cost-effectively on the network.

Finally, gas_price_estimation_tx_priority is also used, when deciding, which percentile to use for base fee and tip for historical fee data. Here's how that looks:

		case Priority_Fast:
			baseFee = stats.GasPrice.Perc99
			historicalGasTipCap = stats.TipCap.Perc99
		case Priority_Standard:
			baseFee = stats.GasPrice.Perc50
			historicalGasTipCap = stats.TipCap.Perc50
		case Priority_Slow:
			baseFee = stats.GasPrice.Perc25
			historicalGasTipCap = stats.TipCap.Perc25
Adjustment factor

All values are multiplied by the adjustment factor, which is calculated based on gas_price_estimation_tx_priority:

	case Priority_Fast:
		return 1.2
	case Priority_Standard:
		return 1.0
	case Priority_Slow:
		return 0.8
Buffer precents

We further adjust the gas price by adding a buffer to it, based on congestion rate:

	case Congestion_Low:
		return 0.10, nil
	case Congestion_Medium:
		return 0.20, nil
	case Congestion_High:
		return 0.30, nil
	case Congestion_Degen:
		return 0.40, nil

We cache block header data in an in-memory cache, so we don't have to fetch it every time we estimate gas. The cache has capacity equal to gas_price_estimation_blocks and every time we add a new element, we remove one that is least frequently used and oldest (with block number being a constant and chain always moving forward it makes no sense to keep old blocks).

For both transaction types if any of the steps fails, we fallback to hardcoded values.

Experimental features

In order to enable an experimental feature you need to pass it's name in config. It's a global config, you cannot enable it per-network. Example:

# other settings before...
tracing_enabled = false
trace_to_json = false
experiments_enabled = ["slow_funds_return", "eip_1559_fee_equalizer"]

Here's what they do:

  • slow_funds_return will work only in core and when enabled it changes tx priority to slow and increases transaction timeout to 30 minutes.
  • eip_1559_fee_equalizer in case of EIP-1559 transactions if it detects that historical base fee and suggested/historical tip are more than 3 orders of magnitude apart, it will use the higher value for both (this helps in cases where base fee is almost 0 and transaction is never processed).

Documentation

Index

Constants

View Source
const (
	ErrEmptyConfigPath     = "toml config path is empty, set SETH_CONFIG_PATH"
	ErrCreateABIStore      = "failed to create ABI store"
	ErrReadingKeys         = "failed to read keys"
	ErrCreateNonceManager  = "failed to create nonce manager"
	ErrCreateTracer        = "failed to create tracer"
	ErrReadContractMap     = "failed to read deployed contract map"
	ErrNoKeyLoaded         = "failed to load private key"
	ErrRpcHealtCheckFailed = "RPC health check failed ¯\\_(ツ)_/¯"

	ContractMapFilePattern          = "deployed_contracts_%s_%s.toml"
	RevertedTransactionsFilePattern = "reverted_transactions_%s_%s.json"
)
View Source
const (
	ErrReadSethConfig         = "failed to read TOML config for seth"
	ErrReadKeyFileConfig      = "failed to read TOML keyfile config"
	ErrUnmarshalSethConfig    = "failed to unmarshal TOML config for seth"
	ErrUnmarshalKeyFileConfig = "failed to unmarshal TOML keyfile config for seth"
	ErrEmptyNetwork           = "no network was selected, set NETWORK=..., check TOML config for available networks and set the env var"
	ErrEmptyRootPrivateKey    = "no private keys were set, set ROOT_PRIVATE_KEY=..."

	GETH  = "Geth"
	ANVIL = "Anvil"
)
View Source
const (
	Experiment_SlowFundsReturn    = "slow_funds_return"
	Experiment_Eip1559FeeEqualier = "eip_1559_fee_equalizer"
)
View Source
const (
	ErrOpenABIFile = "failed to open ABI file"
	ErrParseABI    = "failed to parse ABI file"
	ErrOpenBINFile = "failed to open BIN file"
)
View Source
const (
	ErrDecodeInput          = "failed to decode transaction input"
	ErrDecodeOutput         = "failed to decode transaction output"
	ErrDecodeLog            = "failed to decode log"
	ErrDecodedLogNonIndexed = "failed to decode non-indexed log data"
	ErrDecodeILogIndexed    = "failed to decode indexed log data"
	ErrNoTxData             = "no tx data or it's less than 4 bytes"
	ErrRPCJSONCastError     = "failed to cast CallMsg error as rpc.DataError"

	WarnNoContractStore = "ContractStore is nil, use seth.NewContractStore(...) to decode transactions"
)
View Source
const (
	Priority_Degen    = "degen" //this is undocumented option, which we left for cases, when we need to set the highest gas price
	Priority_Fast     = "fast"
	Priority_Standard = "standard"
	Priority_Slow     = "slow"

	Congestion_Low    = "low"
	Congestion_Medium = "medium"
	Congestion_High   = "high"
	Congestion_Degen  = "degen"
)
View Source
const (
	// each block has the same weight in the computation
	CongestionStrategy_Simple = "simple"
	// newer blocks have more weight in the computation
	CongestionStrategy_NewestFirst = "newest_first"
)
View Source
const (
	ErrKeySyncTimeout = "key sync timeout, consider increasing key_sync_timeout in seth.toml, or increasing the number of keys"
	ErrKeySync        = "failed to sync the key"
	ErrNonce          = "failed to get nonce"
)
View Source
const (
	ErrNoTrace                   = "no trace found"
	ErrNoABIMethod               = "no ABI method found"
	ErrNoAbiFound                = "no ABI found in Contract Store"
	ErrNoFourByteFound           = "no method signatures found in tracing data"
	ErrInvalidMethodSignature    = "no method signature found or it's not 4 bytes long"
	ErrSignatureNotFoundIn4Bytes = "signature not found in 4 bytes trace"
	WrnMissingCallTrace          = "" /* 135-byte string literal not displayed */

	FAILED_TO_DECODE = "failed to decode"
	UNKNOWN          = "unknown"
	NO_DATA          = "no data"

	CommentMissingABI = "Call not decoded due to missing ABI instance"
)
View Source
const (
	ErrEmptyKeyFile               = "keyfile is empty"
	ErrInsufficientRootKeyBalance = "insufficient root key balance: %s"
)
View Source
const (
	ErrRPCConnectionRefused = "connection refused"
)
View Source
const (
	ErrRetryTimeout = "retry timeout"
)
View Source
const (
	LogLevelEnvVar = "SETH_LOG_LEVEL"
)

Variables

View Source
var (
	// DefaultEphemeralAddresses is amount of addresses created in ephemeral client mode
	DefaultEphemeralAddresses int64 = 60
)

Functions

func CreateOrAppendToJsonArray added in v0.1.2

func CreateOrAppendToJsonArray(filePath string, newItem any) error

CreateOrAppendToJsonArray appends to a JSON array in a file or creates a new JSON array if the file is empty or doesn't exist

func EtherToWei added in v0.1.3

func EtherToWei(eth *big.Float) *big.Int

EtherToWei converts an ETH float amount to wei

func LoadDeployedContracts

func LoadDeployedContracts(filename string) (map[string]string, error)

func NewAddress

func NewAddress() (string, string, error)

NewAddress creates a new address

func NewEphemeralKeys

func NewEphemeralKeys(addrs int64) ([]string, error)

NewEphemeralKeys creates a new ephemeral keyfile, can be used for simulated networks

func OpenJsonFileAsStruct added in v0.1.2

func OpenJsonFileAsStruct(path string, v any) error

func ReturnFunds

func ReturnFunds(c *Client, toAddr string) error

ReturnFunds returns funds to the root key from all the test keys in some "keyfile.toml"

func SaveDeployedContract

func SaveDeployedContract(filename, contractName, address string) error

func UpdateAndSplitFunds

func UpdateAndSplitFunds(c *Client, opts *FundKeyFileCmdOpts) error

UpdateAndSplitFunds splits funds from the root key into equal parts

func UpdateKeyFileBalances

func UpdateKeyFileBalances(c *Client) error

UpdateKeyFileBalances updates file balances

func WeiToEther added in v0.1.3

func WeiToEther(wei *big.Int) *big.Float

WeiToEther converts a wei amount to eth float

Types

type ABIFinder

type ABIFinder struct {
	ContractMap   ContractMap
	ContractStore *ContractStore
}

func NewABIFinder

func NewABIFinder(contractMap ContractMap, contractStore *ContractStore) ABIFinder

func (*ABIFinder) FindABIByMethod

func (a *ABIFinder) FindABIByMethod(address string, signature []byte) (ABIFinderResult, error)

FindABIByMethod finds the ABI method and instance for the given contract address and signature If the contract address is known, it will use the ABI instance that is known to be at the address. If the contract address is not known, it will iterate over all known ABIs and check if any of them has a method with the given signature. If there are duplicates we will use the first ABI that matched.

type ABIFinderResult

type ABIFinderResult struct {
	ABI            abi.ABI
	Method         *abi.Method
	DuplicateCount int
	// contains filtered or unexported fields
}

func (*ABIFinderResult) ContractName

func (a *ABIFinderResult) ContractName() string

type ABIStore

type ABIStore map[string]abi.ABI

type Call

type Call struct {
	From    string     `json:"from"`
	Gas     string     `json:"gas"`
	GasUsed string     `json:"gasUsed"`
	Input   string     `json:"input"`
	Logs    []TraceLog `json:"logs"`
	Output  string     `json:"output"`
	To      string     `json:"to"`
	Type    string     `json:"type"`
	Value   string     `json:"value"`
}

type CallOpt

type CallOpt func(o *bind.CallOpts)

CallOpt is a functional option for bind.CallOpts

func WithBlockNumber

func WithBlockNumber(bn uint64) CallOpt

WithBlockNumber sets blockNumber option for bind.CallOpts

func WithPending

func WithPending(pending bool) CallOpt

WithPending sets pending option for bind.CallOpts

type Client

type Client struct {
	Cfg                      *Config
	Client                   *ethclient.Client
	Addresses                []common.Address
	PrivateKeys              []*ecdsa.PrivateKey
	ChainID                  int64
	URL                      string
	Context                  context.Context
	CancelFunc               context.CancelFunc
	Errors                   []error
	ContractStore            *ContractStore
	NonceManager             *NonceManager
	Tracer                   *Tracer
	TraceReverted            bool
	ContractAddressToNameMap ContractMap
	ABIFinder                *ABIFinder
	HeaderCache              *LFUHeaderCache
}

Client is a vanilla go-ethereum client with enhanced debug logging

func NewClient

func NewClient() (*Client, error)

NewClient creates a new raw seth client with all deps setup from env vars

func NewClientRaw

func NewClientRaw(
	cfg *Config,
	addrs []common.Address,
	pkeys []*ecdsa.PrivateKey,
	opts ...ClientOpt,
) (*Client, error)

NewClientRaw creates a new raw seth client without dependencies

func NewClientWithConfig

func NewClientWithConfig(cfg *Config) (*Client, error)

NewClientWithConfig creates a new seth client with all deps setup from config

func (*Client) AnySyncedKey

func (m *Client) AnySyncedKey() int

AnySyncedKey returns the first synced key

func (*Client) CalculateGasEstimations added in v0.1.3

func (m *Client) CalculateGasEstimations(request GasEstimationRequest) GasEstimations

CalculateGasEstimations calculates gas estimations (price, tip/cap) or uses hardcoded values if estimation is disabled, estimation errors or network is a simulated one.

func (*Client) CalculateNetworkCongestionMetric added in v0.1.3

func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy string) (float64, error)

CalculateNetworkCongestionMetric calculates a simple congestion metric based on the last N blocks according to selected strategy.

func (*Client) CalculateSubKeyFunding

func (m *Client) CalculateSubKeyFunding(addrs int64) (*FundingDetails, error)

CalculateSubKeyFunding calculates all required params to split funds from the root key to N test keys

func (*Client) CallMsgFromTx

func (m *Client) CallMsgFromTx(tx *types.Transaction) ethereum.CallMsg

CallMsgFromTx creates ethereum.CallMsg from tx, used in simulated calls

func (*Client) CreateOrUnmarshalKeyFile

func (m *Client) CreateOrUnmarshalKeyFile(opts *FundKeyFileCmdOpts) (*KeyFile, error)

func (*Client) Decode

func (m *Client) Decode(tx *types.Transaction, txErr error) (*DecodedTransaction, error)

Decode waits for transaction to be minted and decodes basic info of the first method that was called by the transaction (such as: method name, inputs and any logs/events emitted by the transaction. Additionally if `tracing_enabled` flag is set in Client config it will trace all other calls that were made during the transaction execution and print them to console using logger. If transaction was reverted, then tracing won't take place, but the error return will contain revert reason (if we were able to get it from the receipt).

func (*Client) DecodeCustomABIErr

func (m *Client) DecodeCustomABIErr(txErr error) (string, error)

DecodeCustomABIErr decodes typed Solidity errors

func (*Client) DeployContract

func (m *Client) DeployContract(auth *bind.TransactOpts, name string, abi abi.ABI, bytecode []byte, params ...interface{}) (DeploymentData, error)

DeployContract deploys contract using ABI and bytecode passed to it, waits for transaction to be minted and contract really available at the address, so that when the method returns it's safe to interact with it. It also saves the contract address and ABI name to the contract map, so that we can use that, when tracing transactions. It is suggested to use name identical to the name of the contract Solidity file.

func (*Client) DeployContractFromContractStore

func (m *Client) DeployContractFromContractStore(auth *bind.TransactOpts, name string, params ...interface{}) (DeploymentData, error)

DeployContractFromContractStore deploys contract from Seth's Contract Store, waits for transaction to be minted and contract really available at the address, so that when the method returns it's safe to interact with it. It also saves the contract address and ABI name to the contract map, so that we can use that, when tracing transactions. Name by which you refer the contract should be the same as the name of ABI file (you can omit the .abi suffix).

func (*Client) DeployDebugContract

func (m *Client) DeployDebugContract(subDbgAddr common.Address) (*network_debug_contract.NetworkDebugContract, common.Address, error)

func (*Client) GetSuggestedEIP1559Fees added in v0.1.3

func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (maxFeeCap *big.Int, adjustedTipCap *big.Int, err error)

GetSuggestedEIP1559Fees returns suggested tip/fee cap calculated based on historical data, current congestion, and priority.

func (*Client) GetSuggestedLegacyFees added in v0.1.3

func (m *Client) GetSuggestedLegacyFees(ctx context.Context, priority string) (adjustedGasPrice *big.Int, err error)

GetSuggestedLegacyFees calculates the suggested gas price based on historical data, current congestion, and priority.

func (*Client) HistoricalFeeData added in v0.1.3

func (m *Client) HistoricalFeeData(priority string) (baseFee float64, historicalGasTipCap float64, err error)

func (*Client) NewCallKeyOpts

func (m *Client) NewCallKeyOpts(keyNum int, o ...CallOpt) *bind.CallOpts

NewCallKeyOpts returns a new sequential call options wrapper from the key N

func (*Client) NewCallOpts

func (m *Client) NewCallOpts(o ...CallOpt) *bind.CallOpts

NewCallOpts returns a new sequential call options wrapper

func (*Client) NewTXKeyOpts

func (m *Client) NewTXKeyOpts(keyNum int, o ...TransactOpt) *bind.TransactOpts

NewTXKeyOpts returns a new transaction options wrapper, sets opts.GasPrice and opts.GasLimit from seth.toml or override with options

func (*Client) NewTXOpts

func (m *Client) NewTXOpts(o ...TransactOpt) *bind.TransactOpts

NewTXOpts returns a new transaction options wrapper, Sets gas price/fee tip/cap and gas limit either based on TOML config or estimations.

func (*Client) RetryTxAndDecode

func (m *Client) RetryTxAndDecode(f func() (*types.Transaction, error)) (*DecodedTransaction, error)

RetryTxAndDecode executes transaction several times, retries if connection is lost and decodes all the data

func (*Client) SaveDecodedCallsAsJson

func (m *Client) SaveDecodedCallsAsJson(dirname string) error

func (*Client) TransferETHFromKey

func (m *Client) TransferETHFromKey(ctx context.Context, fromKeyNum int, to string, value *big.Int) error

func (*Client) WaitMined

WaitMined the same as bind.WaitMined, awaits transaction receipt until timeout

type ClientOpt

type ClientOpt func(c *Client)

ClientOpt is a client functional option

func WithABIFinder

func WithABIFinder(abiFinder *ABIFinder) ClientOpt

WithABIFinder ABIFinder functional option

func WithContractMap

func WithContractMap(contractAddressToNameMap map[string]string) ClientOpt

WithContractMap contractAddressToNameMap functional option

func WithContractStore

func WithContractStore(as *ContractStore) ClientOpt

WithContractStore ContractStore functional option

func WithNonceManager

func WithNonceManager(nm *NonceManager) ClientOpt

WithNonceManager NonceManager functional option

func WithTracer

func WithTracer(t *Tracer) ClientOpt

WithTracer Tracer functional option

type CommonData

type CommonData struct {
	Signature string                 `json:"signature"`
	Method    string                 `json:"method"`
	Input     map[string]interface{} `json:"input,omitempty"`
	Output    map[string]interface{} `json:"output,omitempty"`
}

type Config

type Config struct {
	// internal fields
	RevertedTransactionsFile string

	KeyFilePath string

	// external fields
	EphemeralAddrs *int64 `toml:"ephemeral_addresses_number"`

	ABIDir                        string           `toml:"abi_dir"`
	BINDir                        string           `toml:"bin_dir"`
	ContractMapFile               string           `toml:"contract_map_file"`
	SaveDeployedContractsMap      bool             `toml:"save_deployed_contracts_map"`
	Network                       *Network         `toml:"network"`
	Networks                      []*Network       `toml:"networks"`
	NonceManager                  *NonceManagerCfg `toml:"nonce_manager"`
	TracingEnabled                bool             `toml:"tracing_enabled"`
	TraceToJson                   bool             `toml:"trace_to_json"`
	PendingNonceProtectionEnabled bool             `toml:"pending_nonce_protection_enabled"`
	ConfigDir                     string           `toml:"abs_path"`
	ExperimentsEnabled            []string         `toml:"experiments_enabled"`
	CheckRpcHealthOnStart         bool             `toml:"check_rpc_health_on_start"`
	// contains filtered or unexported fields
}

func ReadConfig

func ReadConfig() (*Config, error)

ReadConfig reads the TOML config file from location specified by env var "SETH_CONFIG_PATH" and returns a Config struct

func (*Config) GenerateContractMapFileName

func (c *Config) GenerateContractMapFileName() string

GenerateContractMapFileName generates a file name for the contract map

func (*Config) IsExperimentEnabled added in v0.1.3

func (c *Config) IsExperimentEnabled(experiment string) bool

func (*Config) IsSimulatedNetwork

func (c *Config) IsSimulatedNetwork() bool

IsSimulatedNetwork returns true if the network is simulated (i.e. Geth or Anvil)

func (*Config) ParseKeys

func (c *Config) ParseKeys() ([]common.Address, []*ecdsa.PrivateKey, error)

ParseKeys parses private keys from the config

func (*Config) ShoulSaveDeployedContractMap added in v0.1.1

func (c *Config) ShoulSaveDeployedContractMap() bool

ShoulSaveDeployedContractMap returns true if the contract map should be saved (i.e. not a simulated network and functionality is enabled)

type ContractMap

type ContractMap map[string]string

func (ContractMap) AddContract

func (c ContractMap) AddContract(addr, name string)

func (ContractMap) GetContractAddress

func (c ContractMap) GetContractAddress(addr string) string

func (ContractMap) GetContractName

func (c ContractMap) GetContractName(addr string) string

func (ContractMap) IsKnownAddress

func (c ContractMap) IsKnownAddress(addr string) bool

type ContractStore

type ContractStore struct {
	ABIs ABIStore
	BINs map[string][]byte
}

ContractStore contains all ABIs that are used in decoding. It might also contain contract bytecode for deployment

func NewContractStore

func NewContractStore(abiPath, binPath string) (*ContractStore, error)

NewContractStore creates a new Contract store

func (*ContractStore) AddABI

func (c *ContractStore) AddABI(name string, abi abi.ABI)

func (*ContractStore) AddBIN

func (c *ContractStore) AddBIN(name string, bin []byte)

func (*ContractStore) GetABI

func (c *ContractStore) GetABI(name string) (*abi.ABI, bool)

func (*ContractStore) GetBIN

func (c *ContractStore) GetBIN(name string) ([]byte, bool)

type DecodableLog

type DecodableLog interface {
	GetTopics() []common.Hash
	GetData() []byte
}

type DecodedCall

type DecodedCall struct {
	CommonData
	FromAddress string             `json:"from_address,omitempty"`
	ToAddress   string             `json:"to_address,omitempty"`
	From        string             `json:"from,omitempty"`
	To          string             `json:"to,omitempty"`
	Events      []DecodedCommonLog `json:"events,omitempty"`
	Comment     string             `json:"comment,omitempty"`
	Value       int64              `json:"value,omitempty"`
	GasLimit    uint64             `json:"gas_limit,omitempty"`
	GasUsed     uint64             `json:"gas_used,omitempty"`
}

DecodedCall decoded call

type DecodedCommonLog

type DecodedCommonLog struct {
	Signature string                 `json:"signature"`
	Address   common.Address         `json:"address"`
	EventData map[string]interface{} `json:"event_data"`
	Topics    []string               `json:"topics,omitempty"`
}

func (*DecodedCommonLog) MergeEventData

func (d *DecodedCommonLog) MergeEventData(newEventData map[string]interface{})

type DecodedTransaction

type DecodedTransaction struct {
	CommonData
	Index       uint                    `json:"index"`
	Hash        string                  `json:"hash,omitempty"`
	Protected   bool                    `json:"protected,omitempty"`
	Transaction *types.Transaction      `json:"transaction,omitempty"`
	Receipt     *types.Receipt          `json:"receipt,omitempty"`
	Events      []DecodedTransactionLog `json:"events,omitempty"`
}

DecodedTransaction decoded transaction

type DecodedTransactionLog

type DecodedTransactionLog struct {
	DecodedCommonLog
	BlockNumber uint64 `json:"block_number"`
	Index       uint   `json:"index"`
	TXHash      string `json:"hash"`
	TXIndex     uint   `json:"tx_index"`
	Removed     bool   `json:"removed"`
	FileTag     string `json:"file_tag,omitempty"`
}

DecodedTransactionLog decoded Solidity log(event)

func (*DecodedTransactionLog) MergeEventData

func (d *DecodedTransactionLog) MergeEventData(newEventData map[string]interface{})

type DeploymentData

type DeploymentData struct {
	Address       common.Address
	Transaction   *types.Transaction
	BoundContract *bind.BoundContract
}

type Duration

type Duration struct {
	// contains filtered or unexported fields
}

Duration is a non-negative time duration.

func MakeDuration

func MakeDuration(d time.Duration) (Duration, error)

func MustMakeDuration

func MustMakeDuration(d time.Duration) *Duration

func ParseDuration

func ParseDuration(s string) (Duration, error)

func (Duration) Before

func (d Duration) Before(t time.Time) time.Time

Before returns the time d units before time t

func (Duration) Duration

func (d Duration) Duration() time.Duration

Duration returns the value as the standard time.Duration value.

func (Duration) IsInstant

func (d Duration) IsInstant() bool

IsInstant is true if and only if d is of duration 0

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (Duration) MarshalText

func (d Duration) MarshalText() ([]byte, error)

MarshalText implements the text.Marshaler interface.

func (*Duration) Scan

func (d *Duration) Scan(v interface{}) (err error)

func (Duration) Shorter

func (d Duration) Shorter(od Duration) bool

Shorter returns true if and only if d is shorter than od.

func (Duration) String

func (d Duration) String() string

String returns a string representing the duration in the form "72h3m0.5s". Leading zero units are omitted. As a special case, durations less than one second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure that the leading digit is non-zero. The zero duration formats as 0s.

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(input []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

func (*Duration) UnmarshalText

func (d *Duration) UnmarshalText(input []byte) error

UnmarshalText implements the text.Unmarshaler interface.

func (Duration) Value

func (d Duration) Value() (driver.Value, error)

type FundKeyFileCmdOpts

type FundKeyFileCmdOpts struct {
	Addrs int64
}

FundKeyFileCmdOpts funding params for CLI

type FundingDetails

type FundingDetails struct {
	RootBalance        *big.Int
	TotalFee           *big.Int
	FreeBalance        *big.Int
	AddrFunding        *big.Int
	NetworkTransferFee int64
}

FundingDetails funding details about shares we put into test keys

type GasEstimationRequest added in v0.1.3

type GasEstimationRequest struct {
	GasEstimationEnabled bool
	FallbackGasPrice     int64
	FallbackGasFeeCap    int64
	FallbackGasTipCap    int64
	Priority             string
}

type GasEstimations added in v0.1.3

type GasEstimations struct {
	GasPrice  *big.Int
	GasTipCap *big.Int
	GasFeeCap *big.Int
}

type GasEstimator

type GasEstimator struct {
	Client              *Client
	BlockGasLimits      []uint64
	TransactionGasPrice []uint64
}

GasEstimator estimates gas prices

func NewGasEstimator

func NewGasEstimator(c *Client) *GasEstimator

NewGasEstimator creates a new gas estimator

func (*GasEstimator) Stats

func (m *GasEstimator) Stats(fromNumber uint64, priorityPerc float64) (GasSuggestions, error)

Stats prints gas stats

type GasPercentiles

type GasPercentiles struct {
	Max    float64
	Perc99 float64
	Perc75 float64
	Perc50 float64
	Perc25 float64
}

GasPercentiles contains gas percentiles

type GasSuggestions

type GasSuggestions struct {
	GasPrice           *GasPercentiles
	TipCap             *GasPercentiles
	SuggestedGasPrice  *big.Int
	SuggestedGasTipCap *big.Int
}

type KeyData

type KeyData struct {
	PrivateKey string `toml:"private_key"`
	Address    string `toml:"address"`
	Funds      string `toml:"funds"`
}

KeyData data for test keys

type KeyFile

type KeyFile struct {
	Keys []*KeyData `toml:"keys"`
}

KeyFile is a struct that holds all test keys data

func NewKeyFile

func NewKeyFile() *KeyFile

type KeyNonce

type KeyNonce struct {
	KeyNum int
	Nonce  uint64
}

type LFUHeaderCache added in v0.1.3

type LFUHeaderCache struct {
	// contains filtered or unexported fields
}

LFUHeaderCache is a Least Frequently Used header cache

func NewLFUBlockCache added in v0.1.3

func NewLFUBlockCache(capacity uint64) *LFUHeaderCache

NewLFUBlockCache creates a new LFU cache with the given capacity.

func (*LFUHeaderCache) Get added in v0.1.3

func (c *LFUHeaderCache) Get(blockNumber int64) (*types.Header, bool)

Get retrieves a header from the cache.

func (*LFUHeaderCache) Set added in v0.1.3

func (c *LFUHeaderCache) Set(header *types.Header) error

Set adds or updates a header in the cache.

type LogWithEventData

type LogWithEventData interface {
	MergeEventData(map[string]interface{})
}

type Network

type Network struct {
	Name                         string    `toml:"name"`
	ChainID                      string    `toml:"chain_id"`
	URLs                         []string  `toml:"urls_secret"`
	EIP1559DynamicFees           bool      `toml:"eip_1559_dynamic_fees"`
	GasPrice                     int64     `toml:"gas_price"`
	GasFeeCap                    int64     `toml:"gas_fee_cap"`
	GasTipCap                    int64     `toml:"gas_tip_cap"`
	GasLimit                     uint64    `toml:"gas_limit"`
	TxnTimeout                   *Duration `toml:"transaction_timeout"`
	TransferGasFee               int64     `toml:"transfer_gas_fee"`
	PrivateKeys                  []string  `toml:"private_keys_secret"`
	GasPriceEstimationEnabled    bool      `toml:"gas_price_estimation_enabled"`
	GasPriceEstimationBlocks     uint64    `toml:"gas_price_estimation_blocks"`
	GasPriceEstimationTxPriority string    `toml:"gas_price_estimation_tx_priority"`
}

type NonceManager

type NonceManager struct {
	*sync.Mutex

	Client      *Client
	SyncTimeout time.Duration
	SyncedKeys  chan *KeyNonce
	Addresses   []common.Address
	PrivateKeys []*ecdsa.PrivateKey
	Nonces      map[common.Address]int64
	// contains filtered or unexported fields
}

NonceManager tracks nonce for each address

func NewNonceManager

func NewNonceManager(cfg *Config, addrs []common.Address, privKeys []*ecdsa.PrivateKey) (*NonceManager, error)

NewNonceManager creates a new nonce manager that tracks nonce for each address

func (*NonceManager) NextNonce

func (m *NonceManager) NextNonce(addr common.Address) *big.Int

NextNonce returns new nonce for addr this method is external for module testing, but you should not use it since handling nonces on the client is unpredictable

func (*NonceManager) UpdateNonces

func (m *NonceManager) UpdateNonces() error

UpdateNonces syncs nonces for addresses

type NonceManagerCfg

type NonceManagerCfg struct {
	KeySyncRateLimitSec int       `toml:"key_sync_rate_limit_per_sec"`
	KeySyncTimeout      *Duration `toml:"key_sync_timeout"`
	KeySyncRetries      uint      `toml:"key_sync_retries"`
	KeySyncRetryDelay   *Duration `toml:"key_sync_retry_delay"`
}

type NonceStatus added in v0.1.3

type NonceStatus struct {
	LastNonce    uint64
	PendingNonce uint64
}

type TXCallTraceOutput

type TXCallTraceOutput struct {
	Call
	Calls []Call `json:"calls"`
}

func (*TXCallTraceOutput) AsCall

func (t *TXCallTraceOutput) AsCall() Call

type TXFourByteMetadataOutput

type TXFourByteMetadataOutput struct {
	CallSize int
	Times    int
}

type Trace

type Trace struct {
	TxHash       string
	FourByte     map[string]*TXFourByteMetadataOutput
	CallTrace    *TXCallTraceOutput
	OpCodesTrace map[string]interface{}
}

type TraceLog

type TraceLog struct {
	Address string   `json:"address"`
	Data    string   `json:"data"`
	Topics  []string `json:"topics"`
}

func (TraceLog) GetData

func (t TraceLog) GetData() []byte

func (TraceLog) GetTopics

func (t TraceLog) GetTopics() []common.Hash

type Tracer

type Tracer struct {
	Cfg *Config

	Addresses                []common.Address
	ContractStore            *ContractStore
	ContractAddressToNameMap ContractMap
	DecodedCalls             map[string][]*DecodedCall
	ABIFinder                *ABIFinder
	// contains filtered or unexported fields
}

func NewTracer

func NewTracer(url string, cs *ContractStore, abiFinder *ABIFinder, cfg *Config, contractAddressToNameMap map[string]string, addresses []common.Address) (*Tracer, error)

func (*Tracer) DecodeTrace

func (t *Tracer) DecodeTrace(l zerolog.Logger, trace Trace) ([]*DecodedCall, error)

DecodeTrace decodes the trace of a transaction including all subcalls. It returns a list of decoded calls. Depending on the config it also saves the decoded calls as JSON files.

func (*Tracer) PrintTXTrace

func (t *Tracer) PrintTXTrace(txHash string) error

func (*Tracer) SaveDecodedCallsAsJson

func (t *Tracer) SaveDecodedCallsAsJson(dirname string) error

func (*Tracer) TraceGethTX

func (t *Tracer) TraceGethTX(txHash string) error

type TransactOpt

type TransactOpt func(o *bind.TransactOpts)

TransactOpt is a wrapper for bind.TransactOpts

func WithGasFeeCap

func WithGasFeeCap(gasFeeCap *big.Int) TransactOpt

WithGasFeeCap sets gasFeeCap option for bind.TransactOpts

func WithGasLimit

func WithGasLimit(gasLimit uint64) TransactOpt

WithGasLimit sets gasLimit option for bind.TransactOpts

func WithGasPrice

func WithGasPrice(gasPrice *big.Int) TransactOpt

WithGasPrice sets gasPrice option for bind.TransactOpts

func WithGasTipCap

func WithGasTipCap(gasTipCap *big.Int) TransactOpt

WithGasTipCap sets gasTipCap option for bind.TransactOpts

func WithNoSend

func WithNoSend(noSend bool) TransactOpt

WithNoSend sets noSend option for bind.TransactOpts

func WithNonce

func WithNonce(nonce *big.Int) TransactOpt

WithNonce sets nonce option for bind.TransactOpts

func WithValue

func WithValue(value *big.Int) TransactOpt

WithValue sets value option for bind.TransactOpts

type TransactionLog

type TransactionLog struct {
	Topics []common.Hash
	Data   []byte
}

func (TransactionLog) GetData

func (t TransactionLog) GetData() []byte

func (TransactionLog) GetTopics

func (t TransactionLog) GetTopics() []common.Hash

Directories

Path Synopsis
cmd
contracts

Jump to

Keyboard shortcuts

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