Skip to content

Commit

Permalink
Merge pull request #249 from rainlanguage/2024-11-06-node-error-report
Browse files Browse the repository at this point in the history
report on error being  from eth node
  • Loading branch information
rouzwelt authored Nov 10, 2024
2 parents d16c425 + 947c75f commit 64cce4d
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 35 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Other optional arguments are:
- `-V` or `--version`, output the version number
- `-h` or `--help`, output usage information

<br>
<br>

### List of available supported dexes (decentralized exchanges)
- all of the below names are case INSENSITIVE:
Expand Down
6 changes: 3 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function createViemClient(
): Promise<ViemClient> {
const transport =
!rpcs || rpcs?.length === 0
? fallback(fallbacks[chainId].transport, { rank: false, retryCount: 6 })
? fallback(fallbacks[chainId].transport, { rank: false, retryCount: 3 })
: useFallbacks
? fallback(
[
Expand All @@ -92,7 +92,7 @@ export async function createViemClient(
),
...fallbacks[chainId].transport,
],
{ rank: false, retryCount: 6 },
{ rank: false, retryCount: 3 },
)
: fallback(
rpcs.map((v) =>
Expand All @@ -102,7 +102,7 @@ export async function createViemClient(
onFetchResponse: config?.onFetchResponse,
}),
),
{ rank: false, retryCount: 6 },
{ rank: false, retryCount: 3 },
);

return testClient
Expand Down
26 changes: 25 additions & 1 deletion src/error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { BaseError } from "viem";
import {
BaseError,
RpcRequestError,
InvalidInputRpcError,
ExecutionRevertedError,
TransactionRejectedRpcError,
} from "viem";

/**
* Specifies error severity
Expand Down Expand Up @@ -32,3 +38,21 @@ export function errorSnapshot(header: string, err: any): string {
}
return message.join("\n");
}

/**
* Checks if a viem BaseError is from eth node, copied from
* "viem/_types/utils/errors/getNodeError" since not a default export
*/
export function containsNodeError(err: BaseError): boolean {
try {
return (
err instanceof TransactionRejectedRpcError ||
err instanceof InvalidInputRpcError ||
err instanceof ExecutionRevertedError ||
(err instanceof RpcRequestError && err.code === ExecutionRevertedError.code) ||
("cause" in err && containsNodeError(err.cause as any))
);
} catch (error) {
return false;
}
}
14 changes: 14 additions & 0 deletions src/modes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export async function findOpp({
spanAttributes,
rawtx: undefined,
oppBlockNumber: undefined,
noneNodeError: undefined,
};
if ((allResults[0] as any)?.reason?.spanAttributes) {
spanAttributes["route-processor"] = JSON.stringify(
Expand All @@ -114,6 +115,19 @@ export async function findOpp({
(allResults[2] as any).reason.spanAttributes,
);
}
if ((allResults[0] as any)?.reason?.value?.noneNodeError) {
result.noneNodeError = (allResults[0] as any).reason.value.noneNodeError;
} else if (
result.noneNodeError === undefined &&
(allResults[1] as any)?.reason?.value?.noneNodeError
) {
result.noneNodeError = (allResults[1] as any).reason.value.noneNodeError;
} else if (
result.noneNodeError === undefined &&
(allResults[2] as any)?.reason?.value?.noneNodeError
) {
result.noneNodeError = (allResults[2] as any).reason.value.noneNodeError;
}
throw result;
}
}
37 changes: 33 additions & 4 deletions src/modes/interOrderbook.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { PublicClient } from "viem";
import { orderbookAbi } from "../abis";
import { errorSnapshot } from "../error";
import { BaseError, PublicClient } from "viem";
import { getBountyEnsureBytecode } from "../config";
import { BigNumber, Contract, ethers } from "ethers";
import { containsNodeError, errorSnapshot } from "../error";
import { estimateProfit, withBigintSerializer } from "../utils";
import { BotConfig, BundledOrders, ViemClient, DryrunResult, SpanAttrs } from "../types";

Expand Down Expand Up @@ -104,14 +104,23 @@ export async function dryrun({
spanAttributes["blockNumber"] = blockNumber;
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx));
} catch (e) {
spanAttributes["error"] = errorSnapshot("", e);
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
spanAttributes["isNodeError"] = isNodeError;
spanAttributes["error"] = errMsg;
spanAttributes["rawtx"] = JSON.stringify(
{
...rawtx,
from: signer.account.address,
},
withBigintSerializer,
);
if (!isNodeError) {
result.value = {
noneNodeError: errMsg,
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
let gasCost = gasLimit.mul(gasPrice);
Expand Down Expand Up @@ -149,14 +158,23 @@ export async function dryrun({
task,
]);
} catch (e) {
spanAttributes["error"] = errorSnapshot("", e);
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
spanAttributes["isNodeError"] = isNodeError;
spanAttributes["error"] = errMsg;
spanAttributes["rawtx"] = JSON.stringify(
{
...rawtx,
from: signer.account.address,
},
withBigintSerializer,
);
if (!isNodeError) {
result.value = {
noneNodeError: errMsg,
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
}
Expand Down Expand Up @@ -213,6 +231,7 @@ export async function findOpp({
reason: undefined,
spanAttributes,
};
const allNoneNodeErrors: (string | undefined)[] = [];

const opposingOrderbookOrders = orderbooksOrders
.map((v) => {
Expand Down Expand Up @@ -266,6 +285,9 @@ export async function findOpp({
}),
);
} catch (e: any) {
for (const err of (e as AggregateError).errors) {
allNoneNodeErrors.push(err?.value?.noneNodeError);
}
maximumInput = maximumInput.div(2);
try {
// try to find the first resolving binary search
Expand Down Expand Up @@ -303,6 +325,13 @@ export async function findOpp({
e.errors[i].spanAttributes;
}
spanAttributes["againstOrderbooks"] = JSON.stringify(allOrderbooksAttributes);
const noneNodeErrors = allNoneNodeErrors.filter((v) => !!v);
if (allNoneNodeErrors.length && noneNodeErrors.length / allNoneNodeErrors.length > 0.5) {
result.value = {
noneNodeError: noneNodeErrors[0],
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
}
Expand Down
35 changes: 31 additions & 4 deletions src/modes/intraOrderbook.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { PublicClient } from "viem";
import { errorSnapshot } from "../error";
import { BigNumber, ethers } from "ethers";
import { BaseError, PublicClient } from "viem";
import { erc20Abi, orderbookAbi } from "../abis";
import { getWithdrawEnsureBytecode } from "../config";
import { containsNodeError, errorSnapshot } from "../error";
import { estimateProfit, withBigintSerializer } from "../utils";
import {
BotConfig,
Expand Down Expand Up @@ -92,14 +92,23 @@ export async function dryrun({
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx));
} catch (e) {
// reason, code, method, transaction, error, stack, message
spanAttributes["error"] = errorSnapshot("", e);
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
spanAttributes["isNodeError"] = isNodeError;
spanAttributes["error"] = errMsg;
spanAttributes["rawtx"] = JSON.stringify(
{
...rawtx,
from: signer.account.address,
},
withBigintSerializer,
);
if (!isNodeError) {
result.value = {
noneNodeError: errMsg,
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
let gasCost = gasLimit.mul(gasPrice);
Expand Down Expand Up @@ -162,14 +171,23 @@ export async function dryrun({
[clear2Calldata, withdrawInputCalldata, withdrawOutputCalldata],
]);
} catch (e) {
spanAttributes["error"] = errorSnapshot("", e);
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
spanAttributes["isNodeError"] = isNodeError;
spanAttributes["error"] = errMsg;
spanAttributes["rawtx"] = JSON.stringify(
{
...rawtx,
from: signer.account.address,
},
withBigintSerializer,
);
if (!isNodeError) {
result.value = {
noneNodeError: errMsg,
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
}
Expand Down Expand Up @@ -248,6 +266,7 @@ export async function findOpp({
if (!opposingOrders || !opposingOrders.length) throw undefined;

const allErrorAttributes: string[] = [];
const allNoneNodeErrors: (string | undefined)[] = [];
const erc20 = new ethers.utils.Interface(erc20Abi);
const inputBalance = ethers.BigNumber.from(
(
Expand Down Expand Up @@ -284,9 +303,17 @@ export async function findOpp({
outputBalance,
});
} catch (e: any) {
allNoneNodeErrors.push(e?.value?.noneNodeError);
allErrorAttributes.push(JSON.stringify(e.spanAttributes));
}
}
spanAttributes["intraOrderbook"] = allErrorAttributes;
const noneNodeErrors = allNoneNodeErrors.filter((v) => !!v);
if (allNoneNodeErrors.length && noneNodeErrors.length / allNoneNodeErrors.length > 0.5) {
result.value = {
noneNodeError: noneNodeErrors[0],
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
48 changes: 43 additions & 5 deletions src/modes/routeProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { PublicClient } from "viem";
import { Token } from "sushi/currency";
import { errorSnapshot } from "../error";
import { BaseError, PublicClient } from "viem";
import { getBountyEnsureBytecode } from "../config";
import { ChainId, DataFetcher, Router } from "sushi";
import { BigNumber, Contract, ethers } from "ethers";
import { containsNodeError, errorSnapshot } from "../error";
import { BotConfig, BundledOrders, ViemClient, DryrunResult, SpanAttrs } from "../types";
import { estimateProfit, RPoolFilter, visualizeRoute, withBigintSerializer } from "../utils";

Expand Down Expand Up @@ -170,7 +170,10 @@ export async function dryrun({
gasLimit = ethers.BigNumber.from(await signer.estimateGas(rawtx));
} catch (e) {
// reason, code, method, transaction, error, stack, message
spanAttributes["error"] = errorSnapshot("", e);
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
spanAttributes["isNodeError"] = isNodeError;
spanAttributes["error"] = errMsg;
spanAttributes["rawtx"] = JSON.stringify(
{
...rawtx,
Expand All @@ -179,6 +182,12 @@ export async function dryrun({
withBigintSerializer,
);
result.reason = RouteProcessorDryrunHaltReason.NoOpportunity;
if (!isNodeError) {
result.value = {
noneNodeError: errMsg,
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
let gasCost = gasLimit.mul(gasPrice);
Expand Down Expand Up @@ -216,7 +225,10 @@ export async function dryrun({
task,
]);
} catch (e) {
spanAttributes["error"] = errorSnapshot("", e);
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
spanAttributes["isNodeError"] = isNodeError;
spanAttributes["error"] = errMsg;
spanAttributes["rawtx"] = JSON.stringify(
{
...rawtx,
Expand All @@ -225,6 +237,12 @@ export async function dryrun({
withBigintSerializer,
);
result.reason = RouteProcessorDryrunHaltReason.NoOpportunity;
if (!isNodeError) {
result.value = {
noneNodeError: errMsg,
estimatedProfit: ethers.constants.Zero,
};
}
return Promise.reject(result);
}
}
Expand Down Expand Up @@ -300,6 +318,7 @@ export async function findOpp({

const allSuccessHops: DryrunResult[] = [];
const allHopsAttributes: string[] = [];
const allNoneNodeErrors: (string | undefined)[] = [];
for (let i = 1; i < config.hops + 1; i++) {
try {
const dryrunResult = await dryrun({
Expand Down Expand Up @@ -337,6 +356,7 @@ export async function findOpp({
delete e.spanAttributes["error"];
delete e.spanAttributes["rawtx"];
}
allNoneNodeErrors.push(e?.value?.noneNodeError);
allHopsAttributes.push(JSON.stringify(e.spanAttributes));

// set the maxInput for next hop by decreasing
Expand All @@ -351,7 +371,19 @@ export async function findOpp({
spanAttributes["hops"] = allHopsAttributes;

if (noRoute) result.reason = RouteProcessorDryrunHaltReason.NoRoute;
else result.reason = RouteProcessorDryrunHaltReason.NoOpportunity;
else {
const noneNodeErrors = allNoneNodeErrors.filter((v) => !!v);
if (
allNoneNodeErrors.length &&
noneNodeErrors.length / allNoneNodeErrors.length > 0.5
) {
result.value = {
noneNodeError: noneNodeErrors[0],
estimatedProfit: ethers.constants.Zero,
};
}
result.reason = RouteProcessorDryrunHaltReason.NoOpportunity;
}

return Promise.reject(result);
}
Expand Down Expand Up @@ -439,6 +471,12 @@ export async function findOppWithRetries({
for (const attrKey in (allPromises[0] as any).reason.spanAttributes) {
spanAttributes[attrKey] = (allPromises[0] as any).reason.spanAttributes[attrKey];
}
if ((allPromises[0] as any)?.reason?.value?.noneNodeError) {
result.value = {
noneNodeError: (allPromises[0] as any).reason.value.noneNodeError,
estimatedProfit: ethers.constants.Zero,
};
}
result.reason = RouteProcessorDryrunHaltReason.NoOpportunity;
throw result;
}
Expand Down
Loading

0 comments on commit 64cce4d

Please sign in to comment.