From 6a38f0166de242ab26abfef378e07bf33e9ca3f1 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Thu, 16 Jan 2025 21:39:33 +0000 Subject: [PATCH 1/4] init --- src/modes/interOrderbook.ts | 14 ++++++++++++++ src/modes/intraOrderbook.ts | 14 ++++++++++++++ src/modes/routeProcessor.ts | 15 +++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/src/modes/interOrderbook.ts b/src/modes/interOrderbook.ts index 93a48c1f..7860caa6 100644 --- a/src/modes/interOrderbook.ts +++ b/src/modes/interOrderbook.ts @@ -214,6 +214,20 @@ export async function dryrun({ return Promise.reject(result); } } + if (gasLimit.isZero()) { + spanAttributes["stage"] = 2; + spanAttributes["isNodeError"] = true; + spanAttributes["error"] = + "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection"; + spanAttributes["rawtx"] = JSON.stringify( + { + ...rawtx, + from: signer.account.address, + }, + withBigintSerializer, + ); + return Promise.reject(result); + } rawtx.gas = gasLimit.toBigInt(); // if reached here, it means there was a success and found opp diff --git a/src/modes/intraOrderbook.ts b/src/modes/intraOrderbook.ts index ac7c8853..4f525112 100644 --- a/src/modes/intraOrderbook.ts +++ b/src/modes/intraOrderbook.ts @@ -225,6 +225,20 @@ export async function dryrun({ return Promise.reject(result); } } + if (gasLimit.isZero()) { + spanAttributes["stage"] = 2; + spanAttributes["isNodeError"] = true; + spanAttributes["error"] = + "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection"; + spanAttributes["rawtx"] = JSON.stringify( + { + ...rawtx, + from: signer.account.address, + }, + withBigintSerializer, + ); + return Promise.reject(result); + } rawtx.gas = gasLimit.toBigInt(); // if reached here, it means there was a success and found opp diff --git a/src/modes/routeProcessor.ts b/src/modes/routeProcessor.ts index 37c05fc8..1a16f8bf 100644 --- a/src/modes/routeProcessor.ts +++ b/src/modes/routeProcessor.ts @@ -281,6 +281,21 @@ export async function dryrun({ return Promise.reject(result); } } + if (gasLimit.isZero()) { + spanAttributes["stage"] = 2; + spanAttributes["isNodeError"] = true; + spanAttributes["error"] = + "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection"; + spanAttributes["rawtx"] = JSON.stringify( + { + ...rawtx, + from: signer.account.address, + }, + withBigintSerializer, + ); + result.reason = RouteProcessorDryrunHaltReason.NoOpportunity; + return Promise.reject(result); + } rawtx.gas = gasLimit.toBigInt(); // if reached here, it means there was a success and found opp From 31ce5d843b226a6bbbb4a2f17be47fdb697668ca Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Thu, 16 Jan 2025 21:47:57 +0000 Subject: [PATCH 2/4] update --- src/modes/interOrderbook.ts | 22 +++++++--------------- src/modes/intraOrderbook.ts | 22 +++++++--------------- src/modes/routeProcessor.ts | 23 +++++++---------------- 3 files changed, 21 insertions(+), 46 deletions(-) diff --git a/src/modes/interOrderbook.ts b/src/modes/interOrderbook.ts index 7860caa6..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( @@ -214,20 +220,6 @@ export async function dryrun({ return Promise.reject(result); } } - if (gasLimit.isZero()) { - spanAttributes["stage"] = 2; - spanAttributes["isNodeError"] = true; - spanAttributes["error"] = - "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection"; - spanAttributes["rawtx"] = JSON.stringify( - { - ...rawtx, - from: signer.account.address, - }, - withBigintSerializer, - ); - return Promise.reject(result); - } rawtx.gas = gasLimit.toBigInt(); // if reached here, it means there was a success and found opp diff --git a/src/modes/intraOrderbook.ts b/src/modes/intraOrderbook.ts index 4f525112..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( @@ -225,20 +231,6 @@ export async function dryrun({ return Promise.reject(result); } } - if (gasLimit.isZero()) { - spanAttributes["stage"] = 2; - spanAttributes["isNodeError"] = true; - spanAttributes["error"] = - "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection"; - spanAttributes["rawtx"] = JSON.stringify( - { - ...rawtx, - from: signer.account.address, - }, - withBigintSerializer, - ); - return Promise.reject(result); - } rawtx.gas = gasLimit.toBigInt(); // if reached here, it means there was a success and found opp diff --git a/src/modes/routeProcessor.ts b/src/modes/routeProcessor.ts index 1a16f8bf..ff52f80b 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( @@ -281,21 +287,6 @@ export async function dryrun({ return Promise.reject(result); } } - if (gasLimit.isZero()) { - spanAttributes["stage"] = 2; - spanAttributes["isNodeError"] = true; - spanAttributes["error"] = - "Failed to estimated gas, rpc returned 0 for gasEstimate call without rejection"; - spanAttributes["rawtx"] = JSON.stringify( - { - ...rawtx, - from: signer.account.address, - }, - withBigintSerializer, - ); - result.reason = RouteProcessorDryrunHaltReason.NoOpportunity; - return Promise.reject(result); - } rawtx.gas = gasLimit.toBigInt(); // if reached here, it means there was a success and found opp From b6527b879bbf8c0551fbea9df9a84193646d696a Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Thu, 16 Jan 2025 22:21:39 +0000 Subject: [PATCH 3/4] Update gas.ts --- src/gas.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gas.ts b/src/gas.ts index d0a49853..694c336c 100644 --- a/src/gas.ts +++ b/src/gas.ts @@ -6,6 +6,9 @@ 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. */ @@ -83,7 +86,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; From 9c92571a21fe5024e7f6eaec8b012d47fb3ede70 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Fri, 17 Jan 2025 03:47:59 +0000 Subject: [PATCH 4/4] update --- src/gas.ts | 6 ++---- src/modes/routeProcessor.ts | 2 +- test/gas.test.ts | 7 +++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/gas.ts b/src/gas.ts index 694c336c..2de97609 100644 --- a/src/gas.ts +++ b/src/gas.ts @@ -1,8 +1,8 @@ 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"; @@ -32,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; } diff --git a/src/modes/routeProcessor.ts b/src/modes/routeProcessor.ts index ff52f80b..fd880cd0 100644 --- a/src/modes/routeProcessor.ts +++ b/src/modes/routeProcessor.ts @@ -498,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 },