Skip to content

Commit

Permalink
add fluent waiting for pending transactions (#1100)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tofel authored Aug 28, 2024
1 parent 9ea5d6a commit c852ecd
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 10 deletions.
8 changes: 7 additions & 1 deletion seth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ client, err := builder.
// tracing
WithTracing(seth.TracingLevel_All, []string{seth.TraceOutput_Console}).
// protections
WithProtections(true, true).
WithProtections(true, true, seth.MustMakeDuration(2*time.Minute)).
// artifacts folder
WithArtifactsFolder("some_folder").
// nonce manager
Expand Down Expand Up @@ -319,6 +319,12 @@ 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:
```toml
pending_nonce_protection_enabled = true
pending_nonce_protection_timeout = "5m"
```

You can add more networks like this:

```toml
Expand Down
8 changes: 6 additions & 2 deletions seth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ func ValidateConfig(cfg *Config) error {
cfg.Network.DialTimeout = &Duration{D: DefaultDialTimeout}
}

if cfg.PendingNonceProtectionTimeout == nil {
cfg.PendingNonceProtectionTimeout = &Duration{D: DefaultPendingNonceProtectionTimeout}
}

return nil
}

Expand Down Expand Up @@ -752,7 +756,7 @@ func (m *Client) getProposedTransactionOptions(keyNum int) (*bind.TransactOpts,
var ctx context.Context

if m.Cfg.PendingNonceProtectionEnabled {
if nonceStatus.PendingNonce > nonceStatus.LastNonce {
if pendingErr := m.WaitUntilNoPendingTxForKeyNum(keyNum, m.Cfg.PendingNonceProtectionTimeout.Duration()); pendingErr != nil {
errMsg := `
pending nonce for key %d is higher than last nonce, there are %d pending transactions.
Expand All @@ -762,7 +766,7 @@ This issue is caused by one of two things:
`
err := fmt.Errorf(errMsg, keyNum, nonceStatus.PendingNonce-nonceStatus.LastNonce)
m.Errors = append(m.Errors, err)
// can't return nil, otherwise RPC wrapper will panic and we might lose funds on testnets/mainnets, that's why
// can't return nil, otherwise RPC wrapper will panic, and we might lose funds on testnets/mainnets, that's why
// error is passed in Context here to avoid panic, whoever is using Seth should make sure that there is no error
// present in Context before using *bind.TransactOpts
ctx = context.WithValue(context.Background(), ContextErrorKey{}, err)
Expand Down
7 changes: 4 additions & 3 deletions seth/client_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,12 @@ func (c *ClientBuilder) WithTracing(level string, outputs []string) *ClientBuild
return c
}

// 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 and true for node health check.
func (c *ClientBuilder) WithProtections(pendingNonceProtectionEnabled, nodeHealthStartupCheck bool) *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 (c *ClientBuilder) WithProtections(pendingNonceProtectionEnabled, nodeHealthStartupCheck bool, pendingNonceProtectionTimeout *Duration) *ClientBuilder {
c.config.PendingNonceProtectionEnabled = pendingNonceProtectionEnabled
c.config.CheckRpcHealthOnStart = nodeHealthStartupCheck
c.config.PendingNonceProtectionTimeout = pendingNonceProtectionTimeout
return c
}

Expand Down
6 changes: 4 additions & 2 deletions seth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ const (
NETWORK_ENV_VAR = "SETH_NETWORK"
URL_ENV_VAR = "SETH_URL"

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

DefaultTransferGasFee = 21_000
DefaultGasPrice = 1_000_000_000 // 1 Gwei
Expand Down Expand Up @@ -59,6 +60,7 @@ type Config struct {
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"`
Expand Down
2 changes: 1 addition & 1 deletion seth/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func TestConfig_MaximalBuilder(t *testing.T) {
// tracing
WithTracing(seth.TracingLevel_All, []string{seth.TraceOutput_Console}).
// protections
WithProtections(true, true).
WithProtections(true, true, seth.MustMakeDuration(2*time.Minute)).
// artifacts folder
WithArtifactsFolder("some_folder").
// nonce manager
Expand Down
2 changes: 1 addition & 1 deletion seth/gas_bump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func TestGasBumping_Contract_Deployment_Legacy_CustomBumpingFunction(t *testing.
WithEIP1559DynamicFees(false).
WithLegacyGasPrice(1).
WithTransactionTimeout(10*time.Second).
WithProtections(false, false).
WithProtections(false, false, nil).
WithGasBumping(5, 0, func(gasPrice *big.Int) *big.Int {
customGasBumps++
return new(big.Int).Mul(gasPrice, big.NewInt(512))
Expand Down
4 changes: 4 additions & 0 deletions seth/seth.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ ephemeral_addresses_number = 0
# it when running load tests.
pending_nonce_protection_enabled = false

# When pending_nonce_protection_enabled is set to true, this value will be used to determine how long we should wait
# for all pending transactions to be mined (so that pending nonce and last nonce are equal).
# pending_nonce_protection_timeout = "10s"

# Amount to be left on root key/address, when we are using ephemeral addresses. It's the amount that will not
# be divided into ephemeral keys.
root_key_funds_buffer = 10 # 10 ether
Expand Down

0 comments on commit c852ecd

Please sign in to comment.