diff --git a/src/gas.ts b/src/gas.ts index d0a49853..2de97609 100644 --- a/src/gas.ts +++ b/src/gas.ts @@ -1,11 +1,14 @@ import { ChainId } from "sushi"; import { BigNumber } from "ethers"; import { getQuoteConfig } from "./utils"; +import { publicActionsL2 } from "viem/op-stack"; import { encodeFunctionData, multicall3Abi, toHex } from "viem"; -import { publicActionsL2, walletActionsL2 } from "viem/op-stack"; import { BotConfig, BundledOrders, OperationState, RawTx, ViemClient } from "./types"; import { ArbitrumNodeInterfaceAbi, ArbitrumNodeInterfaceAddress, OrderbookQuoteAbi } from "./abis"; +// default gas price for bsc chain, 1 gwei +export const BSC_DEFAULT_GAS_PRICE = 1_000_000_000n as const; + /** * Estimates gas cost of the given tx, also takes into account L1 gas cost if the chain is a special L2. */ @@ -29,9 +32,7 @@ export async function estimateGasCost( }; if (config.isSpecialL2) { try { - const l1Signer_ = l1Signer - ? l1Signer - : signer.extend(walletActionsL2()).extend(publicActionsL2()); + const l1Signer_ = l1Signer ? l1Signer : signer.extend(publicActionsL2()); if (typeof l1GasPrice !== "bigint") { l1GasPrice = (await l1Signer_.getL1BaseFee()) as bigint; } @@ -83,7 +84,11 @@ export async function getGasPrice(config: BotConfig, state: OperationState) { } const [gasPriceResult, l1GasPriceResult = undefined] = await Promise.allSettled(promises); if (gasPriceResult.status === "fulfilled") { - state.gasPrice = (gasPriceResult.value * BigInt(config.gasPriceMultiplier)) / 100n; + let gasPrice = gasPriceResult.value; + if (config.chain.id === ChainId.BSC && gasPrice < BSC_DEFAULT_GAS_PRICE) { + gasPrice = BSC_DEFAULT_GAS_PRICE; + } + state.gasPrice = (gasPrice * BigInt(config.gasPriceMultiplier)) / 100n; } if (l1GasPriceResult?.status === "fulfilled") { state.l1GasPrice = l1GasPriceResult.value; diff --git a/src/modes/interOrderbook.ts b/src/modes/interOrderbook.ts index 93a48c1f..8fd93a0b 100644 --- a/src/modes/interOrderbook.ts +++ b/src/modes/interOrderbook.ts @@ -1,9 +1,9 @@ import { orderbookAbi } from "../abis"; import { estimateGasCost } from "../gas"; -import { BaseError, PublicClient } from "viem"; import { BigNumber, Contract, ethers } from "ethers"; import { containsNodeError, errorSnapshot } from "../error"; import { getBountyEnsureRainlang, parseRainlang } from "../task"; +import { BaseError, ExecutionRevertedError, PublicClient } from "viem"; import { BotConfig, BundledOrders, ViemClient, DryrunResult, SpanAttrs } from "../types"; import { ONE18, @@ -175,6 +175,12 @@ export async function dryrun({ gasLimit = ethers.BigNumber.from(estimation.gas) .mul(config.gasLimitMultiplier) .div(100); + if (gasLimit.isZero()) { + throw new ExecutionRevertedError({ + message: + "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection", + }); + } rawtx.gas = gasLimit.toBigInt(); gasCost = gasLimit.mul(gasPrice).add(estimation.l1Cost); task.evaluable.bytecode = await parseRainlang( diff --git a/src/modes/intraOrderbook.ts b/src/modes/intraOrderbook.ts index ac7c8853..ed35bafb 100644 --- a/src/modes/intraOrderbook.ts +++ b/src/modes/intraOrderbook.ts @@ -1,9 +1,9 @@ import { orderbookAbi } from "../abis"; import { estimateGasCost } from "../gas"; import { BigNumber, ethers } from "ethers"; -import { BaseError, erc20Abi, PublicClient } from "viem"; import { containsNodeError, errorSnapshot } from "../error"; import { getWithdrawEnsureRainlang, parseRainlang } from "../task"; +import { BaseError, erc20Abi, ExecutionRevertedError, PublicClient } from "viem"; import { estimateProfit, scale18, withBigintSerializer, extendSpanAttributes } from "../utils"; import { SpanAttrs, @@ -177,6 +177,12 @@ export async function dryrun({ gasLimit = ethers.BigNumber.from(estimation.gas) .mul(config.gasLimitMultiplier) .div(100); + if (gasLimit.isZero()) { + throw new ExecutionRevertedError({ + message: + "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection", + }); + } rawtx.gas = gasLimit.toBigInt(); gasCost = gasLimit.mul(gasPrice).add(estimation.l1Cost); task.evaluable.bytecode = await parseRainlang( diff --git a/src/modes/routeProcessor.ts b/src/modes/routeProcessor.ts index 37c05fc8..fd880cd0 100644 --- a/src/modes/routeProcessor.ts +++ b/src/modes/routeProcessor.ts @@ -1,10 +1,10 @@ import { Token } from "sushi/currency"; import { estimateGasCost } from "../gas"; -import { BaseError, PublicClient } from "viem"; import { ChainId, DataFetcher, Router } from "sushi"; import { BigNumber, Contract, ethers } from "ethers"; import { containsNodeError, errorSnapshot } from "../error"; import { getBountyEnsureRainlang, parseRainlang } from "../task"; +import { BaseError, ExecutionRevertedError, PublicClient } from "viem"; import { SpanAttrs, BotConfig, ViemClient, DryrunResult, BundledOrders } from "../types"; import { ONE18, @@ -241,6 +241,12 @@ export async function dryrun({ gasLimit = ethers.BigNumber.from(estimation.gas) .mul(config.gasLimitMultiplier) .div(100); + if (gasLimit.isZero()) { + throw new ExecutionRevertedError({ + message: + "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection", + }); + } rawtx.gas = gasLimit.toBigInt(); gasCost = gasLimit.mul(gasPrice).add(estimation.l1Cost); task.evaluable.bytecode = await parseRainlang( @@ -492,7 +498,7 @@ export async function findOppWithRetries({ // ie its maxInput is the greatest const prom = allPromises[i]; if (prom.status === "fulfilled") { - if (!choice || choice.maximumInput!.lt(prom.value.value!.maximumInput!)) { + if (!choice || choice.estimatedProfit.lt(prom.value.value!.estimatedProfit)) { // record the attributes of the choosing one for (const attrKey in prom.value.spanAttributes) { spanAttributes[attrKey] = prom.value.spanAttributes[attrKey]; diff --git a/test/gas.test.ts b/test/gas.test.ts index 60ca5276..f8f1d85e 100644 --- a/test/gas.test.ts +++ b/test/gas.test.ts @@ -1,8 +1,8 @@ import { assert } from "chai"; +import { ChainId } from "sushi"; import { orderPairObject1 } from "./data"; import { OperationState, ViemClient } from "../src/types"; import { estimateGasCost, getGasPrice, getL1Fee, getQuoteGas, getTxFee } from "../src/gas"; -import { ChainId } from "sushi"; describe("Test gas", async function () { it("should estimate gas correctly for L1 and L2 chains", async function () { @@ -80,7 +80,9 @@ describe("Test gas", async function () { it("should get tx fee", async function () { // mock config and receipt - const config = {} as any; + const config = { + chain: { id: 137 }, + } as any; const receipt = { effectiveGasPrice: 10n, gasUsed: 5n, @@ -102,6 +104,7 @@ describe("Test gas", async function () { const l1GasPrice = 2n; // mock config and viem client const config = { + chain: { id: 137 }, isSpecialL2: false, gasPriceMultiplier: 100n, viemClient: { getGasPrice: async () => gasPrice },