Gas计算函数报错:账户余额不足以支付gas费用求助
我尝试调用某合约执行套利交换,触发如下错误:
Low-level call error: err: insufficient funds for gas * price + value: address 0x have 26389996078916648 want 3681518208500000000 (supplied gas 500000000)
其中3681518208500000000对应3.1354971585ETH,按当前汇率2583.29美元/ETH计算,约合8099.91美元。
我怀疑问题出在以下gasCalculated函数中,恳请帮忙排查:
func gasCalculated(from, to, token string, amount int) *big.Int { // Fetch the contract ABI contractABI, err := abi.JSON(strings.NewReader(flashLoanReceiverABI())) // Correct ABI fetching if err != nil { fmt.Println("Failed to fetch ABI:", err) return big.NewInt(0) } // Create context ctx := context.Background() // Fetch the base fee and gas tip (miner's tip) for the next block header, err := connection.Get().HeaderByNumber(ctx, nil) if err != nil { fmt.Println("Failed to get header:", err) return big.NewInt(0) } tip, err := connection.Get().SuggestGasTipCap(ctx) if err != nil { fmt.Println("Failed to suggest gas tip cap:", err) return big.NewInt(0) } // Increase the tip cap if it's too low (e.g., minimum 1 Gwei) minTipCap := big.NewInt(1000000000) // 1 Gwei if tip.Cmp(minTipCap) < 0 { tip.Set(minTipCap) } // Adding a margin of 2 Gwei to the base fee margin := big.NewInt(2000000000) // 2 Gwei // Calculate GasFeeCap by adding a margin to the base fee to ensure transaction inclusion gasFeeCap := new(big.Int).Add(header.BaseFee, margin) // Add the suggested gas tip cap to ensure priority in the transaction pool gasFeeCap.Add(gasFeeCap, tip) // Specify the sender and recipient addresses fromAddress := common.HexToAddress(from) toAddress := common.HexToAddress(to) tokenAddress := common.HexToAddress(token) addressType, err := abi.NewType("address", "", nil) if err != nil { log.Fatalf("Failed to create address type: %v", err) } addresses := abi.Arguments{ {Type: addressType}, {Type: addressType}, {Type: addressType}, } packedData, err := addresses.Pack(fromAddress, toAddress, tokenAddress) if err != nil { log.Fatalf("Failed to pack addresses: %v", err) } data, err := contractABI.Pack("onFlashLoan", fromAddress, tokenAddress, big.NewInt(int64(amount)), big.NewInt(0), packedData) if err != nil { fmt.Println("ABI packing failed:", err) return big.NewInt(0) } callMsg := ethereum.CallMsg{ From: fromAddress, To: &toAddress, GasFeeCap: gasFeeCap, GasTipCap: tip, Value: big.NewInt(0), // Adjust if ETH is sent along with the call Data: data, } // Perform the low-level eth_call to check the transaction var result hexutil.Bytes result, err = connection.Get().CallContract(ctx, callMsg, header.Number) if err != nil { fmt.Printf("Low-level call %v\n", err) //<--Low-level call error: err: insufficient funds for gas * price + value: address 0x have 26389996078916648 want 3681518208500000000 (supplied gas 500000000) return big.NewInt(0) } // Decode or inspect `result` to understand the failure fmt.Printf("Low-level call result: %x\n", result) // Estimate the gas required for the transaction gasLimit, err := connection.Get().EstimateGas(ctx, callMsg) if err != nil { fmt.Println("Failed to estimate gas:", err) return big.NewInt(0) } return big.NewInt(int64(gasLimit)) }
问题排查分析
GasFeeCap计算逻辑错误
当前代码中gasFeeCap的计算是baseFee + margin + tip,但按照EIP-1559规则,gasFeeCap是用户愿意支付的单Gas最高费用,正确值应为baseFee + tip(实际支付的费用是min(gasFeeCap, baseFee + tip))。你直接将三者相加,导致gasFeeCap被大幅拉高,结合默认的超大GasLimit(500000000),总费用计算值直接超出地址余额。CallContract流程顺序错误
代码先执行CallContract再做Gas估算,此时CallMsg未指定Gas字段,节点会使用默认的高GasLimit值。过高的GasLimit乘以错误计算的gasFeeCap,直接触发余额不足提示。正确流程应该是先估算Gas用量,再传入CallContract中。参数匹配风险
需确认目标合约onFlashLoan函数的参数签名是否与你打包的参数结构完全匹配,错误的参数可能导致合约执行异常,间接影响Gas费用计算。
修复建议
- 修正
gasFeeCap计算逻辑:// 正确计算gasFeeCap:取baseFee+tip与baseFee+margin的较大值 basePlusTip := new(big.Int).Add(header.BaseFee, tip) basePlusMargin := new(big.Int).Add(header.BaseFee, margin) gasFeeCap := basePlusTip if basePlusMargin.Cmp(basePlusTip) > 0 { gasFeeCap = basePlusMargin } - 调整流程顺序,先估算Gas再执行Call:
// 先估算Gas用量 gasLimit, err := connection.Get().EstimateGas(ctx, callMsg) if err != nil { fmt.Println("Failed to estimate gas:", err) return big.NewInt(0) } // 将估算的GasLimit加入CallMsg callMsg.Gas = gasLimit // 再执行CallContract result, err = connection.Get().CallContract(ctx, callMsg, header.Number) - 验证目标合约
onFlashLoan的函数签名,确保ABI打包的参数数量、类型完全匹配。
内容的提问来源于stack exchange,提问作者pigfox

