seth

package module
v0.0.0-...-c2b60f3 Latest Latest
Warning

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

Go to latest
Published: Nov 21, 2024 License: MIT, MIT Imports: 48 Imported by: 18

README

Seth

Reliable and debug-friendly Ethereum client

Go Report Card Decoding tests Tracing tests Gas bumping tests API tests CLI tests Integration tests (testnets)

Content

  1. Goals
  2. Features
  3. Examples
  4. Setup
    1. Building test contracts
    2. Testing
  5. Configuration
    1. Simplified configuration
    2. ClientBuilder
    3. Supported env vars
    4. TOML configuration
  6. Automated gas price estimation
  7. DOT Graphs of transactions
  8. Using multiple private keys
  9. Experimental features
  10. Gas bumping for slow transactions
  11. CLI
  12. Manual gas price estimation
  13. Block Stats
  14. Single transaction tracing
  15. Bulk transaction tracing
  16. RPC traffic logging

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

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
  • 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)
  • Block stats CLI
  • Check if address has a pending nonce (transaction) and panic if it does
  • DOT graph output for tracing
  • Gas bumping for slow transactions

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

Examples

Check examples folder

Lib provides 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 any of the private keys passed as part of Network configuration in seth.toml or ephemeral keys.

// 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 test 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

Simplified configuration

If you do not want to set all the parameters, you can use a simplified progammatical configuration. Here's an example:

cfg := seth.DefaultConfig("ws://localhost:8546", []string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"})
client, err := seth.NewClientWithConfig(cfg)
if err != nil {
    log.Fatal(err)
}

This config uses what we consider reasonable defaults, such as:

  • 5 minute transaction confirmation timeout
  • 1 minute RPC node dial timeout
  • enabled EIP-1559 dynamic fees and automatic gas prices estimation (with 200 blocks history; will auto-disable itself if RPC doesn't support EIP-1559)
  • tracing only of reverted transaction to console and DOT graphs
  • checking of RPC node health on client creation
  • no ephemeral keys
ClientBuilder

You can also use a ClientBuilder to build a config programmatically. Here's an extensive example:

client, err := NewClientBuilder().
    // network
    WithNetworkName("my network").
	// if empty we will ask the RPC node for the chain ID
	WithNetworkChainId(1337).
    WithRpcUrl("ws://localhost:8546").
    WithPrivateKeys([]string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}).
    WithRpcDialTimeout(10*time.Second).
    WithTransactionTimeouts(1*time.Minute).
    // addresses
    WithEphemeralAddresses(10, 10).
    // tracing
    WithTracing(seth.TracingLevel_All, []string{seth.TraceOutput_Console}).
    // protections
    WithProtections(true, true, seth.MustMakeDuration(2*time.Minute)).
    // artifacts folder
    WithArtifactsFolder("some_folder").
	// folder with gethwrappers for ABI decoding
    WithGethWrappersFolders([]string{"./gethwrappers/ccip", "./gethwrappers/keystone"}).
    // nonce manager
    WithNonceManager(10, 3, 60, 5).
    // EIP-1559 and gas estimations
    WithEIP1559DynamicFees(true).
    WithDynamicGasPrices(120_000_000_000, 44_000_000_000).
    WithGasPriceEstimations(true, 10, seth.Priority_Fast).
// gas bumping: retries, max gas price, bumping strategy function
    WithGasBumping(5, 100_000_000_000, PriorityBasedGasBumpingStrategyFn).
    Build()

if err != nil {
    log.Fatal(err)
}

By default, it uses the same values as simplified configuration, but you can override them by calling the appropriate methods. Builder includes only options that we thought to be most useful, it's not a 1:1 mapping of all fields in the Config struct. Therefore, if you need to set some more advanced options, you should create the Config struct directly, use TOML config or manually set the fields on the Config struct returned by the builder.

It' also possible to use the builder to create a new config from an existing one:

client, err := NewClientBuilderWithConfig(&existingConfig).
        UseNetworkWithChainId(1337).
        WithEIP1559DynamicFees(false).
	    Build()

if err != nil {
    log.Fatal(err)
}

This can be useful if you already have a config, but want to modify it slightly. It can also be useful if you read TOML config with multiple Networks and you want to specify which one you want to use.

Supported 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_NETWORK=Geth # selected network
export SETH_ROOT_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 # root private key

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

Alternatively if you don't have a network defined in the TOML you can still use the CLI by providing these 2 key env vars:

export SETH_URL=https://rpc.fuji.testnet.anyswap.exchange
export SETH_CHAIN_ID=43113

go run cmd/seth/seth.go ... # your command

In that case you should still pass network name with -n flag.

TOML configuration

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"

Decide whether you want to generate any ephemeral keys:

# Set number of ephemeral keys to be generated (0 for no ephemeral keys). Each key will receive a proportion of native tokens from root private key's balance with the value equal to `(root_balance / ephemeral_keys_number) - transfer_fee * ephemeral_keys_number`.
ephemeral_addresses_number = 10

You can enable auto-tracing for all transactions meeting configured level, which means that every time you use Decode() we will decode the transaction and also trace all calls made within the transaction, together with all inputs, outputs, logs and events. Three tracing levels are available:

  • all - trace all transactions
  • reverted - trace only reverted transactions (that's default setting used if you don't set tracing_level)
  • none - don't trace any transactions

Example:

tracing_level = "reverted"

Additionally, you can decide where tracing/decoding data goes to. There are three options:

  • console - we will print all tracing data to the console
  • json - we will save tracing data for each transaction to a JSON file
  • dot - we will save tracing data for each transaction to a DOT file (graph)
trace_outputs = ["console", "json", "dot"]

For info on viewing DOT files please check the DOT graphs section below.

Example: image These two options should be used with care, when tracing_level is set to all as they might generate a lot of data.

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 transferring 10k wei from root key to root key and check if the transaction was successful.

You can also enable pending nonce protection that will check if given key has any pending transactions. By default, we will wait 1 minute for all transactions to be mined. If any of them is still pending, we will panic. You can enable it with:

pending_nonce_protection_enabled = true
pending_nonce_protection_timeout = "5m"

You can add more networks like this:

[[Networks]]
name = "Fuji"
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
# hardcoded gas limit for sending funds that will be used if estimation of gas limit fails
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 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 don't we will use the default settings for Default network.

ChainID is not needed, as it's fetched from the node.

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.

Automatic 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. Disabled if gas_price_estimation_blocks equals 0.
  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 greatest 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.

When fetching historical base fee and tip data, we will use the last gas_price_estimation_blocks blocks. If it's set to 0 we will default to 100 last blocks. If the blockchain has less than 100 blocks we will use all of them.

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

For fast transactions we will increase gas price by 20%, for standard we will use the value as is and for slow we will decrease it by 20%.

Buffer percents

If gas_price_estimation_blocks is higher than 0 we further adjust the gas price by adding a buffer to it, based on congestion rate:

case Congestion_Low:
    return 1.10, nil
case Congestion_Medium:
    return 1.20, nil
case Congestion_High:
    return 1.30, nil
case Congestion_VeryHigh:
    return 1.40, nil

For low congestion rate we will increase gas price by 10%, for medium by 20%, for high by 30% and for very high by 40%. 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). It's important to know that in order to use congestion metrics we need to fetch at least 80% of the requested blocks. If that fails, we will skip this part of the estimation and only adjust the gas price based on priority. For both transaction types if any of the steps fails, we fall back to hardcoded values.

DOT graphs

There are multiple ways of visualising DOT graphs:

  • xdot application [recommended]
  • VSCode Extensions
  • online viewers
xdot

To install simply run homebrew install xdot and then run xdot <path_to_dot_file>. This tool seems to be the best for the job, since the viewer is interactive and supports tooltips, which in our case contain extra tracing information.

VSCode Extensions

There are multiple extensions that can be used to view DOT files in VSCode. We recommend using Graphviz Preview. The downside is that it doesn't support tooltips.

Goland

We were unable to find any (working) plugins for DOT graph visualization. If you do know any, please let us know.

Online viewers

There's at least a dozen of them available, but none of them support tooltips and most can't handle our multi-line labels. These two are known to work, though:

Using multiple keys

If you want to use existing multiple keys (instead of ephemeral ones) you can pass them as part of the network configuration. In that case it's recommended to not read them from TOML file. If you need to read them for the filesystem/os it's best if you use environment variables. Once you've read them in a safe manner you should programmatically add them to Seth's Config struct (which safe parts can be read from TOML file). You can either add them directly to Network, if it's already set up, or you can add them to Networks slice to the network you intend to use.

For example you could start by reading the TOML configuration first:

cfg, err := seth.ReadCfg()
if err != nil {
    log.Fatal(err)
}

Then read the private keys in a safe manner. For example from a secure vault or environment variables:

var privateKeys []string
var err error
privateKeys, err = some_utils.ReadPrivateKeysFromEnv()
if err != nil {
    log.Fatal(err)
}

and then add them to the Network you plan to use. Let's assume it's called Sepolia:

for i, network := range cfg.Networks {
    if network.Name == "Sepolia" {
        cfg.Networks[i].PrivateKeys = privateKeys
    }
}

Or if you aren't using [[Networks]] in your TOML config and have just a single Network:

cfg.Network.PrivateKeys = privateKeys

Or... you can use the convenience function AppendPksToNetwork() to have them added to both the Network and Networks slice:

added := cfg.AppendPksToNetwork(privateKeys, "Sepolia")
if !added {
    log.Fatal("Network Sepolia not found in the config")
}

Finally, proceed to create a new Seth instance:

seth, err := seth.NewClientWithConfig(cfg)
if err != nil {
    log.Fatal(err)
}

A working example can be found here as TestSmokeExampleMultiKeyFromEnv test.

Currently, there's no safe way to pass multiple keys to CLI. In that case TOML is the only way to go, but you should be mindful that if you commit the TOML file with keys in it, you should assume they are compromised and all funds on them are lost.

Experimental features

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

# other settings before...
tracing_level = "reverted"
trace_outputs = ["console"]
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).

Gas bumping for slow transactions

Seth has built-in gas bumping mechanism for slow transactions. If a transaction is not mined within a certain time frame (Network's transaction timeout), Seth will automatically bump the gas price and resubmit the transaction. This feature is disabled by default and can be enabled by setting the [gas_bumps] retries to a non-zero number:

[gas_bumps]
retries = 5

Once enabled, by default the amount, by which gas price is bumped depends on gas_price_estimation_tx_priority setting and is calculated as follows:

  • Priority_Fast: 30% increase
  • Priority_Standard: 15% increase
  • Priority_Slow: 5% increase
  • everything else: no increase

You can cap max gas price by settings (in wei):

[gas_bumps]
max_gas_price = 1000000000000

Once the gas price bump would go above the limit we stop bumping and use the last gas price that was below the limit.

How gas price is calculated depends on transaction type:

  • for legacy transactions it's just the gas price
  • for EIP-1559 transactions it's the sum of gas fee cap and tip cap
  • for Blob transactions (EIP-4844) it's the sum of gas fee cap and tip cap and max fee per blob
  • for AccessList transactions (EIP-2930) it's just the gas price

Please note that Blob and AccessList support remains experimental and is not tested.

If you want to use a custom bumping strategy, you can use a function with GasBumpStrategyFn type. Here's an example of a custom strategy that bumps the gas price by 100% for every retry:

var customGasBumpStrategyFn = func(gasPrice *big.Int) *big.Int {
    return new(big.Int).Mul(gasPrice, big.NewInt(2))
}

To use this strategy, you need to pass it to the WithGasBumping function in the ClientBuilder:

var hundredGwei in64 = 100_000_000_000
client, err := builder.
    // other settings...
    WithGasBumping(5, hundredGwei, customGasBumpStrategyFn).
    Build()

Or set it directly on Seth's config:

// assuming sethClient is already created
sethClient.Config.GasBumps.StrategyFn = customGasBumpStrategyFn

Since strategy function only accepts a single parameter, if you want to base its behaviour on anything else than that you will need to capture these values from the context, in which you define the strategy function. For example, you can use a closure to capture the initial gas price:

gasOracleClient := NewGasOracleClient()

var oracleGasBumpStrategyFn = func(gasPrice *big.Int) *big.Int {
    // get the current gas price from the oracle
    suggestedGasPrice := gasOracleClient.GetCurrentGasPrice()

	// if oracle suggests a higher gas price, use it
    if suggestedGasPrice.Cmp(gasPrice) == 1 {
        return suggestedGasPrice
    }

	// otherwise bump by 100%
    return new(big.Int).Mul(gasPrice, big.NewInt(2))
}

Same strategy is applied to all types of transactions, regardless whether it's gas price, gas fee cap, gas tip cap or max blob fee.

When enabled, gas bumping is used in two places:

  • during contract deployment via DeployContract function
  • inside Decode() function

It is recommended to decrease transaction timeout when using gas bumping, as it will be effectively increased by the number of retries. So if you were running with 5 minutes timeout and 0 retries, you should set it to 1 minute and 5 retries or 30 seconds and 10 retries.

Don't worry if while bumping logic executes previous transaction gets mined. In that case sending replacement transaction with higher gas will fail (because it is using the same nonce as original transaction) and we will retry waiting for the mining of the original transaction.

Gas bumping is only applied for submitted transaction. If transaction was rejected by the node (e.g. because of too low base fee) we will not bump the gas price nor try to submit it, because original transaction submission happens outside of Seth.

CLI

You can either define the network you want to interact with in your TOML config and then refer it in the CLI command, or you can pass all network parameters via env vars. Most of the examples below show how to use the former approach.

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

Block Stats

If you need to get some insights into network stats and create a realistic load/chaos profile with simulators (anvil as an example), you can use stats CLI command

Define your network in seth.toml

Edit your seth.toml

[[networks]]
name = "MyCustomNetwork"
urls_secret = ["..."]

[block_stats]
rpc_requests_per_second_limit = 5

Then check the stats for the last N blocks

seth -n MyCustomNetwork stats -s -10

To check stats for the interval (A, B)

seth -n MyCustomNetwork stats -s A -e B
Pass all network parameters via env vars

If you don't have a network defined in the TOML you can still use the CLI by providing the RPC url via cmd arg.

Then check the stats for the last N blocks

seth -u "https://my-rpc.network.io" stats -s -10

To check stats for the interval (A, B)

seth -u "https://my-rpc.network.io" stats -s A -e B

Results can help you to understand if network is stable, what is avg block time, gas price, block utilization and transactions per second.

# Stats
perc_95_tps = 8.0
perc_95_block_duration = '3s'
perc_95_block_gas_used = 1305450
perc_95_block_gas_limit = 15000000
perc_95_block_base_fee = 25000000000
avg_tps = 2.433333333333333
avg_block_duration = '2s'
avg_block_gas_used = 493233
avg_block_gas_limit = 15000000
avg_block_base_fee = 25000000000

# Recommended performance/chaos test parameters
duration = '2m0s'
block_gas_base_fee_initial_value = 25000000000
block_gas_base_fee_bump_percentage = '100.00% (no bump required)'
block_gas_usage_percentage = '3.28822000% gas used (no congestion)'
avg_tps = 3.0
max_tps = 8.0
Single transaction tracing

You can trace a single transaction using seth trace command. Example with seth alias mentioned before:

seth -u "https://my-rpc.network.io" trace -t 0x4c21294bf4c0a19de16e0fca74e1ea1687ba96c3cab64f6fca5640fb7b84df65

or if you want to use a predefined-network:

seth -n=Geth trace -t 0x4c21294bf4c0a19de16e0fca74e1ea1687ba96c3cab64f6fca5640fb7b84df65
Bulk transaction tracing

You can trace multiple transactions at once using seth trace command for a predefined network named Geth. Example:

seth -n=Geth trace -f reverted_transactions.json

or by passing all the RPC parameter with a flag:

seth -u "https://my-rpc.network.io" 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.)

RPC Traffic logging

With SETH_LOG_LEVEL=trace we will also log to console all traffic between Seth and RPC node. This can be useful for debugging as you can see all the requests and responses.

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"
	ErrRpcHealthCheckFailed     = "RPC health check failed ¯\\_(ツ)_/¯"
	ErrContractDeploymentFailed = "contract deployment failed"

	ContractMapFilePattern          = "deployed_contracts_%s_%s.toml"
	RevertedTransactionsFilePattern = "reverted_transactions_%s_%s.json"
)
View Source
const (
	ErrReadSethConfig      = "failed to read TOML config for seth"
	ErrUnmarshalSethConfig = "failed to unmarshal TOML config for seth"
	ErrEmptyRootPrivateKey = "no root private key were set, set %s=..."

	GETH  = "Geth"
	ANVIL = "Anvil"

	CONFIG_FILE_ENV_VAR = "SETH_CONFIG_PATH"

	ROOT_PRIVATE_KEY_ENV_VAR = "SETH_ROOT_PRIVATE_KEY"
	NETWORK_ENV_VAR          = "SETH_NETWORK"
	URL_ENV_VAR              = "SETH_URL"

	DefaultNetworkName                   = "Default"
	DefaultDialTimeout                   = 1 * time.Minute
	DefaultPendingNonceProtectionTimeout = 1 * time.Minute

	DefaultTransferGasFee = 21_000
	DefaultGasPrice       = 100_000_000_000 // 100 Gwei
	DefaultGasFeeCap      = 100_000_000_000 // 100 Gwei
	DefaultGasTipCap      = 50_000_000_000  // 50 Gwei
)
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"
	ErrNoABIInFile = "no ABI content found in 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"
	ErrTooShortTxData       = "tx data is less than 4 bytes, can't decode"
	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_VeryHigh = "extreme"
)
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"
	TimeoutKeyNum     = -80001
)
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"
	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 (
	MetadataNotFoundErr       = "metadata section not found"
	InvalidMetadataLengthErr  = "invalid metadata length"
	FailedToDecodeMetadataErr = "failed to decode metadata"
	NotCompiledWithSolcErr    = "not compiled with solc"
)
View Source
const (
	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 (
	// Amount of funds that will be left on the root key, when splitting funds between ephemeral addresses
	ZeroInt64 int64

	TracingLevel_None     = "NONE"
	TracingLevel_Reverted = "REVERTED"
	TracingLevel_All      = "ALL"

	TraceOutput_Console = "console"
	TraceOutput_JSON    = "json"
	TraceOutput_DOT     = "dot"
)
View Source
var (
	ZeroGasSuggestedErr = "either base fee or suggested tip is 0"
	BlockFetchingErr    = "failed to fetch enough block headers for congestion calculation"
)
View Source
var NoOpGasBumpStrategyFn = func(previousGasPrice *big.Int) *big.Int {
	return previousGasPrice
}

NoOpGasBumpStrategyFn is a default gas bump strategy that does nothing

View Source
var PriorityBasedGasBumpingStrategyFn = func(priority string) GasBumpStrategyFn {
	switch priority {
	case Priority_Degen:

		return func(gasPrice *big.Int) *big.Int {
			return gasPrice.Mul(gasPrice, big.NewInt(2))
		}
	case Priority_Fast:

		return func(gasPrice *big.Int) *big.Int {
			gasPriceFloat, _ := gasPrice.Float64()
			newGasPriceFloat := big.NewFloat(0.0).Mul(big.NewFloat(gasPriceFloat), big.NewFloat(1.3))
			newGasPrice, _ := newGasPriceFloat.Int64()
			return big.NewInt(newGasPrice)
		}
	case Priority_Standard:

		return func(gasPrice *big.Int) *big.Int {
			gasPriceFloat, _ := gasPrice.Float64()
			newGasPriceFloat := big.NewFloat(0.0).Mul(big.NewFloat(gasPriceFloat), big.NewFloat(1.15))
			newGasPrice, _ := newGasPriceFloat.Int64()
			return big.NewInt(newGasPrice)
		}
	case Priority_Slow:

		return func(gasPrice *big.Int) *big.Int {
			gasPriceFloat, _ := gasPrice.Float64()
			newGasPriceFloat := big.NewFloat(0.0).Mul(big.NewFloat(gasPriceFloat), big.NewFloat(1.05))
			newGasPrice, _ := newGasPriceFloat.Int64()
			return big.NewInt(newGasPrice)
		}
	default:
		return func(gasPrice *big.Int) *big.Int {
			return gasPrice
		}
	}
}

PriorityBasedGasBumpingStrategyFn is a function that returns a gas bump strategy based on the priority. For Fast priority it bumps gas price by 30%, for Standard by 15%, for Slow by 5% and for the rest it does nothing.

Functions

func CreateOrAppendToJsonArray

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 DoesPragmaSupportCustomRevert

func DoesPragmaSupportCustomRevert(pragma Pragma) bool

DoesPragmaSupportCustomRevert checks if the pragma version supports custom revert messages (must be >= 0.8.4)

func EtherToWei

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 desired number of ephemeral keys, should be used only with ephemeral networks. Remember that they are not persisted anywhere, so you shouldn't use that option with live networks.

func NewLoggingTransport

func NewLoggingTransport() http.RoundTripper

NewLoggingTransport creates a new logging transport for GAP or default transport controlled by SETH_LOG_LEVEL

func OpenJsonFileAsStruct

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 other keys

func SaveDeployedContract

func SaveDeployedContract(filename, contractName, address string) error

func ValidateConfig

func ValidateConfig(cfg *Config) error

func WeiToEther

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 BlockStats

type BlockStats struct {
	Limiter ratelimit.Limiter
	Client  *Client
}

BlockStats is a block stats calculator

func NewBlockStats

func NewBlockStats(c *Client) (*BlockStats, error)

NewBlockStats creates a new instance of BlockStats

func (*BlockStats) CalculateBlockDurations

func (cs *BlockStats) CalculateBlockDurations(blocks []*types.Block) error

CalculateBlockDurations calculates and logs the duration, TPS, gas used, and gas limit between each consecutive block

func (*BlockStats) Stats

func (cs *BlockStats) Stats(startBlock *big.Int, endBlock *big.Int) error

Stats fetches and logs the blocks' statistics from startBlock to endBlock

type BlockStatsConfig

type BlockStatsConfig struct {
	RPCRateLimit int `toml:"rpc_requests_per_second_limit"`
}

func (*BlockStatsConfig) Validate

func (cfg *BlockStatsConfig) Validate() error

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"`
	Error   string     `json:"error"`
	Calls   []Call     `json:"calls"`
}

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
	ContractAddressToNameMap ContractMap
	ABIFinder                *ABIFinder
	HeaderCache              *LFUHeaderCache
}

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

func DefaultClient

func DefaultClient(rpcUrl string, privateKeys []string) (*Client, error)

DefaultClient returns a Client with reasonable default config with the specified RPC URL and private keys. You should pass at least 1 private key. It assumes that network is EIP-1559 compatible (if it's not, the client will later automatically update its configuration to reflect it).

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

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

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, gasPrice, rooKeyBuffer 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, error)

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

func (*Client) Decode

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

Decode waits for transaction to be minted, then decodes transaction inputs, outputs, logs and events and depending on 'tracing_level' it either returns immediately or if the level matches it traces all calls. Where tracing results are sent depends on the 'trace_outputs' field in the config. If transaction was reverted the error returned will be revert error, not decoding error (that, if any, will only be logged). At the same time we also return decoded transaction, so contrary to go convention you might get both error and result, because we want to return the decoded transaction even if it was reverted. Last, but not least, if gas bumps are enabled, we will try to bump gas on transaction mining timeout and resubmit it with higher gas.

func (*Client) DecodeCustomABIErr

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

DecodeCustomABIErr decodes typed Solidity errors

func (*Client) DecodeSendErr

func (m *Client) DecodeSendErr(txErr error) error

DecodeSendErr tries to decode the error and return the reason of the revert. If the error is not revert, it returns the original error. If the error is revert, but it cannot be decoded, it logs the error and returns the original error. If the error is revert, and it can be decoded, it returns the decoded error. This function is used to decode errors that are returned by the send transaction function.

func (*Client) DecodeTx

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

DecodeTx waits for transaction to be minted, then decodes transaction inputs, outputs, logs and events and depending on 'tracing_level' and transaction status (reverted or not) it either returns immediately or traces all calls. If transaction was reverted the error returned will be revert error, not decoding error (that, if any, will only be logged). At the same time we also return decoded transaction, so contrary to go convention you might get both error and result, because we want to return the decoded transaction even if it was reverted. Last, but not least, if gas bumps are enabled, we will try to bump gas on transaction mining timeout and resubmit it with higher gas.

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) DownloadContractAndGetPragma

func (m *Client) DownloadContractAndGetPragma(address common.Address, block *big.Int) (Pragma, error)

func (*Client) EstimateGasLimitForFundTransfer

func (m *Client) EstimateGasLimitForFundTransfer(from, to common.Address, amount *big.Int) (uint64, error)

EstimateGasLimitForFundTransfer estimates gas limit for fund transfer

func (*Client) GetRootKeyAddress

func (m *Client) GetRootKeyAddress() (common.Address, error)

GetRootKeyAddress returns the root key address from the client configuration. If no addresses are found, it returns an error. Root key address is the first address in the list of addresses.

func (*Client) GetRootPrivateKey

func (m *Client) GetRootPrivateKey() (*ecdsa.PrivateKey, error)

GetRootPrivateKey returns the private key of root key/address from the client configuration. If no private keys are found, it returns an error. Root private key is the first private key in the list of private keys.

func (*Client) GetSuggestedEIP1559Fees

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

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

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

func (*Client) MustGetRootKeyAddress

func (m *Client) MustGetRootKeyAddress() common.Address

MustGetRootKeyAddress returns the root key address from the client configuration. If no addresses are found, it panics. Root key address is the first address in the list of addresses.

func (*Client) MustGetRootPrivateKey

func (m *Client) MustGetRootPrivateKey() *ecdsa.PrivateKey

MustGetRootPrivateKey returns the private key of root key/address from the client configuration. If no private keys are found, it panics. Root private key is the first private key in the list of private keys.

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) NewDefaultGasEstimationRequest

func (m *Client) NewDefaultGasEstimationRequest() GasEstimationRequest

NewDefaultGasEstimationRequest creates a new default gas estimation request based on current network configuration

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, gasPrice *big.Int) error

func (*Client) WaitMined

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

func (*Client) WaitUntilNoPendingTx

func (m *Client) WaitUntilNoPendingTx(address common.Address, timeout time.Duration) error

WaitUntilNoPendingTx waits until there's no pending transaction for address. If after timeout there are still pending transactions, it returns error.

func (*Client) WaitUntilNoPendingTxForKeyNum

func (m *Client) WaitUntilNoPendingTxForKeyNum(keyNum int, timeout time.Duration) error

WaitUntilNoPendingTxForKeyNum waits until there's no pending transaction for key at index `keyNum`. If index is out of range or if after timeout there are still pending transactions, it returns error.

func (*Client) WaitUntilNoPendingTxForRootKey

func (m *Client) WaitUntilNoPendingTxForRootKey(timeout time.Duration) error

WaitUntilNoPendingTxForRootKey waits until there's no pending transaction for root key. If after timeout there are still pending transactions, it returns error.

type ClientBuilder

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

func NewClientBuilder

func NewClientBuilder() *ClientBuilder

NewClientBuilder creates a new ClientBuilder with reasonable default values. You only need to pass private key(s) and RPC URL to build a usable config.

func NewClientBuilderWithConfig

func NewClientBuilderWithConfig(config *Config) *ClientBuilder

NewClientBuilderWithConfig creates a new ClientBuilder with a provided config. If it doesn't have the network set, remember to set it with `UseNetworkWithName(name string)` or `WithSelectedNetworkWithChainId(chainId uint64)`, before calling any of the methods that modify the Network.

func (*ClientBuilder) Build

func (c *ClientBuilder) Build() (*Client, error)

Build creates a new Client from the builder.

func (*ClientBuilder) BuildConfig

func (c *ClientBuilder) BuildConfig() (*Config, error)

BuildConfig returns the config from the builder.

func (*ClientBuilder) UseNetworkWithChainId

func (c *ClientBuilder) UseNetworkWithChainId(chainId uint64) *ClientBuilder

UseNetworkWithChainId sets the network to use by chain ID. If the network with the provided chain ID is not found in the `Networks` slice, config will fail on build. There is no default value.

func (*ClientBuilder) UseNetworkWithName

func (c *ClientBuilder) UseNetworkWithName(name string) *ClientBuilder

UseNetworkWithName sets the network to use by name. If the network with the provided name is not found in the `Networks` slice, config will fail on build. There is no default value.

func (*ClientBuilder) WithArtifactsFolder

func (c *ClientBuilder) WithArtifactsFolder(folder string) *ClientBuilder

WithArtifactsFolder sets the folder where the Seth artifacts such as DOT graphs or JSON will be saved. Default value is "seth_artifacts".

func (*ClientBuilder) WithDynamicGasPrices

func (c *ClientBuilder) WithDynamicGasPrices(gasFeeCap, gasTipCap int64) *ClientBuilder

WithDynamicGasPrices sets the gas fee cap and gas tip cap for EIP-1559 dynamic fees. These values will be used only if EIP-1559 dynamic fees are enabled. Default values are 150 gwei for gas fee cap and 50 gwei for gas tip cap.

func (*ClientBuilder) WithEIP1559DynamicFees

func (c *ClientBuilder) WithEIP1559DynamicFees(enabled bool) *ClientBuilder

WithEIP1559DynamicFees enables or disables EIP-1559 dynamic fees. If enabled, you should set gas fee cap and gas tip cap with `WithDynamicGasPrices()` Default value is true.

func (*ClientBuilder) WithEphemeralAddresses

func (c *ClientBuilder) WithEphemeralAddresses(ephemeralAddressCount, rootKeyBufferAmount int64) *ClientBuilder

WithEphemeralAddresses sets the number of ephemeral addresses to generate and the amount of funds to keep in the root private key. Default values are 0 for ephemeral addresses and 0 for root key funds buffer.

func (*ClientBuilder) WithGasBumping

func (c *ClientBuilder) WithGasBumping(retries uint, maxGasPrice int64, customBumpingStrategy GasBumpStrategyFn) *ClientBuilder

WithGasBumping sets the number of retries for gas bumping and max gas price. You can also provide a custom bumping strategy. If the transaction is not mined within this number of retries, it will be considered failed. If the gas price is bumped to a value higher than max gas price, no more gas bumping will be attempted and previous gas price will be used by all subsequent attempts. If set to 0 max price is not checked. Default value is 0 retries. If you want to use default bumping strategy (where gas increase % based on gas_price_estimation_tx_priority), pass `nil` as the customBumpingStrategy.

func (*ClientBuilder) WithGasPriceEstimations

func (c *ClientBuilder) WithGasPriceEstimations(enabled bool, estimationBlocks uint64, txPriority string) *ClientBuilder

WithGasPriceEstimations enables or disables gas price estimations, sets the number of blocks to use for estimation or transaction priority. Even with estimations enabled you should still either set legacy gas price with `WithLegacyGasPrice()` or EIP-1559 dynamic fees with `WithDynamicGasPrices()` ss they will be used as fallback values, if the estimations fail. Following priorities are supported: "slow", "standard" and "fast" Default values are true for enabled, 200 blocks for estimation and "standard" for priority.

func (*ClientBuilder) WithGethWrappersFolders

func (c *ClientBuilder) WithGethWrappersFolders(folders []string) *ClientBuilder

WithGethWrappersFolders sets list of folders where the Geth wrappers are stored. Seth will load ABIs from all wrappers it finds in theses folders (including subfolders). Default value is an empty string (= loading disabled).

func (*ClientBuilder) WithLegacyGasPrice

func (c *ClientBuilder) WithLegacyGasPrice(gasPrice int64) *ClientBuilder

WithLegacyGasPrice sets the gas price for legacy transactions that will be used only if EIP-1559 dynamic fees are disabled. Default value is 1 gwei.

func (*ClientBuilder) WithNetworkChainId

func (c *ClientBuilder) WithNetworkChainId(chainId uint64) *ClientBuilder

WithNetworkChainId sets the network chainID. If no value is set, we will ask the RPC node for the chainID. There is no default value.

func (*ClientBuilder) WithNetworkName

func (c *ClientBuilder) WithNetworkName(name string) *ClientBuilder

WithNetworkName sets the network name, useful mostly for debugging and logging. Default value is "default".

func (*ClientBuilder) WithNonceManager

func (c *ClientBuilder) WithNonceManager(rateLimitSec int, retries uint, timeout, retryDelay time.Duration) *ClientBuilder

WithNonceManager sets the rate limit for key sync, number of retries, timeout and retry delay. Default values are 10 calls per second, 3 retires, 60s timeout and 5s retry delay.

func (*ClientBuilder) WithPrivateKeys

func (c *ClientBuilder) WithPrivateKeys(pks []string) *ClientBuilder

WithPrivateKeys sets the private keys for the config. At least one is required to build a valid config. Default value is an empty slice (which is an incorrect value).

func (*ClientBuilder) WithProtections

func (c *ClientBuilder) WithProtections(pendingNonceProtectionEnabled, nodeHealthStartupCheck bool, pendingNonceProtectionTimeout *Duration) *ClientBuilder

WithProtections enables or disables nonce protection (fails, when key has a pending transaction, and you try to submit another one) and node health check on startup. Default values are false for nonce protection, true for node health check and 1 minute timeout.

func (*ClientBuilder) WithRpcDialTimeout

func (c *ClientBuilder) WithRpcDialTimeout(timeout time.Duration) *ClientBuilder

WithRpcDialTimeout sets the timeout for dialing the RPC server. If the connection is not established within this time, it will be considered failed. Default value is 1 minute.

func (*ClientBuilder) WithRpcUrl

func (c *ClientBuilder) WithRpcUrl(url string) *ClientBuilder

WithRpcUrl sets the RPC URL for the config. Default value is an empty string (which is an incorrect value).

func (*ClientBuilder) WithTracing

func (c *ClientBuilder) WithTracing(level string, outputs []string) *ClientBuilder

WithTracing sets the tracing level and outputs. Tracing level can be one of: "all", "reverted", "none". Outputs can be one or more of: "console", "dot" or "json". Default values are "reverted" and ["console", "dot"].

func (*ClientBuilder) WithTransactionTimeout

func (c *ClientBuilder) WithTransactionTimeout(timeout time.Duration) *ClientBuilder

WithTransactionTimeout sets the timeout for transactions. If the transaction is not mined within this time, it will be considered failed. Default value is 5 minutes.

func (*ClientBuilder) WithTransferGasFee

func (c *ClientBuilder) WithTransferGasFee(transferGasFee int64) *ClientBuilder

WithTransferGasFee sets the gas fee for transfer transactions. This value is used, when sending funds to ephemeral keys or returning funds to root private key. Default value is 21_000 wei.

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 ContractMap) 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 {
	CallType        string                 `json:"call_type,omitempty"`
	Signature       string                 `json:"signature"`
	Method          string                 `json:"method"`
	Input           map[string]interface{} `json:"input,omitempty"`
	Output          map[string]interface{} `json:"output,omitempty"`
	NestingLevel    int                    `json:"nesting_level,omitempty"`
	ParentSignature string                 `json:"parent_signature,omitempty"`
	Error           string                 `json:"error,omitempty"`
}

type Config

type Config struct {
	RPCHeaders http.Header

	// external fields
	// ArtifactDir is the directory where all artifacts generated by seth are stored (e.g. transaction traces)
	ArtifactsDir                  string            `toml:"artifacts_dir"`
	EphemeralAddrs                *int64            `toml:"ephemeral_addresses_number"`
	RootKeyFundsBuffer            *int64            `toml:"root_key_funds_buffer"`
	ABIDir                        string            `toml:"abi_dir"`
	BINDir                        string            `toml:"bin_dir"`
	GethWrappersDirs              []string          `toml:"geth_wrappers_dirs"`
	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"`
	TracingLevel                  string            `toml:"tracing_level"`
	TraceOutputs                  []string          `toml:"trace_outputs"`
	PendingNonceProtectionEnabled bool              `toml:"pending_nonce_protection_enabled"`
	PendingNonceProtectionTimeout *Duration         `toml:"pending_nonce_protection_timeout"`
	ConfigDir                     string            `toml:"abs_path"`
	ExperimentsEnabled            []string          `toml:"experiments_enabled"`
	CheckRpcHealthOnStart         bool              `toml:"check_rpc_health_on_start"`
	BlockStatsConfig              *BlockStatsConfig `toml:"block_stats"`
	GasBump                       *GasBumpConfig    `toml:"gas_bump"`
	// 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) AppendPksToNetwork

func (c *Config) AppendPksToNetwork(pks []string, name string) bool

AppendPksToNetwork appends private keys to the network with the specified name and returns "true" if the network was updated.

func (*Config) FirstNetworkURL

func (c *Config) FirstNetworkURL() string

FirstNetworkURL returns first network URL

func (*Config) GasBumpRetries

func (c *Config) GasBumpRetries() uint

GasBumpRetries returns the number of retries for gas bumping

func (*Config) GenerateContractMapFileName

func (c *Config) GenerateContractMapFileName() string

GenerateContractMapFileName generates a file name for the contract map

func (*Config) GetMaxConcurrency

func (c *Config) GetMaxConcurrency() int

GetMaxConcurrency returns the maximum number of concurrent transactions. Root key is excluded from the count.

func (*Config) HasMaxBumpGasPrice

func (c *Config) HasMaxBumpGasPrice() bool

HasMaxBumpGasPrice returns true if the max gas price for gas bumping is set

func (*Config) IsExperimentEnabled

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

IsExperimentEnabled returns true if the experiment is enabled

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) ShouldSaveDeployedContractMap

func (c *Config) ShouldSaveDeployedContractMap() bool

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

type ContextErrorKey

type ContextErrorKey struct{}

type ContractLoader

type ContractLoader[T any] struct {
	Client *Client
}

ContractLoader is a helper struct for loading contracts

func NewContractLoader

func NewContractLoader[T any](client *Client) *ContractLoader[T]

NewContractLoader creates a new contract loader

func (*ContractLoader[T]) LoadContract

func (cl *ContractLoader[T]) LoadContract(name string, address common.Address, abiLoadFn func() (*abi.ABI, error), wrapperInitFn func(common.Address, bind.ContractBackend) (*T, error)) (*T, error)

LoadContract loads contract by name, address, ABI loader and wrapper init function, it adds contract ABI to Seth Contract Store and address to Contract Map. Thanks to that we can easily trace and debug interactions with the contract. Signatures of functions passed to this method were chosen to conform to Geth wrappers' GetAbi() and NewXXXContract() functions.

type ContractMap

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

func NewContractMap

func NewContractMap(contracts map[string]string) ContractMap

func NewEmptyContractMap

func NewEmptyContractMap() ContractMap

func (ContractMap) AddContract

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

func (ContractMap) GetContractAddress

func (c ContractMap) GetContractAddress(addr string) string

func (ContractMap) GetContractMap

func (c ContractMap) GetContractMap() map[string]string

func (ContractMap) GetContractName

func (c ContractMap) GetContractName(addr string) string

func (ContractMap) IsKnownAddress

func (c ContractMap) IsKnownAddress(addr string) bool

func (ContractMap) Size

func (c ContractMap) Size() int

type ContractStore

type ContractStore struct {
	ABIs ABIStore
	BINs map[string][]byte
	// contains filtered or unexported fields
}

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

func NewContractStore

func NewContractStore(abiPath, binPath string, gethWrappersPaths []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{ D time.Duration }

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 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 GasBumpConfig

type GasBumpConfig struct {
	Retries     uint              `toml:"retries"`
	MaxGasPrice int64             `toml:"max_gas_price"`
	StrategyFn  GasBumpStrategyFn `toml:"-"`
}

type GasBumpStrategyFn

type GasBumpStrategyFn = func(previousGasPrice *big.Int) *big.Int

GasBumpStrategyFn is a function that returns a new gas price based on the previous one

type GasEstimationRequest

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

type GasEstimations

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 KeyNonce

type KeyNonce struct {
	KeyNum int
	Nonce  uint64
}

type LFUHeaderCache

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

LFUHeaderCache is a Least Frequently Used header cache

func NewLFUBlockCache

func NewLFUBlockCache(capacity uint64) *LFUHeaderCache

NewLFUBlockCache creates a new LFU cache with the given capacity.

func (*LFUHeaderCache) Get

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

Get retrieves a header from the cache.

func (*LFUHeaderCache) Set

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 LoggingTransport

type LoggingTransport struct {
	Transport http.RoundTripper
}

LoggingTransport is a custom transport to log requests and responses

func (*LoggingTransport) RoundTrip

func (t *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements the RoundTripper interface

type Network

type Network struct {
	Name                         string    `toml:"name"`
	URLs                         []string  `toml:"urls_secret"`
	ChainID                      uint64    `toml:"chain_id"`
	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"`
	DialTimeout                  *Duration `toml:"dial_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

type NonceStatus struct {
	LastNonce    uint64
	PendingNonce uint64
}

type Pragma

type Pragma struct {
	Minor uint64
	Major uint64
	Patch uint64
}

Pragma represents the version of the Solidity compiler used to compile the contract

func DecodePragmaVersion

func DecodePragmaVersion(bytecode string) (Pragma, error)

DecodePragmaVersion extracts the pragma version from the bytecode or returns an error if it's not found or can't be decoded. Based on https://www.rareskills.io/post/solidity-metadata

func (Pragma) String

func (p Pragma) String() string

String returns the string representation of the Pragma

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

	ABIFinder *ABIFinder
	// contains filtered or unexported fields
}

func NewTracer

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

func (*Tracer) AddDecodedCalls

func (t *Tracer) AddDecodedCalls(txHash string, calls []*DecodedCall)

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) GetAllDecodedCalls

func (t *Tracer) GetAllDecodedCalls() map[string][]*DecodedCall

func (*Tracer) GetDecodedCalls

func (t *Tracer) GetDecodedCalls(txHash string) []*DecodedCall

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) ([]*DecodedCall, error)

type TransactOpt

type TransactOpt func(o *bind.TransactOpts)

TransactOpt is a wrapper for bind.TransactOpts

func WithFrom

func WithFrom(fromAddress common.Address) TransactOpt

WithFrom sets from option 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 WithSignerFn

func WithSignerFn(signerFn bind.SignerFn) TransactOpt

WithSignerFn sets signerFn 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

Jump to

Keyboard shortcuts

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