Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
rouzwelt committed Nov 19, 2024
1 parent d54a7e3 commit 47653c1
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 6 deletions.
147 changes: 146 additions & 1 deletion src/error.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { BigNumber } from "ethers";
import { ViemClient } from "./types";
// @ts-ignore
import { abi as obAbi } from "../test/abis/OrderBook.json";
// @ts-ignore
import { abi as rp4Abi } from "../test/abis/RouteProcessor4.json";
// @ts-ignore
import { abi as arbRp4Abi } from "../test/abis/RouteProcessorOrderBookV4ArbOrderTaker.json";
// @ts-ignore
import { abi as genericArbAbi } from "../test/abis/GenericPoolOrderBookV4ArbOrderTaker.json";
import {
isHex,
BaseError,
isAddress,
RpcRequestError,
decodeErrorResult,
ExecutionRevertedError,
InsufficientFundsError,
// InvalidInputRpcError,
Expand All @@ -16,6 +30,22 @@ export enum ErrorSeverity {
HIGH = "HIGH",
}

export type DecodedError = {
name: string;
args: string[];
};

export type RawError = {
code: number;
message: string;
data?: string;
};

export type TxRevertError = {
raw: RawError;
decoded?: DecodedError;
};

/**
* Get error with snapshot
*/
Expand All @@ -24,7 +54,20 @@ export function errorSnapshot(header: string, err: any): string {
if (err instanceof BaseError) {
if (err.shortMessage) message.push("Reason: " + err.shortMessage);
if (err.name) message.push("Error: " + err.name);
if (err.details) message.push("Details: " + err.details);
if (err.details) {
message.push("Details: " + err.details);
if (err.details.includes("unknown reason")) {
const { raw, decoded } = parseRevertError(err);
if (decoded) {
message.push("Error Name: " + decoded.name);
if (decoded.args.length) {
message.push("Error Args: " + JSON.stringify(decoded.args));
}
} else {
if (raw.data) message.push("Error Raw Data: " + raw.data);
}
}
}
} else if (err instanceof Error) {
if ("reason" in err) message.push("Reason: " + err.reason);
else message.push("Reason: " + err.message);
Expand Down Expand Up @@ -58,3 +101,105 @@ export function containsNodeError(err: BaseError): boolean {
return false;
}
}

export async function handleRevert(
viemClient: ViemClient,
hash: `0x${string}`,
): Promise<{ err: any; nodeError: boolean } | undefined> {
try {
const tx = await viemClient.getTransaction({ hash });
await viemClient.call({
account: tx.from,
to: tx.to,
data: tx.input,
gas: tx.gas,
gasPrice: tx.gasPrice,
blockNumber: tx.blockNumber,
});
return undefined;
} catch (err) {
if (err instanceof BaseError) {
const { raw, decoded } = parseRevertError(err);
if (decoded || raw.data) return { err, nodeError: true };
}
return { err, nodeError: false };
}
}

export function parseRevertError(error: BaseError): TxRevertError {
if ("cause" in error) {
return parseRevertError(error.cause as any);
} else {
let decoded: DecodedError | undefined;
const raw: RawError = {
code: (error as any).code ?? NaN,
message: error.message,
data: (error as any).data ?? undefined,
};
if ("data" in error && isHex(error.data)) {
decoded = tryDecodeError(error.data);
}
return { raw, decoded };
}
}

export function tryDecodeError(data: `0x${string}`): DecodedError | undefined {
const handleArgs = (args: readonly unknown[]): string[] => {
return (
args?.map((arg) => {
if (typeof arg === "string") {
return arg;
}
if (typeof arg === "bigint") {
const str = BigNumber.from(arg).toHexString();
if (isAddress(str)) {
return str;
} else {
return arg.toString();
}
}
if (typeof arg === "number") {
return arg.toString();
}
try {
return arg!.toString();
} catch (error) {
return "";
}
}) ?? []
);
};
try {
const result = decodeErrorResult({ data, abi: rp4Abi });
return {
name: result.errorName,
args: handleArgs(result.args ?? []),
};
} catch {
try {
const result = decodeErrorResult({ data, abi: obAbi });
return {
name: result.errorName,
args: handleArgs(result.args ?? []),
};
} catch {
try {
const result = decodeErrorResult({ data, abi: arbRp4Abi });
return {
name: result.errorName,
args: handleArgs(result.args ?? []),
};
} catch {
try {
const result = decodeErrorResult({ data, abi: genericArbAbi });
return {
name: result.errorName,
args: handleArgs(result.args ?? []),
};
} catch {
return undefined;
}
}
}
}
}
18 changes: 18 additions & 0 deletions src/modes/interOrderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ export async function dryrun({
try {
blockNumber = Number(await viemClient.getBlockNumber());
spanAttributes["blockNumber"] = blockNumber;
try {
gasPrice = ethers.BigNumber.from(await viemClient.getGasPrice())
.mul(config.gasPriceMultiplier)
.div("100")
.toBigInt();
rawtx.gasPrice = gasPrice;
} catch {
/**/
}
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx))
.mul(config.gasLimitMultiplier)
.div(100);
Expand Down Expand Up @@ -153,6 +162,15 @@ export async function dryrun({
try {
blockNumber = Number(await viemClient.getBlockNumber());
spanAttributes["blockNumber"] = blockNumber;
try {
gasPrice = ethers.BigNumber.from(await viemClient.getGasPrice())
.mul(config.gasPriceMultiplier)
.div("100")
.toBigInt();
rawtx.gasPrice = gasPrice;
} catch {
/**/
}
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx))
.mul(config.gasLimitMultiplier)
.div(100);
Expand Down
18 changes: 18 additions & 0 deletions src/modes/intraOrderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ export async function dryrun({
try {
blockNumber = Number(await viemClient.getBlockNumber());
spanAttributes["blockNumber"] = blockNumber;
try {
gasPrice = ethers.BigNumber.from(await viemClient.getGasPrice())
.mul(config.gasPriceMultiplier)
.div("100")
.toBigInt();
rawtx.gasPrice = gasPrice;
} catch {
/**/
}
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx))
.mul(config.gasLimitMultiplier)
.div(100);
Expand Down Expand Up @@ -160,6 +169,15 @@ export async function dryrun({
try {
blockNumber = Number(await viemClient.getBlockNumber());
spanAttributes["blockNumber"] = blockNumber;
try {
gasPrice = ethers.BigNumber.from(await viemClient.getGasPrice())
.mul(config.gasPriceMultiplier)
.div("100")
.toBigInt();
rawtx.gasPrice = gasPrice;
} catch {
/**/
}
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx))
.mul(config.gasLimitMultiplier)
.div(100);
Expand Down
18 changes: 18 additions & 0 deletions src/modes/routeProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ export async function dryrun({
try {
blockNumber = Number(await viemClient.getBlockNumber());
spanAttributes["blockNumber"] = blockNumber;
try {
gasPrice = ethers.BigNumber.from(await viemClient.getGasPrice())
.mul(config.gasPriceMultiplier)
.div("100")
.toBigInt();
rawtx.gasPrice = gasPrice;
} catch {
/**/
}
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx))
.mul(config.gasLimitMultiplier)
.div(100);
Expand Down Expand Up @@ -220,6 +229,15 @@ export async function dryrun({
try {
blockNumber = Number(await viemClient.getBlockNumber());
spanAttributes["blockNumber"] = blockNumber;
try {
gasPrice = ethers.BigNumber.from(await viemClient.getGasPrice())
.mul(config.gasPriceMultiplier)
.div("100")
.toBigInt();
rawtx.gasPrice = gasPrice;
} catch {
/**/
}
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx))
.mul(config.gasLimitMultiplier)
.div(100);
Expand Down
17 changes: 12 additions & 5 deletions src/processOrders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { BigNumber, Contract, ethers } from "ethers";
import { Tracer } from "@opentelemetry/sdk-trace-base";
import { Context, SpanStatusCode } from "@opentelemetry/api";
import { fundOwnedOrders, getNonce, rotateAccounts } from "./account";
import { containsNodeError, ErrorSeverity, errorSnapshot } from "./error";
import { containsNodeError, ErrorSeverity, errorSnapshot, handleRevert } from "./error";
import {
Report,
BotConfig,
Expand Down Expand Up @@ -326,11 +326,13 @@ export const processOrders = async (
} else if (e.reason === ProcessPairHaltReason.TxReverted) {
// Tx reverted onchain, this can happen for example
// because of mev front running or false positive opportunities, etc
let message = "transaction reverted onchain";
if (e.error) {
message = errorSnapshot(message, e.error);
span.setAttribute("errorDetails", message);
}
span.setAttribute("severity", ErrorSeverity.HIGH);
span.setStatus({
code: SpanStatusCode.ERROR,
message: "transaction reverted onchain",
});
span.setStatus({ code: SpanStatusCode.ERROR, message });
span.setAttribute("unsuccessfulClear", true);
span.setAttribute("txReverted", true);
} else if (e.reason === ProcessPairHaltReason.TxMineFailed) {
Expand Down Expand Up @@ -777,6 +779,11 @@ export async function processPair(args: {
return result;
} else {
// keep track of gas consumption of the account
const simulation = await handleRevert(config.viemClient as any, txhash);
if (simulation) {
result.error = simulation.err;
spanAttributes["txNoneNodeError"] = !simulation.nodeError;
}
result.report = {
status: ProcessPairReportStatus.FoundOpportunity,
txUrl,
Expand Down
1 change: 1 addition & 0 deletions test/abis/RouteProcessor4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"abi":[{"inputs":[{"internalType":"address","name":"_bentoBox","type":"address"},{"internalType":"address[]","name":"priviledgedUserList","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"MinimalOutputBalanceViolation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Route","type":"event"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"algebraSwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bentoBox","outputs":[{"internalType":"contract IBentoBoxMinimal","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"pancakeV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"priviledgedUsers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"route","type":"bytes"}],"name":"processRoute","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"priviledge","type":"bool"}],"name":"setPriviledge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"transferValueTo","type":"address"},{"internalType":"uint256","name":"amountValueTransfer","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"route","type":"bytes"}],"name":"transferValueAndprocessRoute","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]}

0 comments on commit 47653c1

Please sign in to comment.