From b32a96cbcd691ba889bd7744decdfb53cd8ecf0c Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Mon, 20 Jan 2025 04:53:36 +0000 Subject: [PATCH] update --- src/account.ts | 2 +- src/cli.ts | 2 +- src/gas.ts | 264 ++++++++++++++++++------------------ src/modes/interOrderbook.ts | 2 +- src/modes/intraOrderbook.ts | 2 +- src/modes/routeProcessor.ts | 2 +- src/processOrders.ts | 2 +- src/tx.ts | 143 ++++++++++++++++++- test/gas.test.ts | 2 +- 9 files changed, 281 insertions(+), 140 deletions(-) diff --git a/src/account.ts b/src/account.ts index 2d6bc824..14d32046 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,6 +1,6 @@ import { ChainId, RPParams } from "sushi"; import { BigNumber, ethers } from "ethers"; -import { estimateGasCost, getTxFee } from "./gas"; +import { estimateGasCost, getTxFee } from "./tx"; import { ErrorSeverity, errorSnapshot } from "./error"; import { Native, Token, WNATIVE } from "sushi/currency"; import { ROUTE_PROCESSOR_4_ADDRESS } from "sushi/config"; diff --git a/src/cli.ts b/src/cli.ts index 2b288ef4..e25106a4 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,6 +1,6 @@ import { config } from "dotenv"; import { isAddress } from "viem"; -import { getGasPrice } from "./gas"; +import { getGasPrice } from "./tx"; import { Command } from "commander"; import { getMetaInfo } from "./config"; import { BigNumber, ethers } from "ethers"; diff --git a/src/gas.ts b/src/gas.ts index aba0cd5e..48d18456 100644 --- a/src/gas.ts +++ b/src/gas.ts @@ -1,141 +1,141 @@ -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 { BotConfig, BundledOrders, OperationState, RawTx, ViemClient } from "./types"; -import { ArbitrumNodeInterfaceAbi, ArbitrumNodeInterfaceAddress, OrderbookQuoteAbi } from "./abis"; +// 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 { 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; +// // 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. - */ -export async function estimateGasCost( - tx: RawTx, - signer: ViemClient, - config: BotConfig, - l1GasPrice?: bigint, - l1Signer?: any, -) { - const gasPrice = - tx.gasPrice ?? ((await signer.getGasPrice()) * BigInt(config.gasPriceMultiplier)) / 100n; - const gas = await signer.estimateGas(tx); - const result = { - gas, - gasPrice, - l1GasPrice: 0n, - l1Cost: 0n, - totalGasCost: gasPrice * gas, - }; - if (config.isSpecialL2) { - try { - const l1Signer_ = l1Signer ? l1Signer : signer.extend(publicActionsL2()); - if (typeof l1GasPrice !== "bigint") { - l1GasPrice = (await l1Signer_.getL1BaseFee()) as bigint; - } - const l1Cost = await l1Signer_.estimateL1Fee({ - to: tx.to, - data: tx.data, - }); - result.l1GasPrice = l1GasPrice; - result.l1Cost = l1Cost; - result.totalGasCost += l1Cost; - } catch {} - } - return result; -} +// /** +// * Estimates gas cost of the given tx, also takes into account L1 gas cost if the chain is a special L2. +// */ +// export async function estimateGasCost( +// tx: RawTx, +// signer: ViemClient, +// config: BotConfig, +// l1GasPrice?: bigint, +// l1Signer?: any, +// ) { +// const gasPrice = +// tx.gasPrice ?? ((await signer.getGasPrice()) * BigInt(config.gasPriceMultiplier)) / 100n; +// const gas = await signer.estimateGas(tx); +// const result = { +// gas, +// gasPrice, +// l1GasPrice: 0n, +// l1Cost: 0n, +// totalGasCost: gasPrice * gas, +// }; +// if (config.isSpecialL2) { +// try { +// const l1Signer_ = l1Signer ? l1Signer : signer.extend(publicActionsL2()); +// if (typeof l1GasPrice !== "bigint") { +// l1GasPrice = (await l1Signer_.getL1BaseFee()) as bigint; +// } +// const l1Cost = await l1Signer_.estimateL1Fee({ +// to: tx.to, +// data: tx.data, +// }); +// result.l1GasPrice = l1GasPrice; +// result.l1Cost = l1Cost; +// result.totalGasCost += l1Cost; +// } catch {} +// } +// return result; +// } -/** - * Retruns the L1 gas cost of a transaction if operating chain is special L2 else returns 0. - */ -export function getL1Fee(receipt: any, config: BotConfig): bigint { - if (!config.isSpecialL2) return 0n; +// /** +// * Retruns the L1 gas cost of a transaction if operating chain is special L2 else returns 0. +// */ +// export function getL1Fee(receipt: any, config: BotConfig): bigint { +// if (!config.isSpecialL2) return 0n; - if (typeof receipt.l1Fee === "bigint") { - return receipt.l1Fee; - } else if (typeof receipt.l1GasPrice === "bigint" && typeof receipt.l1GasUsed === "bigint") { - return (receipt.l1GasPrice as bigint) * (receipt.l1GasUsed as bigint); - } else { - return 0n; - } -} +// if (typeof receipt.l1Fee === "bigint") { +// return receipt.l1Fee; +// } else if (typeof receipt.l1GasPrice === "bigint" && typeof receipt.l1GasUsed === "bigint") { +// return (receipt.l1GasPrice as bigint) * (receipt.l1GasUsed as bigint); +// } else { +// return 0n; +// } +// } -/** - * Get Transaction total gas cost from receipt (includes L1 fee) - */ -export function getTxFee(receipt: any, config: BotConfig): bigint { - const gasUsed = BigNumber.from(receipt.gasUsed).toBigInt(); - const effectiveGasPrice = BigNumber.from(receipt.effectiveGasPrice).toBigInt(); - return effectiveGasPrice * gasUsed + getL1Fee(receipt, config); -} +// /** +// * Get Transaction total gas cost from receipt (includes L1 fee) +// */ +// export function getTxFee(receipt: any, config: BotConfig): bigint { +// const gasUsed = BigNumber.from(receipt.gasUsed).toBigInt(); +// const effectiveGasPrice = BigNumber.from(receipt.effectiveGasPrice).toBigInt(); +// return effectiveGasPrice * gasUsed + getL1Fee(receipt, config); +// } -/** - * Fetches the gas price (L1 gas price as well if chain is special L2) - */ -export async function getGasPrice(config: BotConfig, state: OperationState) { - const promises = [config.viemClient.getGasPrice()]; - if (config.isSpecialL2) { - const l1Client = config.viemClient.extend(publicActionsL2()); - promises.push(l1Client.getL1BaseFee()); - } - const [gasPriceResult, l1GasPriceResult = undefined] = await Promise.allSettled(promises); - if (gasPriceResult.status === "fulfilled") { - 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; - } -} +// /** +// * Fetches the gas price (L1 gas price as well if chain is special L2) +// */ +// export async function getGasPrice(config: BotConfig, state: OperationState) { +// const promises = [config.viemClient.getGasPrice()]; +// if (config.isSpecialL2) { +// const l1Client = config.viemClient.extend(publicActionsL2()); +// promises.push(l1Client.getL1BaseFee()); +// } +// const [gasPriceResult, l1GasPriceResult = undefined] = await Promise.allSettled(promises); +// if (gasPriceResult.status === "fulfilled") { +// 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; +// } +// } -/** - * Calculates the gas limit that used for quoting orders - */ -export async function getQuoteGas( - config: BotConfig, - orderDetails: BundledOrders, - multicallAddressOverride?: string, -): Promise { - if (config.chain.id === ChainId.ARBITRUM) { - // build the calldata of a quote call - const quoteConfig = getQuoteConfig(orderDetails.takeOrders[0]) as any; - quoteConfig.inputIOIndex = BigInt(quoteConfig.inputIOIndex); - quoteConfig.outputIOIndex = BigInt(quoteConfig.outputIOIndex); - quoteConfig.order.evaluable.bytecode = toHex(quoteConfig.order.evaluable.bytecode); - const multicallConfig = { - target: orderDetails.orderbook as `0x${string}`, - allowFailure: true, - callData: encodeFunctionData({ - abi: OrderbookQuoteAbi, - functionName: "quote", - args: [quoteConfig], - }), - }; - const calldata = encodeFunctionData({ - abi: multicall3Abi, - functionName: "aggregate3", - args: [[multicallConfig] as const], - }); +// /** +// * Calculates the gas limit that used for quoting orders +// */ +// export async function getQuoteGas( +// config: BotConfig, +// orderDetails: BundledOrders, +// multicallAddressOverride?: string, +// ): Promise { +// if (config.chain.id === ChainId.ARBITRUM) { +// // build the calldata of a quote call +// const quoteConfig = getQuoteConfig(orderDetails.takeOrders[0]) as any; +// quoteConfig.inputIOIndex = BigInt(quoteConfig.inputIOIndex); +// quoteConfig.outputIOIndex = BigInt(quoteConfig.outputIOIndex); +// quoteConfig.order.evaluable.bytecode = toHex(quoteConfig.order.evaluable.bytecode); +// const multicallConfig = { +// target: orderDetails.orderbook as `0x${string}`, +// allowFailure: true, +// callData: encodeFunctionData({ +// abi: OrderbookQuoteAbi, +// functionName: "quote", +// args: [quoteConfig], +// }), +// }; +// const calldata = encodeFunctionData({ +// abi: multicall3Abi, +// functionName: "aggregate3", +// args: [[multicallConfig] as const], +// }); - const multicallAddress = - (multicallAddressOverride as `0x${string}` | undefined) ?? - config.viemClient.chain?.contracts?.multicall3?.address; - if (!multicallAddress) throw "unknown multicall address"; +// const multicallAddress = +// (multicallAddressOverride as `0x${string}` | undefined) ?? +// config.viemClient.chain?.contracts?.multicall3?.address; +// if (!multicallAddress) throw "unknown multicall address"; - // call Arbitrum Node Interface for the calldata to get L1 gas - const result = await config.viemClient.simulateContract({ - abi: ArbitrumNodeInterfaceAbi, - address: ArbitrumNodeInterfaceAddress, - functionName: "gasEstimateL1Component", - args: [multicallAddress, false, calldata], - }); - return config.quoteGas + result.result[0]; - } else { - return config.quoteGas; - } -} +// // call Arbitrum Node Interface for the calldata to get L1 gas +// const result = await config.viemClient.simulateContract({ +// abi: ArbitrumNodeInterfaceAbi, +// address: ArbitrumNodeInterfaceAddress, +// functionName: "gasEstimateL1Component", +// args: [multicallAddress, false, calldata], +// }); +// return config.quoteGas + result.result[0]; +// } else { +// return config.quoteGas; +// } +// } diff --git a/src/modes/interOrderbook.ts b/src/modes/interOrderbook.ts index 8fd93a0b..4e304ca3 100644 --- a/src/modes/interOrderbook.ts +++ b/src/modes/interOrderbook.ts @@ -1,5 +1,5 @@ import { orderbookAbi } from "../abis"; -import { estimateGasCost } from "../gas"; +import { estimateGasCost } from "../tx"; import { BigNumber, Contract, ethers } from "ethers"; import { containsNodeError, errorSnapshot } from "../error"; import { getBountyEnsureRainlang, parseRainlang } from "../task"; diff --git a/src/modes/intraOrderbook.ts b/src/modes/intraOrderbook.ts index ed35bafb..87528431 100644 --- a/src/modes/intraOrderbook.ts +++ b/src/modes/intraOrderbook.ts @@ -1,5 +1,5 @@ import { orderbookAbi } from "../abis"; -import { estimateGasCost } from "../gas"; +import { estimateGasCost } from "../tx"; import { BigNumber, ethers } from "ethers"; import { containsNodeError, errorSnapshot } from "../error"; import { getWithdrawEnsureRainlang, parseRainlang } from "../task"; diff --git a/src/modes/routeProcessor.ts b/src/modes/routeProcessor.ts index fd880cd0..292ee33a 100644 --- a/src/modes/routeProcessor.ts +++ b/src/modes/routeProcessor.ts @@ -1,5 +1,5 @@ import { Token } from "sushi/currency"; -import { estimateGasCost } from "../gas"; +import { estimateGasCost } from "../tx"; import { ChainId, DataFetcher, Router } from "sushi"; import { BigNumber, Contract, ethers } from "ethers"; import { containsNodeError, errorSnapshot } from "../error"; diff --git a/src/processOrders.ts b/src/processOrders.ts index 17d9fab4..44083fd6 100644 --- a/src/processOrders.ts +++ b/src/processOrders.ts @@ -1,6 +1,6 @@ import { ChainId } from "sushi"; import { findOpp } from "./modes"; -import { getGasPrice, getQuoteGas } from "./gas"; +import { getGasPrice, getQuoteGas } from "./tx"; import { PublicClient } from "viem"; import { Token } from "sushi/currency"; import { createViemClient } from "./config"; diff --git a/src/tx.ts b/src/tx.ts index b9216539..2fc1c42b 100644 --- a/src/tx.ts +++ b/src/tx.ts @@ -1,6 +1,6 @@ import { Token } from "sushi/currency"; import { Contract, ethers } from "ethers"; -import { getL1Fee, getTxFee } from "./gas"; +// import { getL1Fee, getTxFee } from "./gas"; import { addWatchedToken } from "./account"; import { containsNodeError, handleRevert } from "./error"; import { ProcessPairHaltReason, ProcessPairReportStatus } from "./processOrders"; @@ -15,6 +15,13 @@ import { withBigintSerializer, getActualClearAmount, } from "./utils"; +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 { OperationState } from "./types"; +import { ArbitrumNodeInterfaceAbi, ArbitrumNodeInterfaceAddress, OrderbookQuoteAbi } from "./abis"; /** * Handles the given transaction, starts by sending the transaction and @@ -381,3 +388,137 @@ export function getTxGas(config: BotConfig, gas: bigint): bigint { return gas; } } + +// 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. + */ +export async function estimateGasCost( + tx: RawTx, + signer: ViemClient, + config: BotConfig, + l1GasPrice?: bigint, + l1Signer?: any, +) { + const gasPrice = + tx.gasPrice ?? ((await signer.getGasPrice()) * BigInt(config.gasPriceMultiplier)) / 100n; + const gas = await signer.estimateGas(tx); + const result = { + gas, + gasPrice, + l1GasPrice: 0n, + l1Cost: 0n, + totalGasCost: gasPrice * gas, + }; + if (config.isSpecialL2) { + try { + const l1Signer_ = l1Signer ? l1Signer : signer.extend(publicActionsL2()); + if (typeof l1GasPrice !== "bigint") { + l1GasPrice = (await l1Signer_.getL1BaseFee()) as bigint; + } + const l1Cost = await l1Signer_.estimateL1Fee({ + to: tx.to, + data: tx.data, + }); + result.l1GasPrice = l1GasPrice; + result.l1Cost = l1Cost; + result.totalGasCost += l1Cost; + } catch {} + } + return result; +} + +/** + * Retruns the L1 gas cost of a transaction if operating chain is special L2 else returns 0. + */ +export function getL1Fee(receipt: any, config: BotConfig): bigint { + if (!config.isSpecialL2) return 0n; + + if (typeof receipt.l1Fee === "bigint") { + return receipt.l1Fee; + } else if (typeof receipt.l1GasPrice === "bigint" && typeof receipt.l1GasUsed === "bigint") { + return (receipt.l1GasPrice as bigint) * (receipt.l1GasUsed as bigint); + } else { + return 0n; + } +} + +/** + * Get Transaction total gas cost from receipt (includes L1 fee) + */ +export function getTxFee(receipt: any, config: BotConfig): bigint { + const gasUsed = BigNumber.from(receipt.gasUsed).toBigInt(); + const effectiveGasPrice = BigNumber.from(receipt.effectiveGasPrice).toBigInt(); + return effectiveGasPrice * gasUsed + getL1Fee(receipt, config); +} + +/** + * Fetches the gas price (L1 gas price as well if chain is special L2) + */ +export async function getGasPrice(config: BotConfig, state: OperationState) { + const promises = [config.viemClient.getGasPrice()]; + if (config.isSpecialL2) { + const l1Client = config.viemClient.extend(publicActionsL2()); + promises.push(l1Client.getL1BaseFee()); + } + const [gasPriceResult, l1GasPriceResult = undefined] = await Promise.allSettled(promises); + if (gasPriceResult.status === "fulfilled") { + 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; + } +} + +/** + * Calculates the gas limit that used for quoting orders + */ +export async function getQuoteGas( + config: BotConfig, + orderDetails: BundledOrders, + multicallAddressOverride?: string, +): Promise { + if (config.chain.id === ChainId.ARBITRUM) { + // build the calldata of a quote call + const quoteConfig = getQuoteConfig(orderDetails.takeOrders[0]) as any; + quoteConfig.inputIOIndex = BigInt(quoteConfig.inputIOIndex); + quoteConfig.outputIOIndex = BigInt(quoteConfig.outputIOIndex); + quoteConfig.order.evaluable.bytecode = toHex(quoteConfig.order.evaluable.bytecode); + const multicallConfig = { + target: orderDetails.orderbook as `0x${string}`, + allowFailure: true, + callData: encodeFunctionData({ + abi: OrderbookQuoteAbi, + functionName: "quote", + args: [quoteConfig], + }), + }; + const calldata = encodeFunctionData({ + abi: multicall3Abi, + functionName: "aggregate3", + args: [[multicallConfig] as const], + }); + + const multicallAddress = + (multicallAddressOverride as `0x${string}` | undefined) ?? + config.viemClient.chain?.contracts?.multicall3?.address; + if (!multicallAddress) throw "unknown multicall address"; + + // call Arbitrum Node Interface for the calldata to get L1 gas + const result = await config.viemClient.simulateContract({ + abi: ArbitrumNodeInterfaceAbi, + address: ArbitrumNodeInterfaceAddress, + functionName: "gasEstimateL1Component", + args: [multicallAddress, false, calldata], + }); + return config.quoteGas + result.result[0]; + } else { + return config.quoteGas; + } +} diff --git a/test/gas.test.ts b/test/gas.test.ts index f8f1d85e..61aefd66 100644 --- a/test/gas.test.ts +++ b/test/gas.test.ts @@ -2,7 +2,7 @@ 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 { estimateGasCost, getGasPrice, getL1Fee, getQuoteGas, getTxFee } from "../src/tx"; describe("Test gas", async function () { it("should estimate gas correctly for L1 and L2 chains", async function () {