Skip to content

Commit

Permalink
Merge pull request #293 from rainlanguage/2025-01-15-arbitrum-debug
Browse files Browse the repository at this point in the history
quote gas  [skip-ci]
  • Loading branch information
rouzwelt authored Jan 16, 2025
2 parents 52bc1e0 + 820e507 commit d65b415
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 7 deletions.
50 changes: 50 additions & 0 deletions src/abis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export const TakeOrdersConfigV3 =
`(uint256 minimumInput, uint256 maximumInput, uint256 maximumIORatio, ${TakeOrderConfigV3}[] orders, bytes data)` as const;
export const ClearConfig =
"(uint256 aliceInputIOIndex, uint256 aliceOutputIOIndex, uint256 bobInputIOIndex, uint256 bobOutputIOIndex, uint256 aliceBountyVaultId, uint256 bobBountyVaultId)" as const;
export const Quote =
`(${OrderV3} order, uint256 inputIOIndex, uint256 outputIOIndex, ${SignedContextV1}[] signedContext)` as const;

/**
* Minimal ABI for Orderbook contract only including vaultBalance() function
Expand All @@ -48,6 +50,7 @@ export const orderbookAbi = [
`function takeOrders2(${TakeOrdersConfigV3} memory config) external returns (uint256 totalInput, uint256 totalOutput)`,
`function clear2(${OrderV3} memory aliceOrder, ${OrderV3} memory bobOrder, ${ClearConfig} calldata clearConfig, ${SignedContextV1}[] memory aliceSignedContext, ${SignedContextV1}[] memory bobSignedContext) external`,
`event TakeOrderV2(address sender, ${TakeOrderConfigV3} config, uint256 input, uint256 output)`,
`function quote(${Quote} calldata quoteConfig) external view returns (bool, uint256, uint256)`,
] as const;

/**
Expand Down Expand Up @@ -104,3 +107,50 @@ export const DefaultArbEvaluable = {
} as const;

export const TakeOrderV2EventAbi = parseAbi([orderbookAbi[13]]);
export const OrderbookQuoteAbi = parseAbi([orderbookAbi[14]]);

/**
* Arbitrum node interface address, used to get L1 gas limit.
* This is not an actual deployed smart contract, it is only
* available to be called through an Arbitrum RPC node, and not
* as normally other smart contracts are called.
*/
export const ArbitrumNodeInterfaceAddress: `0x${string}` =
"0x00000000000000000000000000000000000000C8" as const;

/**
* Arbitrum node interface abi, used to get L1 gas limit
*/
export const ArbitrumNodeInterfaceAbi = [
{
inputs: [
{ internalType: "address", name: "to", type: "address" },
{ internalType: "bool", name: "contractCreation", type: "bool" },
{ internalType: "bytes", name: "data", type: "bytes" },
],
name: "gasEstimateComponents",
outputs: [
{ internalType: "uint64", name: "gasEstimate", type: "uint64" },
{ internalType: "uint64", name: "gasEstimateForL1", type: "uint64" },
{ internalType: "uint256", name: "baseFee", type: "uint256" },
{ internalType: "uint256", name: "l1BaseFeeEstimate", type: "uint256" },
],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{ internalType: "address", name: "to", type: "address" },
{ internalType: "bool", name: "contractCreation", type: "bool" },
{ internalType: "bytes", name: "data", type: "bytes" },
],
name: "gasEstimateL1Component",
outputs: [
{ internalType: "uint64", name: "gasEstimateForL1", type: "uint64" },
{ internalType: "uint256", name: "baseFee", type: "uint256" },
{ internalType: "uint256", name: "l1BaseFeeEstimate", type: "uint256" },
],
stateMutability: "payable",
type: "function",
},
] as const;
53 changes: 52 additions & 1 deletion src/gas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ChainId } from "sushi";
import { BigNumber } from "ethers";
import { getQuoteConfig } from "./utils";
import { encodeFunctionData, multicall3Abi, toHex } from "viem";
import { publicActionsL2, walletActionsL2 } from "viem/op-stack";
import { BotConfig, OperationState, RawTx, ViemClient } from "./types";
import { BotConfig, BundledOrders, OperationState, RawTx, ViemClient } from "./types";
import { ArbitrumNodeInterfaceAbi, ArbitrumNodeInterfaceAddress, OrderbookQuoteAbi } from "./abis";

/**
* Estimates gas cost of the given tx, also takes into account L1 gas cost if the chain is a special L2.
Expand Down Expand Up @@ -85,3 +89,50 @@ export async function getGasPrice(config: BotConfig, state: OperationState) {
state.l1GasPrice = l1GasPriceResult.value;
}
}

/**
* Calculates the gas limit that used for quoting orders
*/
export async function getQuoteGas(
config: BotConfig,
orderDetails: BundledOrders,
multicallAddressOverride?: string,
): Promise<bigint> {
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;
}
}
11 changes: 6 additions & 5 deletions src/processOrders.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChainId } from "sushi";
import { findOpp } from "./modes";
import { getQuoteGas } from "./gas";
import { PublicClient } from "viem";
import { Token } from "sushi/currency";
import { createViemClient } from "./config";
Expand Down Expand Up @@ -435,7 +436,7 @@ export async function processPair(args: {
orderbooksOrders,
state,
} = args;

const isE2eTest = (config as any).isTest;
const spanAttributes: SpanAttrs = {};
const result: ProcessPairResult = {
reason: undefined,
Expand Down Expand Up @@ -470,9 +471,9 @@ export async function processPair(args: {
try {
await quoteSingleOrder(
orderPairObject,
(config as any).isTest ? (config as any).quoteRpc : config.rpc,
isE2eTest ? (config as any).quoteRpc : config.rpc,
undefined,
config.quoteGas,
isE2eTest ? config.quoteGas : await getQuoteGas(config, orderPairObject),
);
if (orderPairObject.takeOrders[0].quote?.maxOutput.isZero()) {
result.report = {
Expand Down Expand Up @@ -508,7 +509,7 @@ export async function processPair(args: {
fetchPoolsTimeout: 90000,
};
// pin block number for test case
if ((config as any).isTest && (config as any).testBlockNumber) {
if (isE2eTest && (config as any).testBlockNumber) {
(options as any).blockNumber = (config as any).testBlockNumber;
}
await dataFetcher.fetchPoolsForToken(fromToken, toToken, PoolBlackList, options);
Expand Down Expand Up @@ -544,7 +545,7 @@ export async function processPair(args: {
fetchPoolsTimeout: 30000,
};
// pin block number for test case
if ((config as any).isTest && (config as any).testBlockNumber) {
if (isE2eTest && (config as any).testBlockNumber) {
(options as any).blockNumber = (config as any).testBlockNumber;
}
inputToEthPrice = await getEthPrice(
Expand Down
31 changes: 30 additions & 1 deletion test/gas.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { assert } from "chai";
import { orderPairObject1 } from "./data";
import { OperationState, ViemClient } from "../src/types";
import { estimateGasCost, getGasPrice, getL1Fee, getTxFee } from "../src/gas";
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 () {
Expand Down Expand Up @@ -126,4 +128,31 @@ describe("Test gas", async function () {
assert.equal(state2.gasPrice, gasPrice);
assert.equal(state2.l1GasPrice, l1GasPrice);
});

it("should get quote gas", async function () {
const limitGas = 1_000_000n;
const arbitrumL1Gas = 2_000_000n;
const multicallAddress = "0x" + "1".repeat(40);

// mock order and bot config and viem client
const orderDetails = orderPairObject1;
const config = {
chain: {
id: ChainId.ARBITRUM,
},
quoteGas: limitGas,
viemClient: {
simulateContract: async () => ({ result: [arbitrumL1Gas, 1_500_000n, 123_000n] }),
},
} as any;

// arbitrum chain
let result = await getQuoteGas(config, orderDetails, multicallAddress);
assert.equal(result, limitGas + arbitrumL1Gas);

// other chains
config.chain.id = 1;
result = await getQuoteGas(config, orderDetails, multicallAddress);
assert.equal(result, limitGas);
});
});

0 comments on commit d65b415

Please sign in to comment.