Skip to content

Commit a681973

Browse files
committed
refactor: use GeometricTxManager
1 parent a2364dc commit a681973

2 files changed

Lines changed: 186 additions & 121 deletions

File tree

core/chainio/avs_writer.go

Lines changed: 182 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"encoding/hex"
66
"fmt"
77
"github.com/Layr-Labs/eigensdk-go/chainio/clients/wallet"
8-
"github.com/Layr-Labs/eigensdk-go/chainio/txmgr"
8+
"github.com/Layr-Labs/eigensdk-go/chainio/txmgr/geometric"
99
"math/big"
1010
"time"
1111

@@ -27,8 +27,8 @@ type AvsWriter struct {
2727
*avsregistry.ChainWriter
2828
AvsContractBindings *AvsServiceBindings
2929
logger logging.Logger
30-
TxManager txmgr.SimpleTxManager
31-
TxManagerFallback txmgr.SimpleTxManager
30+
TxManager geometric.GeometricTxManager
31+
TxManagerFallback geometric.GeometricTxManager
3232
Client eth.InstrumentedClient
3333
ClientFallback eth.InstrumentedClient
3434
metrics *metrics.Metrics
@@ -46,29 +46,70 @@ func NewAvsWriterFromConfig(baseConfig *config.BaseConfig, ecdsaConfig *config.E
4646
ServiceManagerAddress: baseConfig.AlignedLayerDeploymentConfig.AlignedLayerServiceManagerAddr.String(),
4747
}
4848

49-
clients, err := clients.BuildAll(buildAllConfig, ecdsaConfig.PrivateKey, baseConfig.Logger)
50-
49+
clients, err := clients.BuildAll(
50+
buildAllConfig,
51+
ecdsaConfig.PrivateKey,
52+
baseConfig.Logger,
53+
)
5154
if err != nil {
5255
baseConfig.Logger.Error("Cannot build signer config", "err", err)
5356
return nil, err
5457
}
5558

56-
avsServiceBindings, err := NewAvsServiceBindings(baseConfig.AlignedLayerDeploymentConfig.AlignedLayerServiceManagerAddr, baseConfig.AlignedLayerDeploymentConfig.AlignedLayerOperatorStateRetrieverAddr, baseConfig.EthRpcClient, baseConfig.EthRpcClientFallback, baseConfig.Logger)
57-
59+
avsServiceBindings, err := NewAvsServiceBindings(
60+
baseConfig.AlignedLayerDeploymentConfig.AlignedLayerServiceManagerAddr,
61+
baseConfig.AlignedLayerDeploymentConfig.AlignedLayerOperatorStateRetrieverAddr,
62+
baseConfig.EthRpcClient,
63+
baseConfig.EthRpcClientFallback,
64+
baseConfig.Logger,
65+
)
5866
if err != nil {
5967
baseConfig.Logger.Error("Cannot create avs service bindings", "err", err)
6068
return nil, err
6169
}
6270

63-
privateKeyWallet, err := wallet.NewPrivateKeyWallet(&baseConfig.EthRpcClient, ecdsaConfig.SignerFn, ecdsaConfig.Address, baseConfig.Logger)
71+
privateKeyWallet, err := wallet.NewPrivateKeyWallet(
72+
&baseConfig.EthRpcClient,
73+
ecdsaConfig.SignerFn,
74+
ecdsaConfig.Address,
75+
baseConfig.Logger,
76+
)
6477
if err != nil {
6578
baseConfig.Logger.Error("Cannot create private key wallet", "err", err)
6679
return nil, err
6780
}
6881

69-
txManager := txmgr.NewSimpleTxManager(privateKeyWallet, &baseConfig.EthRpcClient, baseConfig.Logger, ecdsaConfig.Address)
82+
privateKeyWalletFallback, err := wallet.NewPrivateKeyWallet(
83+
&baseConfig.EthRpcClientFallback,
84+
ecdsaConfig.SignerFn,
85+
ecdsaConfig.Address,
86+
baseConfig.Logger,
87+
)
88+
if err != nil {
89+
baseConfig.Logger.Error("Cannot create private key wallet", "err", err)
90+
return nil, err
91+
}
7092

71-
txManagerFallback := txmgr.NewSimpleTxManager(privateKeyWallet, &baseConfig.EthRpcClientFallback, baseConfig.Logger, ecdsaConfig.Address)
93+
geometricTxnManagerParams := geometric.GeometricTxnManagerParams{}
94+
95+
//txManager := txmgr.NewSimpleTxManager(privateKeyWallet, &baseConfig.EthRpcClient, baseConfig.Logger, ecdsaConfig.Address)
96+
//
97+
//txManagerFallback := txmgr.NewSimpleTxManager(privateKeyWallet, &baseConfig.EthRpcClientFallback, baseConfig.Logger, ecdsaConfig.Address)
98+
txManager := geometric.NewGeometricTxnManager(
99+
&baseConfig.EthRpcClient,
100+
privateKeyWallet,
101+
baseConfig.Logger,
102+
geometric.NewNoopMetrics(), // TODO: Set a correct metrics instance
103+
geometricTxnManagerParams,
104+
)
105+
106+
txManagerFallback := geometric.NewGeometricTxnManager(
107+
&baseConfig.EthRpcClientFallback,
108+
privateKeyWalletFallback,
109+
baseConfig.Logger,
110+
geometric.NewNoopMetrics(), // TODO: Set a correct metrics instance
111+
geometricTxnManagerParams,
112+
)
72113

73114
chainWriter := clients.AvsRegistryChainWriter
74115

@@ -99,122 +140,145 @@ func NewAvsWriterFromConfig(baseConfig *config.BaseConfig, ecdsaConfig *config.E
99140
// - An error if the process encounters a fatal issue (e.g., permanent failure in verifying balances or state).
100141
func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMerkleRoot [32]byte, senderAddress [20]byte, nonSignerStakesAndSignature servicemanager.IBLSSignatureCheckerNonSignerStakesAndSignature, gasBumpPercentage uint, gasBumpIncrementalPercentage uint, gasBumpPercentageLimit uint, timeToWaitBeforeBump time.Duration, metrics *metrics.Metrics, onSetGasPrice func(*big.Int)) (*types.Receipt, error) {
101142
txOpts, err := w.TxManager.GetNoSendTxOpts()
102-
txOpts.NoSend = true // simulate the transaction
103-
simTx, err := w.RespondToTaskV2Retryable(txOpts, batchMerkleRoot, senderAddress, nonSignerStakesAndSignature, retry.SendToChainRetryParams())
104143
if err != nil {
105-
w.logger.Errorf("Failed to simulate transaction: %v", err)
144+
w.logger.Errorf("Failed to get transaction options: %v", err)
106145
return nil, err
107146
}
108-
w.logger.Infof("Simulated transaction: %v", simTx.Hash().Hex())
109-
110-
// Set the nonce, as we might have to replace the transaction with a higher gas price
111-
txNonce := big.NewInt(int64(simTx.Nonce()))
112-
txOpts.Nonce = txNonce
113-
txOpts.GasPrice = nil
114-
txOpts.NoSend = false
115-
i := 0
116-
117-
var sentTxs []*types.Transaction
118147

119148
batchMerkleRootHashString := hex.EncodeToString(batchMerkleRoot[:])
120149

121-
respondToTaskV2Func := func() (*types.Receipt, error) {
122-
gasPrice, err := utils.GetGasPriceRetryable(w.Client, w.ClientFallback, retry.NetworkRetryParams())
123-
if err != nil {
124-
return nil, err
125-
}
126-
127-
// if txOpts.GasPrice wasn't previously set use the fetched gasPrice
128-
// this should happen on the first iteration only
129-
var previousTxGasPrice *big.Int
130-
if txOpts.GasPrice == nil {
131-
previousTxGasPrice = gasPrice
132-
} else {
133-
previousTxGasPrice = txOpts.GasPrice
134-
}
135-
136-
// in order to avoid replacement transaction underpriced
137-
// the bumped gas price has to be at least 10% higher than the previous one.
138-
minimumGasPriceBump := utils.CalculateGasPriceBumpBasedOnRetry(previousTxGasPrice, 10, 0, gasBumpPercentageLimit, 0)
139-
suggestedBumpedGasPrice := utils.CalculateGasPriceBumpBasedOnRetry(
140-
gasPrice,
141-
gasBumpPercentage,
142-
gasBumpIncrementalPercentage,
143-
gasBumpPercentageLimit,
144-
i,
145-
)
146-
// check the new gas price is sufficiently bumped.
147-
// if the suggested bump does not meet the minimum threshold, use a fallback calculation to slightly increment the previous gas price.
148-
if suggestedBumpedGasPrice.Cmp(minimumGasPriceBump) > 0 {
149-
txOpts.GasPrice = suggestedBumpedGasPrice
150-
} else {
151-
txOpts.GasPrice = minimumGasPriceBump
152-
}
153-
154-
onSetGasPrice(txOpts.GasPrice)
155-
156-
if i > 0 {
157-
w.logger.Infof("Trying to get old sent transaction receipt before sending a new transaction", "merkle root", batchMerkleRootHashString)
158-
for _, tx := range sentTxs {
159-
receipt, _ := w.Client.TransactionReceipt(context.Background(), tx.Hash())
160-
if receipt == nil {
161-
receipt, _ = w.ClientFallback.TransactionReceipt(context.Background(), tx.Hash())
162-
if receipt != nil {
163-
w.updateAggregatorGasCostMetrics(receipt, batchIdentifierHash)
164-
return receipt, nil
165-
}
166-
}
167-
}
168-
w.logger.Infof("Receipts for old transactions not found, will check if the batch state has been responded", "merkle root", batchMerkleRootHashString)
169-
batchState, _ := w.BatchesStateRetryable(&bind.CallOpts{}, batchIdentifierHash, retry.NetworkRetryParams())
170-
if batchState.Responded {
171-
w.logger.Infof("Batch state has been already responded", "merkle root", batchMerkleRootHashString)
172-
return nil, nil
173-
}
174-
w.logger.Infof("Batch state has not been responded yet, will send a new tx", "merkle root", batchMerkleRootHashString)
175-
176-
metrics.IncBumpedGasPriceForAggregatedResponse()
177-
}
178-
179-
// We compare both Aggregator funds and Batcher balance in Aligned against respondToTaskFeeLimit
180-
// Both are required to have some balance, more details inside the function
181-
err = w.checkAggAndBatcherHaveEnoughBalance(simTx, *txOpts, batchIdentifierHash, senderAddress)
182-
if err != nil {
183-
w.logger.Errorf("Permanent error when checking aggregator and batcher balances, err %v", err, "merkle root", batchMerkleRootHashString)
184-
return nil, retry.PermanentError{Inner: err}
185-
}
186-
187-
w.logger.Infof("Sending RespondToTask transaction with a gas price of %v", txOpts.GasPrice, "merkle root", batchMerkleRootHashString)
188-
//realTx, err := w.RespondToTaskV2Retryable(txOpts, batchMerkleRoot, senderAddress, quorumNums, nonSignerStakesAndSignature, retry.SendToChainRetryParams())
189-
receipt, err := w.SendTransactionRetryable(context.Background(), simTx, false, retry.SendToChainRetryParams())
190-
if err != nil {
191-
w.logger.Errorf("Respond to task transaction err, %v", err, "merkle root", batchMerkleRootHashString)
192-
return nil, err
193-
}
194-
sentTxs = append(sentTxs, simTx)
195-
196-
w.logger.Infof("Transaction sent, waiting for receipt", "merkle root", batchMerkleRootHashString)
197-
receipt, err = utils.WaitForTransactionReceiptRetryable(w.Client, w.ClientFallback, receipt.TxHash, retry.WaitForTxRetryParams(timeToWaitBeforeBump))
198-
if receipt != nil {
199-
w.updateAggregatorGasCostMetrics(receipt, batchIdentifierHash)
200-
return receipt, nil
201-
}
202-
203-
// if we are here, it means we have reached the receipt waiting timeout
204-
// we increment the i here to add an incremental percentage to increase the odds of being included in the next blocks
205-
i++
206-
207-
w.logger.Infof("RespondToTask receipt waiting timeout has passed, will try again...", "merkle_root", batchMerkleRootHashString)
208-
if err != nil {
209-
return nil, err
210-
}
211-
return nil, fmt.Errorf("transaction failed")
150+
tx, err := w.RespondToTaskV2Retryable(txOpts, batchMerkleRoot, senderAddress, nonSignerStakesAndSignature, retry.SendToChainRetryParams())
151+
if err != nil {
152+
w.logger.Errorf("Failed to simulate transaction: %v", err)
153+
return nil, err
212154
}
213155

214-
// This just retries the bump of a fee in case of a timeout
215-
// The wait is done before on WaitForTransactionReceiptRetryable, and all the functions are retriable,
216-
// so this retry doesn't need to wait more time
217-
return retry.RetryWithData(respondToTaskV2Func, retry.RespondToTaskV2())
156+
w.logger.Infof("Sending RespondToTask transaction (%v) for MerkleRoot %v", tx.Hash().Hex(), batchMerkleRootHashString)
157+
receipt, err := w.SendTransactionRetryable(context.Background(), tx, retry.SendToChainRetryParams())
158+
if err != nil {
159+
w.logger.Errorf("RespondToTask transaction (%v) for MerkleRoot %v error: %v", tx.Hash().Hex(), batchMerkleRootHashString, err)
160+
return nil, err
161+
}
162+
w.logger.Infof("RespondToTask transaction (%v) sent for MerkleRoot %v. %+v", tx.Hash().Hex(), batchMerkleRootHashString, receipt)
163+
return receipt, nil
164+
// TODO DELETE This commented code
165+
//txOpts, err := w.TxManager.GetNoSendTxOpts()
166+
//txOpts.NoSend = true // simulate the transaction
167+
//simTx, err := w.RespondToTaskV2Retryable(txOpts, batchMerkleRoot, senderAddress, nonSignerStakesAndSignature, retry.SendToChainRetryParams())
168+
//if err != nil {
169+
// w.logger.Errorf("Failed to simulate transaction: %v", err)
170+
// return nil, err
171+
//}
172+
//w.logger.Infof("Simulated transaction: %v", simTx.Hash().Hex())
173+
//
174+
//// Set the nonce, as we might have to replace the transaction with a higher gas price
175+
//txNonce := big.NewInt(int64(simTx.Nonce()))
176+
//txOpts.Nonce = txNonce
177+
//txOpts.GasPrice = nil
178+
//txOpts.NoSend = false
179+
//i := 0
180+
//
181+
//var sentTxs []*types.Transaction
182+
//
183+
//batchMerkleRootHashString := hex.EncodeToString(batchMerkleRoot[:])
184+
//
185+
//respondToTaskV2Func := func() (*types.Receipt, error) {
186+
// gasPrice, err := utils.GetGasPriceRetryable(w.Client, w.ClientFallback, retry.NetworkRetryParams())
187+
// if err != nil {
188+
// return nil, err
189+
// }
190+
//
191+
// // if txOpts.GasPrice wasn't previously set use the fetched gasPrice
192+
// // this should happen on the first iteration only
193+
// var previousTxGasPrice *big.Int
194+
// if txOpts.GasPrice == nil {
195+
// previousTxGasPrice = gasPrice
196+
// } else {
197+
// previousTxGasPrice = txOpts.GasPrice
198+
// }
199+
//
200+
// // in order to avoid replacement transaction underpriced
201+
// // the bumped gas price has to be at least 10% higher than the previous one.
202+
// minimumGasPriceBump := utils.CalculateGasPriceBumpBasedOnRetry(previousTxGasPrice, 10, 0, gasBumpPercentageLimit, 0)
203+
// suggestedBumpedGasPrice := utils.CalculateGasPriceBumpBasedOnRetry(
204+
// gasPrice,
205+
// gasBumpPercentage,
206+
// gasBumpIncrementalPercentage,
207+
// gasBumpPercentageLimit,
208+
// i,
209+
// )
210+
// // check the new gas price is sufficiently bumped.
211+
// // if the suggested bump does not meet the minimum threshold, use a fallback calculation to slightly increment the previous gas price.
212+
// if suggestedBumpedGasPrice.Cmp(minimumGasPriceBump) > 0 {
213+
// txOpts.GasPrice = suggestedBumpedGasPrice
214+
// } else {
215+
// txOpts.GasPrice = minimumGasPriceBump
216+
// }
217+
//
218+
// onSetGasPrice(txOpts.GasPrice)
219+
//
220+
// if i > 0 {
221+
// w.logger.Infof("Trying to get old sent transaction receipt before sending a new transaction", "merkle root", batchMerkleRootHashString)
222+
// for _, tx := range sentTxs {
223+
// receipt, _ := w.Client.TransactionReceipt(context.Background(), tx.Hash())
224+
// if receipt == nil {
225+
// receipt, _ = w.ClientFallback.TransactionReceipt(context.Background(), tx.Hash())
226+
// if receipt != nil {
227+
// w.updateAggregatorGasCostMetrics(receipt, batchIdentifierHash)
228+
// return receipt, nil
229+
// }
230+
// }
231+
// }
232+
// w.logger.Infof("Receipts for old transactions not found, will check if the batch state has been responded", "merkle root", batchMerkleRootHashString)
233+
// batchState, _ := w.BatchesStateRetryable(&bind.CallOpts{}, batchIdentifierHash, retry.NetworkRetryParams())
234+
// if batchState.Responded {
235+
// w.logger.Infof("Batch state has been already responded", "merkle root", batchMerkleRootHashString)
236+
// return nil, nil
237+
// }
238+
// w.logger.Infof("Batch state has not been responded yet, will send a new tx", "merkle root", batchMerkleRootHashString)
239+
//
240+
// metrics.IncBumpedGasPriceForAggregatedResponse()
241+
// }
242+
//
243+
// // We compare both Aggregator funds and Batcher balance in Aligned against respondToTaskFeeLimit
244+
// // Both are required to have some balance, more details inside the function
245+
// err = w.checkAggAndBatcherHaveEnoughBalance(simTx, *txOpts, batchIdentifierHash, senderAddress)
246+
// if err != nil {
247+
// w.logger.Errorf("Permanent error when checking aggregator and batcher balances, err %v", err, "merkle root", batchMerkleRootHashString)
248+
// return nil, retry.PermanentError{Inner: err}
249+
// }
250+
//
251+
// w.logger.Infof("Sending RespondToTask transaction with a gas price of %v", txOpts.GasPrice, "merkle root", batchMerkleRootHashString)
252+
// //realTx, err := w.RespondToTaskV2Retryable(txOpts, batchMerkleRoot, senderAddress, quorumNums, nonSignerStakesAndSignature, retry.SendToChainRetryParams())
253+
// receipt, err := w.SendTransactionRetryable(context.Background(), simTx, false, retry.SendToChainRetryParams())
254+
// if err != nil {
255+
// w.logger.Errorf("Respond to task transaction err, %v", err, "merkle root", batchMerkleRootHashString)
256+
// return nil, err
257+
// }
258+
// sentTxs = append(sentTxs, simTx)
259+
//
260+
// w.logger.Infof("Transaction sent, waiting for receipt", "merkle root", batchMerkleRootHashString)
261+
// receipt, err = utils.WaitForTransactionReceiptRetryable(w.Client, w.ClientFallback, receipt.TxHash, retry.WaitForTxRetryParams(timeToWaitBeforeBump))
262+
// if receipt != nil {
263+
// w.updateAggregatorGasCostMetrics(receipt, batchIdentifierHash)
264+
// return receipt, nil
265+
// }
266+
//
267+
// // if we are here, it means we have reached the receipt waiting timeout
268+
// // we increment the i here to add an incremental percentage to increase the odds of being included in the next blocks
269+
// i++
270+
//
271+
// w.logger.Infof("RespondToTask receipt waiting timeout has passed, will try again...", "merkle_root", batchMerkleRootHashString)
272+
// if err != nil {
273+
// return nil, err
274+
// }
275+
// return nil, fmt.Errorf("transaction failed")
276+
//}
277+
//
278+
//// This just retries the bump of a fee in case of a timeout
279+
//// The wait is done before on WaitForTransactionReceiptRetryable, and all the functions are retriable,
280+
//// so this retry doesn't need to wait more time
281+
//return retry.RetryWithData(respondToTaskV2Func, retry.RespondToTaskV2())
218282
}
219283

220284
// Calculates the transaction cost from the receipt and updates the total amount paid by the aggregator metric

core/chainio/retryable.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ import (
1515

1616
// |---AVS_WRITER---|
1717

18-
func (w *AvsWriter) SendTransactionRetryable(ctx context.Context, tx *types.Transaction, waitForReceipt bool, config *retry.RetryParams) (*types.Receipt, error) {
18+
func (w *AvsWriter) SendTransactionRetryable(ctx context.Context, tx *types.Transaction, config *retry.RetryParams) (*types.Receipt, error) {
19+
// Note: waitForReceipt parameter is ignored in GeometricTxManager
1920
sendTransaction_func := func() (*types.Receipt, error) {
2021
// Try with main txManager
21-
receipt, err := w.TxManager.Send(ctx, tx, waitForReceipt)
22+
receipt, err := w.TxManager.Send(ctx, tx, true)
2223
if err != nil {
2324
// If error try with fallback txManager
24-
receipt, err = w.TxManagerFallback.Send(ctx, tx, waitForReceipt)
25+
receipt, err = w.TxManagerFallback.Send(ctx, tx, true)
2526
}
2627
return receipt, err
2728
}

0 commit comments

Comments
 (0)