Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/ethapi/api, accounts/abi/bind: Cap highest gas limit by account balance for 1559 fee parameters #23309

Merged
merged 5 commits into from Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 16 additions & 3 deletions accounts/abi/bind/backends/simulated.go
Expand Up @@ -488,8 +488,21 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
} else {
hi = b.pendingBlock.GasLimit()
}

// Normalize the max fee per gas the call is willing to spend.
var feeCap *big.Int
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
return 0, errors.New("both gasPrice and (gasFeeCap or gasTipCap) specified")
} else if call.GasPrice != nil {
feeCap = call.GasPrice
} else if call.GasFeeCap != nil {
feeCap = call.GasFeeCap
} else {
feeCap = common.Big0
}

// Recap the highest gas allowance with account's balance.
if call.GasPrice != nil && call.GasPrice.BitLen() != 0 {
if feeCap.BitLen() != 0 {
balance := b.pendingState.GetBalance(call.From) // from can't be nil
available := new(big.Int).Set(balance)
if call.Value != nil {
Expand All @@ -498,14 +511,14 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
}
available.Sub(available, call.Value)
}
allowance := new(big.Int).Div(available, call.GasPrice)
allowance := new(big.Int).Div(available, feeCap)
if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value
if transfer == nil {
transfer = new(big.Int)
}
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "gasprice", call.GasPrice, "fundable", allowance)
"sent", transfer, "feecap", feeCap, "fundable", allowance)
hi = allowance.Uint64()
}
}
Expand Down
23 changes: 23 additions & 0 deletions accounts/abi/bind/backends/simulated_test.go
Expand Up @@ -580,6 +580,26 @@ func TestEstimateGasWithPrice(t *testing.T) {
Value: big.NewInt(100000000000),
Data: nil,
}, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14)

{"EstimateEIP1559WithHighFees", ethereum.CallMsg{
From: addr,
To: &addr,
Gas: 0,
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
GasTipCap: big.NewInt(1),
Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether
Data: nil,
}, params.TxGas, nil},

{"EstimateEIP1559WithSuperHighFees", ethereum.CallMsg{
From: addr,
To: &addr,
Gas: 0,
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
GasTipCap: big.NewInt(1),
Value: big.NewInt(1e17 + 1), // the remaining balance for fee is 2.1ether
Data: nil,
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
}
for i, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message)
Expand All @@ -592,6 +612,9 @@ func TestEstimateGasWithPrice(t *testing.T) {
}
continue
}
if c.expectError == nil && err != nil {
t.Fatalf("test %d: didn't expect error, got %v", i, err)
}
if got != c.expect {
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
}
Expand Down
19 changes: 16 additions & 3 deletions internal/ethapi/api.go
Expand Up @@ -1008,8 +1008,21 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
}
hi = block.GasLimit()
}

// Normalize the max fee per gas the call is willing to spend.
var maxFeePerGas *big.Int
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} else if args.GasPrice != nil {
maxFeePerGas = args.GasPrice.ToInt()
} else if args.MaxFeePerGas != nil {
maxFeePerGas = args.MaxFeePerGas.ToInt()
} else {
maxFeePerGas = common.Big0
}

// Recap the highest gas limit with account's available balance.
if args.GasPrice != nil && args.GasPrice.ToInt().BitLen() != 0 {
if maxFeePerGas.BitLen() != 0 {
state, _, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if err != nil {
return 0, err
Expand All @@ -1022,7 +1035,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
}
available.Sub(available, args.Value.ToInt())
}
allowance := new(big.Int).Div(available, args.GasPrice.ToInt())
allowance := new(big.Int).Div(available, maxFeePerGas)

// If the allowance is larger than maximum uint64, skip checking
if allowance.IsUint64() && hi > allowance.Uint64() {
Expand All @@ -1031,7 +1044,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
transfer = new(hexutil.Big)
}
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer.ToInt(), "gasprice", args.GasPrice.ToInt(), "fundable", allowance)
"sent", transfer.ToInt(), "maxFeePerGas", maxFeePerGas, "fundable", allowance)
hi = allowance.Uint64()
}
}
Expand Down