Skip to content

Commit

Permalink
Merge pull request #282 from rainlanguage/2024-12-12-minor-update
Browse files Browse the repository at this point in the history
scoped otel k/v
  • Loading branch information
rouzwelt authored Jan 3, 2025
2 parents 1bd3166 + e6d7ce8 commit 4828f68
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 73 deletions.
15 changes: 11 additions & 4 deletions src/modes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { findOpp as findInterObOpp } from "./interOrderbook";
import { findOpp as findIntraObOpp } from "./intraOrderbook";
import { findOppWithRetries as findRpOpp } from "./routeProcessor";
import { BotConfig, BundledOrders, ViemClient, DryrunResult, SpanAttrs } from "../types";
import { extendSpanAttributes } from "../utils";

/**
* The main entrypoint for the main logic to find opps.
Expand Down Expand Up @@ -116,18 +117,24 @@ export async function findOpp({
noneNodeError: undefined,
};
if ((allResults[0] as any)?.reason?.spanAttributes) {
spanAttributes["route-processor"] = JSON.stringify(
extendSpanAttributes(
spanAttributes,
(allResults[0] as any).reason.spanAttributes,
"routeProcessor",
);
}
if ((allResults[1] as any)?.reason?.spanAttributes) {
spanAttributes["intra-orderbook"] = JSON.stringify(
(allResults[1] as any).reason.spanAttributes["intraOrderbook"],
extendSpanAttributes(
spanAttributes,
(allResults[1] as any).reason.spanAttributes,
"intraOrderbook",
);
}
if ((allResults[2] as any)?.reason?.spanAttributes) {
spanAttributes["inter-orderbook"] = JSON.stringify(
extendSpanAttributes(
spanAttributes,
(allResults[2] as any).reason.spanAttributes,
"interOrderbook",
);
}
if ((allResults[0] as any)?.reason?.value?.noneNodeError) {
Expand Down
17 changes: 12 additions & 5 deletions src/modes/interOrderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import { BaseError, PublicClient } from "viem";
import { getBountyEnsureBytecode } from "../config";
import { BigNumber, Contract, ethers } from "ethers";
import { containsNodeError, errorSnapshot } from "../error";
import { estimateProfit, ONE18, scale18To, withBigintSerializer } from "../utils";
import { BotConfig, BundledOrders, ViemClient, DryrunResult, SpanAttrs } from "../types";
import {
ONE18,
scale18To,
estimateProfit,
withBigintSerializer,
extendSpanAttributes,
} from "../utils";

/**
* Executes a extimateGas call for an inter-orderbook arb() tx, to determine if the tx is successfull ot not
Expand Down Expand Up @@ -351,12 +357,13 @@ export async function findOpp({
// } catch {
// /**/
// }
const allOrderbooksAttributes: any = {};
for (let i = 0; i < e.errors.length; i++) {
allOrderbooksAttributes[opposingOrderbookOrders[i].orderbook] =
e.errors[i].spanAttributes;
extendSpanAttributes(
spanAttributes,
e.errors[i].spanAttributes,
"againstOrderbooks." + opposingOrderbookOrders[i].orderbook,
);
}
spanAttributes["againstOrderbooks"] = JSON.stringify(allOrderbooksAttributes);
const noneNodeErrors = allNoneNodeErrors.filter((v) => !!v);
if (allNoneNodeErrors.length && noneNodeErrors.length / allNoneNodeErrors.length > 0.5) {
result.value = {
Expand Down
6 changes: 2 additions & 4 deletions src/modes/intraOrderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BigNumber, ethers } from "ethers";
import { getWithdrawEnsureBytecode } from "../config";
import { BaseError, erc20Abi, PublicClient } from "viem";
import { containsNodeError, errorSnapshot } from "../error";
import { estimateProfit, scale18, withBigintSerializer } from "../utils";
import { estimateProfit, scale18, withBigintSerializer, extendSpanAttributes } from "../utils";
import {
SpanAttrs,
BotConfig,
Expand Down Expand Up @@ -292,7 +292,6 @@ export async function findOpp({
);
if (!opposingOrders || !opposingOrders.length) throw undefined;

const allErrorAttributes: string[] = [];
const allNoneNodeErrors: (string | undefined)[] = [];
const inputBalance = scale18(
await viemClient.readContract({
Expand Down Expand Up @@ -329,10 +328,9 @@ export async function findOpp({
});
} catch (e: any) {
allNoneNodeErrors.push(e?.value?.noneNodeError);
allErrorAttributes.push(JSON.stringify(e.spanAttributes));
extendSpanAttributes(spanAttributes, e.spanAttributes, "intraOrderbook." + i);
}
}
spanAttributes["intraOrderbook"] = allErrorAttributes;
const noneNodeErrors = allNoneNodeErrors.filter((v) => !!v);
if (allNoneNodeErrors.length && noneNodeErrors.length / allNoneNodeErrors.length > 0.5) {
result.value = {
Expand Down
10 changes: 3 additions & 7 deletions src/modes/routeProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
estimateProfit,
visualizeRoute,
withBigintSerializer,
extendSpanAttributes,
} from "../utils";

/**
Expand Down Expand Up @@ -342,8 +343,6 @@ export async function findOpp({
ethers.constants.Zero,
);
const maximumInput = BigNumber.from(initAmount.toString());

const allHopsAttributes: string[] = [];
const allNoneNodeErrors: (string | undefined)[] = [];
try {
return await dryrun({
Expand All @@ -366,7 +365,7 @@ export async function findOpp({
// the fail reason can only be no route in case all hops fail reasons are no route
if (e.reason !== RouteProcessorDryrunHaltReason.NoRoute) noRoute = false;
allNoneNodeErrors.push(e?.value?.noneNodeError);
allHopsAttributes.push(JSON.stringify(e.spanAttributes));
extendSpanAttributes(spanAttributes, e.spanAttributes, "full");
}
if (!hasPriceMatch.value) {
const maxTradeSize = findMaxInput({
Expand Down Expand Up @@ -398,14 +397,11 @@ export async function findOpp({
} catch (e: any) {
// the fail reason can only be no route in case all hops fail reasons are no route
if (e.reason !== RouteProcessorDryrunHaltReason.NoRoute) noRoute = false;
delete e.spanAttributes["rawtx"];
allNoneNodeErrors.push(e?.value?.noneNodeError);
allHopsAttributes.push(JSON.stringify(e.spanAttributes));
extendSpanAttributes(spanAttributes, e.spanAttributes, "partial");
}
}
}
// in case of no successfull hop, allHopsAttributes will be included
spanAttributes["hops"] = allHopsAttributes;

if (noRoute) result.reason = RouteProcessorDryrunHaltReason.NoRoute;
else {
Expand Down
14 changes: 10 additions & 4 deletions src/processOrders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,17 @@ export const processOrders = async (
}

for (const { settle, pair, orderPairObject } of results) {
// instantiate a span for this pair
const span = tracer.startSpan(`order_${pair}`, undefined, ctx);
span.setAttribute("details.owner", orderPairObject.takeOrders[0].takeOrder.order.owner);
try {
// settle the process results
// this will return the report of the operation and in case
// there was a revert tx, it will try to simulate it and find
// the root cause as well
const result = await settle();

// instantiate a span for this pair
const span = tracer.startSpan(`order_${pair}`, undefined, ctx);
span.setAttribute("details.owner", orderPairObject.takeOrders[0].takeOrder.order.owner);

// keep track of avg gas cost
if (result.gasCost) {
txGasCosts.push(result.gasCost);
Expand All @@ -264,7 +265,12 @@ export const processOrders = async (
span.setAttribute("severity", ErrorSeverity.HIGH);
span.setStatus({ code: SpanStatusCode.ERROR, message: "unexpected error" });
}
span.end();
} catch (e: any) {
// instantiate a span for this pair
const span = tracer.startSpan(`order_${pair}`, undefined, ctx);
span.setAttribute("details.owner", orderPairObject.takeOrders[0].takeOrder.order.owner);

// set the span attributes with the values gathered at processPair()
span.setAttributes(e.spanAttributes);

Expand Down Expand Up @@ -387,8 +393,8 @@ export const processOrders = async (
span.setAttribute("severity", ErrorSeverity.HIGH);
span.setStatus({ code: SpanStatusCode.ERROR, message });
}
span.end();
}
span.end();
}
return {
reports,
Expand Down
18 changes: 18 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1399,3 +1399,21 @@ export function scale18To(value: BigNumberish, targetDecimals: BigNumberish): Bi
return BigNumber.from(value).div("1" + "0".repeat(18 - decimals));
}
}

/**
* Adds the given k/v pairs to the spanAttributes by prepending the key with given header
*/
export function extendSpanAttributes(
spanAttributes: Record<string, any>,
newAttributes: Record<string, any>,
header: string,
excludeHeaderForKeys: string[] = [],
) {
for (const attrKey in newAttributes) {
if (!excludeHeaderForKeys.includes(attrKey)) {
spanAttributes[header + "." + attrKey] = newAttributes[attrKey];
} else {
spanAttributes[attrKey] = newAttributes[attrKey];
}
}
}
136 changes: 115 additions & 21 deletions test/findOpp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ const { assert } = require("chai");
const testData = require("./data");
const { findOpp } = require("../src/modes");
const { orderbookAbi } = require("../src/abis");
const { clone, estimateProfit } = require("../src/utils");
const { errorSnapshot } = require("../src/error");
const { clone, estimateProfit, withBigintSerializer } = require("../src/utils");
const {
ethers,
utils: { formatUnits },
Expand Down Expand Up @@ -322,7 +323,7 @@ describe("Test find opp", async function () {
assert.deepEqual(result, expected);
});

it("should NOT find opp", async function () {
it.only("should NOT find opp", async function () {
const err = ethers.errors.UNPREDICTABLE_GAS_LIMIT;
signer.estimateGas = async () => {
return Promise.reject(err);
Expand All @@ -348,31 +349,124 @@ describe("Test find opp", async function () {
});
assert.fail("expected to reject, but resolved");
} catch (error) {
const expectedTakeOrdersConfigStruct = {
minimumInput: ethers.constants.One,
maximumInput: ethers.constants.MaxUint256,
maximumIORatio: ethers.constants.MaxUint256,
orders: [orderPairObject.takeOrders[0].takeOrder],
data: expectedRouteData,
};
const task = {
evaluable: {
interpreter:
orderPairObject.takeOrders[0].takeOrder.order.evaluable.interpreter,
store: orderPairObject.takeOrders[0].takeOrder.order.evaluable.store,
bytecode: getBountyEnsureBytecode(
ethers.utils.parseUnits(inputToEthPrice),
ethers.constants.Zero,
ethers.constants.Zero,
signer.account.address,
),
},
signedContext: [],
};
const rawtx = JSON.stringify(
{
data: arb.interface.encodeFunctionData("arb3", [
orderPairObject.orderbook,
expectedTakeOrdersConfigStruct,
task,
]),
to: arb.address,
gasPrice: gasPrice,
from: signer.account.address,
},
withBigintSerializer,
);
const opposingMaxInput = vaultBalance
.mul(orderPairObject.takeOrders[0].quote.ratio)
.div(`1${"0".repeat(36 - orderPairObject.buyTokenDecimals)}`);
const opposingMaxIORatio = ethers.BigNumber.from(`1${"0".repeat(36)}`).div(
orderPairObject.takeOrders[0].quote.ratio,
);
const obInterface = new ethers.utils.Interface(orderbookAbi);
const encodedFN = obInterface.encodeFunctionData("takeOrders2", [
{
minimumInput: ethers.constants.One,
maximumInput: opposingMaxInput,
maximumIORatio: opposingMaxIORatio,
orders: opposingOrderPairObject.takeOrders.map((v) => v.takeOrder),
data: "0x",
},
]);
const expectedTakeOrdersConfigStruct2 = {
minimumInput: ethers.constants.One,
maximumInput: ethers.constants.MaxUint256,
maximumIORatio: ethers.constants.MaxUint256,
orders: [orderPairObject.takeOrders[0].takeOrder],
data: ethers.utils.defaultAbiCoder.encode(
["address", "address", "bytes"],
[
opposingOrderPairObject.orderbook,
opposingOrderPairObject.orderbook,
encodedFN,
],
),
};
const task2 = {
evaluable: {
interpreter:
orderPairObject.takeOrders[0].takeOrder.order.evaluable.interpreter,
store: orderPairObject.takeOrders[0].takeOrder.order.evaluable.store,
bytecode: getBountyEnsureBytecode(
ethers.utils.parseUnits(inputToEthPrice),
ethers.utils.parseUnits(outputToEthPrice),
ethers.constants.Zero,
signer.account.address,
),
},
signedContext: [],
};
const rawtx2 = {
data: arb.interface.encodeFunctionData("arb3", [
orderPairObject.orderbook,
expectedTakeOrdersConfigStruct2,
task2,
]),
to: arb.address,
gasPrice,
from: signer.account.address,
};
const expected = {
rawtx: undefined,
oppBlockNumber: undefined,
noneNodeError: errorSnapshot("", err),
spanAttributes: {
"route-processor": {
hops: [
`{"amountIn":"${formatUnits(vaultBalance)}","marketPrice":"${formatUnits(getCurrentPrice(vaultBalance))}","route":${JSON.stringify(expectedRouteVisual)},"blockNumber":${oppBlockNumber},"error":"${ethers.errors.UNPREDICTABLE_GAS_LIMIT}"}`,
`{"amountIn":"${formatUnits(vaultBalance.div(2))}","marketPrice":"${formatUnits(getCurrentPrice(vaultBalance.div(2)))}","route":${JSON.stringify(expectedRouteVisual)},"blockNumber":${oppBlockNumber}}`,
`{"amountIn":"${formatUnits(vaultBalance.div(4))}","marketPrice":"${formatUnits(getCurrentPrice(vaultBalance.div(4)))}","route":${JSON.stringify(expectedRouteVisual)},"blockNumber":${oppBlockNumber}}`,
],
},
"inter-orderbook": {
againstOrderbooks: {
[opposingOrderbookAddress]: {
amountIn: formatUnits(vaultBalance),
amountOut: formatUnits(getAmountOut(vaultBalance), 6),
blockNumber: oppBlockNumber,
error: err,
},
},
},
// rp span attrs
"routeProcessor.full.stage": 1,
"routeProcessor.full.rawtx": rawtx,
"routeProcessor.full.isNodeError": false,
"routeProcessor.full.route": expectedRouteVisual,
"routeProcessor.full.blockNumber": oppBlockNumber,
"routeProcessor.full.error": errorSnapshot("", err),
"routeProcessor.full.amountIn": formatUnits(vaultBalance),
"routeProcessor.full.amountOut": formatUnits(getAmountOut(vaultBalance), 6),
"routeProcessor.full.marketPrice": formatUnits(getCurrentPrice(vaultBalance)),

// inter-ob span attrs
[`interOrderbook.againstOrderbooks.${opposingOrderbookAddress}.stage`]: 1,
[`interOrderbook.againstOrderbooks.${opposingOrderbookAddress}.isNodeError`]: false,
[`interOrderbook.againstOrderbooks.${opposingOrderbookAddress}.blockNumber`]:
oppBlockNumber,
[`interOrderbook.againstOrderbooks.${opposingOrderbookAddress}.rawtx`]:
JSON.stringify(rawtx2, withBigintSerializer),
[`interOrderbook.againstOrderbooks.${opposingOrderbookAddress}.maxInput`]:
vaultBalance.toString(),
[`interOrderbook.againstOrderbooks.${opposingOrderbookAddress}.error`]:
errorSnapshot("", err),
},
};
assert.deepEqual(error.rawtx, expected.rawtx);
assert.deepEqual(error.oppBlockNumber, expected.oppBlockNumber);
assert.deepEqual(error, expected);
}
});
});
17 changes: 7 additions & 10 deletions test/mode-interOrderbook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,16 +338,13 @@ describe("Test inter-orderbook find opp", async function () {
},
reason: undefined,
spanAttributes: {
againstOrderbooks: JSON.stringify({
[opposingOrderbookAddress]: {
maxInput: vaultBalance.toString(),
blockNumber: oppBlockNumber,
stage: 1,
isNodeError: false,
error: errorSnapshot("", err),
rawtx: JSON.stringify(rawtx),
},
}),
[`againstOrderbooks.${opposingOrderbookAddress}.blockNumber`]: oppBlockNumber,
[`againstOrderbooks.${opposingOrderbookAddress}.stage`]: 1,
[`againstOrderbooks.${opposingOrderbookAddress}.isNodeError`]: false,
[`againstOrderbooks.${opposingOrderbookAddress}.error`]: errorSnapshot("", err),
[`againstOrderbooks.${opposingOrderbookAddress}.rawtx`]: JSON.stringify(rawtx),
[`againstOrderbooks.${opposingOrderbookAddress}.maxInput`]:
vaultBalance.toString(),
},
};
assert.deepEqual(error, expected);
Expand Down
Loading

0 comments on commit 4828f68

Please sign in to comment.