Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .changeset/fresh-bushes-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
"chainlink-deployments-framework": patch
---

Adds a new EVM Confirm Functor which allows the user to specify a custom wait interval for checking confirmation.
Example

```golang
p, err := cldf_evm_provider.NewRPCChainProvider(
d.ChainSelector,
cldf_evm_provider.RPCChainProviderConfig{
DeployerTransactorGen: cldf_evm_provider.TransactorFromRaw(
getNetworkPrivateKey(),
),
RPCs: []rpcclient.RPC{
{
Name: "default",
WSURL: rpcWSURL,
HTTPURL: rpcHTTPURL,
PreferredURLScheme: rpcclient.URLSchemePreferenceHTTP,
},
},
ConfirmFunctor: cldf_evm_provider.ConfirmFuncGeth(
30*time.Second,
// set custom confirm ticker time because Anvil's blocks are instant
cldf_evm_provider.WithTickInterval(5*time.Millisecond),
),
},
).Initialize(context.Background())
```
37 changes: 34 additions & 3 deletions chain/evm/provider/confirm_functor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,29 @@ type ConfirmFunctor interface {
}

// ConfirmFuncGeth returns a ConfirmFunctor that uses the Geth client to confirm transactions.
func ConfirmFuncGeth(waitMinedTimeout time.Duration) ConfirmFunctor {
return &confirmFuncGeth{
func ConfirmFuncGeth(waitMinedTimeout time.Duration, opts ...func(*confirmFuncGeth)) ConfirmFunctor {
cf := &confirmFuncGeth{
tickInterval: 1 * time.Second, // the same value we have in bind.WaitMined hardcoded in "go-ethereum"
Comment thread
skudasov marked this conversation as resolved.
waitMinedTimeout: waitMinedTimeout,
}
for _, o := range opts {
o(cf)
}

return cf
}

// WithTickInterval specifies tick interval to confirm transaction is mined
func WithTickInterval(interval time.Duration) func(*confirmFuncGeth) {
return func(o *confirmFuncGeth) {
o.tickInterval = interval
}
}

// confirmFuncGeth implements the ConfirmFunctor interface which generates a confirmation function
// for transactions using the Geth client.
type confirmFuncGeth struct {
tickInterval time.Duration
waitMinedTimeout time.Duration
}

Expand All @@ -51,7 +65,7 @@ func (g *confirmFuncGeth) Generate(
ctxTimeout, cancel := context.WithTimeout(ctx, g.waitMinedTimeout)
defer cancel()

receipt, err := bind.WaitMined(ctxTimeout, client, tx)
receipt, err := waitMinedWithInterval(ctxTimeout, g.tickInterval, client, tx.Hash())
if err != nil {
return 0, fmt.Errorf("tx %s failed to confirm for selector %d: %w",
tx.Hash().Hex(), selector, err,
Expand Down Expand Up @@ -157,3 +171,20 @@ func (g *confirmFuncSeth) Generate(
return decoded.Receipt.BlockNumber.Uint64(), nil
}, nil
}

// waitMinedWithInterval is a custom function that allows to get receipts faster for networks with instant blocks
func waitMinedWithInterval(ctx context.Context, tick time.Duration, b bind.DeployBackend, txHash common.Hash) (*types.Receipt, error) {
queryTicker := time.NewTicker(tick)
defer queryTicker.Stop()
for {
receipt, err := b.TransactionReceipt(ctx, txHash)
Comment thread
skudasov marked this conversation as resolved.
if err == nil {
return receipt, nil
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-queryTicker.C:
}
}
}
62 changes: 35 additions & 27 deletions chain/evm/provider/confirm_functor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,39 +39,47 @@ func Test_ConfirmFuncGeth_ConfirmFunc(t *testing.T) {
adminTransactor.From: {Balance: prefundAmountWei},
}

tests := []struct {
name string
giveTx func(*testing.T, *SimClient) *types.Transaction
wantErr string
}{
{
name: "successful confirmation",
giveTx: func(t *testing.T, client *SimClient) *types.Transaction {
t.Helper()
defaultGiveTxFunc := func(t *testing.T, client *SimClient) *types.Transaction {
t.Helper()

// Get the nonce
nonce, err := client.PendingNonceAt(t.Context(), adminTransactor.From)
require.NoError(t, err)
// Get the nonce
nonce, err := client.PendingNonceAt(t.Context(), adminTransactor.From)
require.NoError(t, err)

gasPrice, err := client.SuggestGasPrice(t.Context())
require.NoError(t, err)
gasPrice, err := client.SuggestGasPrice(t.Context())
require.NoError(t, err)

// Create a transaction to send tokens. This will be used to test the confirmation function.
tx := types.NewTransaction(
nonce, userTransactor.From, big.NewInt(10000000000000000), 21000, gasPrice, nil,
)
// Create a transaction to send tokens. This will be used to test the confirmation function.
tx := types.NewTransaction(
nonce, userTransactor.From, big.NewInt(10000000000000000), 21000, gasPrice, nil,
)

signedTx, err := types.SignTx(tx, types.NewCancunSigner(simChainID), adminKey)
require.NoError(t, err, "failed to sign transaction")
signedTx, err := types.SignTx(tx, types.NewCancunSigner(simChainID), adminKey)
require.NoError(t, err, "failed to sign transaction")

// Send the transaction
err = client.SendTransaction(t.Context(), signedTx)
require.NoError(t, err)
// Send the transaction
err = client.SendTransaction(t.Context(), signedTx)
require.NoError(t, err)

client.Commit() // Commit the transaction to the simulated backend
client.Commit() // Commit the transaction to the simulated backend

return signedTx
},
return signedTx
}

tests := []struct {
name string
giveTx func(*testing.T, *SimClient) *types.Transaction
confirmerOpts []func(*confirmFuncGeth)
wantErr string
}{
{
name: "successful confirmation",
giveTx: defaultGiveTxFunc,
},
{
name: "successful confirmation with custom WaitMined ticker",
confirmerOpts: []func(*confirmFuncGeth){WithTickInterval(10 * time.Millisecond)},
giveTx: defaultGiveTxFunc,
},
{
name: "failed with nil tx",
Expand Down Expand Up @@ -121,7 +129,7 @@ func Test_ConfirmFuncGeth_ConfirmFunc(t *testing.T) {
tx := tt.giveTx(t, client)

// Generate the confirm function
functor := ConfirmFuncGeth(1 * time.Second)
functor := ConfirmFuncGeth(1*time.Second, tt.confirmerOpts...)
confirmFunc, err := functor.Generate(
t.Context(), chainsel.TEST_1000.Selector, client, adminTransactor.From,
)
Expand Down
Loading