Skip to content

Commit 698de75

Browse files
fix(contracts): remove ExceededMaxRespondFee error (#1407)
Co-authored-by: Urix <[email protected]>
1 parent 0602e6d commit 698de75

File tree

10 files changed

+378
-103
lines changed

10 files changed

+378
-103
lines changed

aggregator/pkg/aggregator.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ type Aggregator struct {
9898
func NewAggregator(aggregatorConfig config.AggregatorConfig) (*Aggregator, error) {
9999
newBatchChan := make(chan *servicemanager.ContractAlignedLayerServiceManagerNewBatchV3)
100100

101+
logger := aggregatorConfig.BaseConfig.Logger
102+
103+
// Metrics
104+
reg := prometheus.NewRegistry()
105+
aggregatorMetrics := metrics.NewMetrics(aggregatorConfig.Aggregator.MetricsIpPortAddress, reg, logger)
106+
107+
// Telemetry
108+
aggregatorTelemetry := NewTelemetry(aggregatorConfig.Aggregator.TelemetryIpPortAddress, logger)
109+
101110
avsReader, err := chainio.NewAvsReaderFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig)
102111
if err != nil {
103112
return nil, err
@@ -108,7 +117,7 @@ func NewAggregator(aggregatorConfig config.AggregatorConfig) (*Aggregator, error
108117
return nil, err
109118
}
110119

111-
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig)
120+
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig, aggregatorMetrics)
112121
if err != nil {
113122
return nil, err
114123
}
@@ -129,7 +138,6 @@ func NewAggregator(aggregatorConfig config.AggregatorConfig) (*Aggregator, error
129138

130139
aggregatorPrivateKey := aggregatorConfig.EcdsaConfig.PrivateKey
131140

132-
logger := aggregatorConfig.BaseConfig.Logger
133141
clients, err := sdkclients.BuildAll(chainioConfig, aggregatorPrivateKey, logger)
134142
if err != nil {
135143
logger.Errorf("Cannot create sdk clients", "err", err)
@@ -155,13 +163,6 @@ func NewAggregator(aggregatorConfig config.AggregatorConfig) (*Aggregator, error
155163
avsRegistryService := avsregistry.NewAvsRegistryServiceChainCaller(avsReader.ChainReader, operatorPubkeysService, logger)
156164
blsAggregationService := blsagg.NewBlsAggregatorService(avsRegistryService, hashFunction, logger)
157165

158-
// Metrics
159-
reg := prometheus.NewRegistry()
160-
aggregatorMetrics := metrics.NewMetrics(aggregatorConfig.Aggregator.MetricsIpPortAddress, reg, logger)
161-
162-
// Telemetry
163-
aggregatorTelemetry := NewTelemetry(aggregatorConfig.Aggregator.TelemetryIpPortAddress, logger)
164-
165166
nextBatchIndex := uint32(0)
166167

167168
aggregator := Aggregator{

contracts/scripts/anvil/state/alignedlayer-deployed-anvil-state.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

contracts/src/core/AlignedLayerServiceManager.sol

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,20 +214,18 @@ contract AlignedLayerServiceManager is
214214
// 70k was measured by trial and error until the aggregator got paid a bit over what it needed
215215
uint256 txCost = (initialGasLeft - gasleft() + 70_000) * tx.gasprice;
216216

217-
if (txCost > currentBatch.respondToTaskFeeLimit) {
218-
revert ExceededMaxRespondFee(
219-
currentBatch.respondToTaskFeeLimit,
220-
txCost
221-
);
222-
}
217+
// limit amount to spend is respondToTaskFeeLimit
218+
uint256 transferAmount = txCost < currentBatch.respondToTaskFeeLimit ?
219+
txCost : currentBatch.respondToTaskFeeLimit;
220+
221+
batchersBalances[senderAddress] -= transferAmount;
223222

224-
// Subtract the txCost from the batcher's balance
225-
batchersBalances[senderAddress] -= txCost;
226223
emit BatcherBalanceUpdated(
227224
senderAddress,
228225
batchersBalances[senderAddress]
229226
);
230-
payable(alignedAggregator).transfer(txCost);
227+
228+
payable(alignedAggregator).transfer(transferAmount);
231229
}
232230

233231
function isVerifierDisabled(

contracts/src/core/IAlignedLayerServiceManager.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ interface IAlignedLayerServiceManager {
3535
error InvalidQuorumThreshold(uint256 signedStake, uint256 requiredStake); // a61eb88a
3636
error SenderIsNotAggregator(address sender, address alignedAggregator); // 2cbe4195
3737
error InvalidDepositAmount(uint256 amount); // 412ed242
38-
error ExceededMaxRespondFee(uint256 respondToTaskFeeLimit, uint256 txCost); // 86fc507e
3938
error InvalidAddress(string param); // 161eb542
4039

4140
function createNewTask(

core/chainio/avs_writer.go

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
retry "github.com/yetanotherco/aligned_layer/core"
1919
"github.com/yetanotherco/aligned_layer/core/config"
2020
"github.com/yetanotherco/aligned_layer/core/utils"
21+
"github.com/yetanotherco/aligned_layer/metrics"
2122
)
2223

2324
type AvsWriter struct {
@@ -27,9 +28,10 @@ type AvsWriter struct {
2728
Signer signer.Signer
2829
Client eth.InstrumentedClient
2930
ClientFallback eth.InstrumentedClient
31+
metrics *metrics.Metrics
3032
}
3133

32-
func NewAvsWriterFromConfig(baseConfig *config.BaseConfig, ecdsaConfig *config.EcdsaConfig) (*AvsWriter, error) {
34+
func NewAvsWriterFromConfig(baseConfig *config.BaseConfig, ecdsaConfig *config.EcdsaConfig, metrics *metrics.Metrics) (*AvsWriter, error) {
3335

3436
buildAllConfig := clients.BuildAllConfig{
3537
EthHttpUrl: baseConfig.EthRpcUrl,
@@ -69,6 +71,7 @@ func NewAvsWriterFromConfig(baseConfig *config.BaseConfig, ecdsaConfig *config.E
6971
Signer: privateKeySigner,
7072
Client: baseConfig.EthRpcClient,
7173
ClientFallback: baseConfig.EthRpcClientFallback,
74+
metrics: metrics,
7275
}, nil
7376
}
7477

@@ -83,11 +86,6 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe
8386
return nil, err
8487
}
8588

86-
err = w.checkRespondToTaskFeeLimit(tx, txOpts, batchIdentifierHash, senderAddress)
87-
if err != nil {
88-
return nil, err
89-
}
90-
9189
// Set the nonce, as we might have to replace the transaction with a higher gas price
9290
txNonce := big.NewInt(int64(tx.Nonce()))
9391
txOpts.Nonce = txNonce
@@ -114,7 +112,9 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe
114112
onGasPriceBumped(txOpts.GasPrice)
115113
}
116114

117-
err = w.checkRespondToTaskFeeLimit(tx, txOpts, batchIdentifierHash, senderAddress)
115+
// We compare both Aggregator funds and Batcher balance in Aligned against respondToTaskFeeLimit
116+
// Both are required to have some balance, more details inside the function
117+
err = w.checkAggAndBatcherHaveEnoughBalance(tx, txOpts, batchIdentifierHash, senderAddress)
118118
if err != nil {
119119
return nil, retry.PermanentError{Inner: err}
120120
}
@@ -128,6 +128,7 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe
128128

129129
receipt, err := utils.WaitForTransactionReceiptRetryable(w.Client, w.ClientFallback, tx.Hash(), timeToWaitBeforeBump)
130130
if receipt != nil {
131+
w.checkIfAggregatorHadToPaidForBatcher(tx, batchIdentifierHash)
131132
return receipt, nil
132133
}
133134

@@ -145,29 +146,45 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe
145146
return retry.RetryWithData(respondToTaskV2Func, retry.MinDelay, retry.RetryFactor, 0, retry.MaxInterval, 0)
146147
}
147148

148-
func (w *AvsWriter) checkRespondToTaskFeeLimit(tx *types.Transaction, txOpts bind.TransactOpts, batchIdentifierHash [32]byte, senderAddress [20]byte) error {
149-
aggregatorAddress := txOpts.From
150-
simulatedCost := new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice())
151-
w.logger.Info("Simulated cost", "cost", simulatedCost)
152-
153-
// Get RespondToTaskFeeLimit
149+
// Calculates the transaction cost from the receipt and compares it with the batcher respondToTaskFeeLimit
150+
// if the tx cost was higher, then it means the aggregator has paid the difference for the batcher (txCost - respondToTaskFeeLimit) and so metrics are updated accordingly.
151+
// otherwise nothing is done.
152+
func (w *AvsWriter) checkIfAggregatorHadToPaidForBatcher(tx *types.Transaction, batchIdentifierHash [32]byte) {
154153
batchState, err := w.BatchesStateRetryable(&bind.CallOpts{}, batchIdentifierHash)
155154
if err != nil {
156-
// Fallback also failed
157-
// Proceed to check values against simulated costs
158-
w.logger.Error("Failed to get batch state", "error", err)
159-
w.logger.Info("Proceeding with simulated cost checks")
160-
return w.compareBalances(simulatedCost, aggregatorAddress, senderAddress)
155+
return
161156
}
162-
// At this point, batchState was successfully retrieved
163-
// Proceed to check values against RespondToTaskFeeLimit
164157
respondToTaskFeeLimit := batchState.RespondToTaskFeeLimit
165-
w.logger.Info("Batch RespondToTaskFeeLimit", "RespondToTaskFeeLimit", respondToTaskFeeLimit)
166158

167-
if respondToTaskFeeLimit.Cmp(simulatedCost) < 0 {
168-
return fmt.Errorf("cost of transaction is higher than Batch.RespondToTaskFeeLimit")
159+
// NOTE we are not using tx.Cost() because tx.Cost() includes tx.Value()
160+
txCost := new(big.Int).Mul(big.NewInt(int64(tx.Gas())), tx.GasPrice())
161+
162+
if respondToTaskFeeLimit.Cmp(txCost) < 0 {
163+
aggregatorDifferencePaid := new(big.Int).Sub(txCost, respondToTaskFeeLimit)
164+
aggregatorDifferencePaidInEth := utils.WeiToEth(aggregatorDifferencePaid)
165+
w.metrics.AddAggregatorGasPaidForBatcher(aggregatorDifferencePaidInEth)
166+
w.metrics.IncAggregatorPaidForBatcher()
167+
w.logger.Warnf("cost of transaction was higher than Batch.RespondToTaskFeeLimit, aggregator has paid the for the difference, aprox: %vethers", aggregatorDifferencePaidInEth)
169168
}
169+
}
170+
171+
func (w *AvsWriter) checkAggAndBatcherHaveEnoughBalance(tx *types.Transaction, txOpts bind.TransactOpts, batchIdentifierHash [32]byte, senderAddress [20]byte) error {
172+
w.logger.Info("Checking if aggregator and batcher have enough balance for the transaction")
173+
aggregatorAddress := txOpts.From
174+
txCost := new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), txOpts.GasPrice)
175+
w.logger.Info("Transaction cost", "cost", txCost)
170176

177+
batchState, err := w.BatchesStateRetryable(&bind.CallOpts{}, batchIdentifierHash)
178+
if err != nil {
179+
w.logger.Error("Failed to get batch state", "error", err)
180+
w.logger.Info("Proceeding to check balances against transaction cost")
181+
return w.compareBalances(txCost, aggregatorAddress, senderAddress)
182+
}
183+
respondToTaskFeeLimit := batchState.RespondToTaskFeeLimit
184+
w.logger.Info("Checking balance against Batch RespondToTaskFeeLimit", "RespondToTaskFeeLimit", respondToTaskFeeLimit)
185+
// Note: we compare both Aggregator funds and Batcher balance in Aligned against respondToTaskFeeLimit
186+
// Batcher will pay up to respondToTaskFeeLimit, for this he needs that amount of funds in Aligned
187+
// Aggregator will pay any extra cost, for this he needs at least respondToTaskFeeLimit in his balance
171188
return w.compareBalances(respondToTaskFeeLimit, aggregatorAddress, senderAddress)
172189
}
173190

core/retry_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ func TestRespondToTaskV2(t *testing.T) {
681681
}
682682

683683
aggregatorConfig := config.NewAggregatorConfig("../config-files/config-aggregator-test.yaml")
684-
w, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig)
684+
w, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig, nil)
685685
if err != nil {
686686
t.Errorf("Error killing process: %v\n", err)
687687
return
@@ -734,7 +734,7 @@ func TestBatchesStateWriter(t *testing.T) {
734734
}
735735

736736
aggregatorConfig := config.NewAggregatorConfig("../config-files/config-aggregator-test.yaml")
737-
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig)
737+
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig, nil)
738738
if err != nil {
739739
t.Errorf("Error killing process: %v\n", err)
740740
return
@@ -784,12 +784,12 @@ func TestBalanceAt(t *testing.T) {
784784
}
785785

786786
aggregatorConfig := config.NewAggregatorConfig("../config-files/config-aggregator-test.yaml")
787-
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig)
787+
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig, nil)
788788
if err != nil {
789789
return
790790
}
791791
aggregator_address := common.HexToAddress("0x0")
792-
blockHeight := big.NewInt(13)
792+
blockHeight := big.NewInt(22)
793793

794794
_, err = avsWriter.BalanceAtRetryable(context.Background(), aggregator_address, blockHeight)
795795
assert.Nil(t, err)
@@ -831,7 +831,7 @@ func TestBatchersBalances(t *testing.T) {
831831
}
832832

833833
aggregatorConfig := config.NewAggregatorConfig("../config-files/config-aggregator-test.yaml")
834-
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig)
834+
avsWriter, err := chainio.NewAvsWriterFromConfig(aggregatorConfig.BaseConfig, aggregatorConfig.EcdsaConfig, nil)
835835
if err != nil {
836836
return
837837
}

core/utils/eth_client_utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ func BytesToQuorumThresholdPercentages(quorumThresholdPercentagesBytes []byte) e
5151
return quorumThresholdPercentages
5252
}
5353

54+
func WeiToEth(wei *big.Int) float64 {
55+
weiToEth := new(big.Float).SetFloat64(1e18)
56+
weiFloat := new(big.Float).SetInt(wei)
57+
58+
result := new(big.Float).Quo(weiFloat, weiToEth)
59+
eth, _ := result.Float64()
60+
61+
return eth
62+
}
63+
5464
// Simple algorithm to calculate the gasPrice bump based on:
5565
// the currentGasPrice, a base bump percentage, a retry percentage, and the retry count.
5666
// Formula: currentGasPrice + (currentGasPrice * (baseBumpPercentage + retryCount * incrementalRetryPercentage) / 100)

0 commit comments

Comments
 (0)