From a13a0e909f9cb5bd1f08435b9e8d29f273d473b1 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Tue, 3 Dec 2024 01:50:12 +0000 Subject: [PATCH] init --- README.md | 4 +++ example.env | 3 ++ package-lock.json | 8 ++--- package.json | 2 +- src/cli.ts | 15 ++++++++ src/index.ts | 1 + src/modes/interOrderbook.ts | 21 ++++++++--- src/modes/intraOrderbook.ts | 3 ++ src/processOrders.ts | 13 ++----- src/query.ts | 5 +-- src/types.ts | 2 ++ src/utils.ts | 4 +++ test/e2e/e2e.test.js | 71 +++++++++++++++++++------------------ 13 files changed, 94 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index ce610a66..f0b0118b 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ Other optional arguments are: - `--gas-price-multiplier`, Option to multiply the gas price fetched from the rpc as percentage, default is 107, ie +7%. Will override the 'GAS_PRICE_MULTIPLIER' in env variables - `--gas-limit-multiplier`, Option to multiply the gas limit estimation from the rpc as percentage, default is 105, ie +5%. Will override the 'GAS_LIMIT_MULTIPLIER' in env variables - `--tx-gas`, Option to set a static gas limit for all submitting txs. Will override the 'TX_GAS' in env variables +- `--quote-gas`, Option to set a static gas limit for quote read calls, default is 1 milion. Will override the 'QUOTE_GAS' in env variables - `-V` or `--version`, output the version number - `-h` or `--help`, output usage information @@ -267,6 +268,9 @@ GAS_LIMIT_MULTIPLIER= # Option to set a static gas limit for all submitting txs TX_GAS= + +# Option to set a static gas limit for quote read calls, default is 1 milion +QUOTE_GAS= ``` If both env variables and CLI argument are set, the CLI arguments will be prioritized and override the env variables. diff --git a/example.env b/example.env index 61d8b14a..5ae79ac6 100644 --- a/example.env +++ b/example.env @@ -94,6 +94,9 @@ GAS_LIMIT_MULTIPLIER= # Option to set a static gas limit for all submitting txs TX_GAS= +# Option to set a static gas limit for quote read calls, default is 1 milion +QUOTE_GAS= + # test rpcs vars TEST_POLYGON_RPC= diff --git a/package-lock.json b/package-lock.json index e083100b..fdddfcac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@opentelemetry/resources": "^1.22.0", "@opentelemetry/sdk-trace-base": "^1.22.0", "@opentelemetry/semantic-conventions": "^1.22.0", - "@rainlanguage/orderbook": "^0.0.1-alpha.1", + "@rainlanguage/orderbook": "^0.0.1-alpha.6", "axios": "^1.3.4", "commander": "^11.0.0", "dotenv": "^16.0.3", @@ -3461,9 +3461,9 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@rainlanguage/orderbook": { - "version": "0.0.1-alpha.1", - "resolved": "https://registry.npmjs.org/@rainlanguage/orderbook/-/orderbook-0.0.1-alpha.1.tgz", - "integrity": "sha512-t7SYpjASwZEyowXH1rVs6LQPwJGUe5rTHWuHQZJq6eJfjZeDwZi5plxtIAwnMeqKrVSMdbAvPZMn0MpfdpctAQ==", + "version": "0.0.1-alpha.6", + "resolved": "https://registry.npmjs.org/@rainlanguage/orderbook/-/orderbook-0.0.1-alpha.6.tgz", + "integrity": "sha512-qGPzlDh0ZMlkPeHS048k0VlhhVIhrB7Ea/UIAZENBai3AYA394Cl6MWu66JNdia9RhVTCApSqRQwmmwLFxb2DA==", "dependencies": { "buffer": "^6.0.3" }, diff --git a/package.json b/package.json index 43e665ec..39e70ab9 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@opentelemetry/resources": "^1.22.0", "@opentelemetry/sdk-trace-base": "^1.22.0", "@opentelemetry/semantic-conventions": "^1.22.0", - "@rainlanguage/orderbook": "^0.0.1-alpha.1", + "@rainlanguage/orderbook": "^0.0.1-alpha.6", "axios": "^1.3.4", "commander": "^11.0.0", "dotenv": "^16.0.3", diff --git a/src/cli.ts b/src/cli.ts index 8e77a27c..5ea03498 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -71,6 +71,7 @@ const ENV_OPTIONS = { gasPriceMultiplier: process?.env?.GAS_PRICE_MULTIPLIER, gasLimitMultiplier: process?.env?.GAS_LIMIT_MULTIPLIER, txGas: process?.env?.TX_GAS, + quoteGas: process?.env?.QUOTE_GAS, route: process?.env?.ROUTE, ownerProfile: process?.env?.OWNER_PROFILE ? Array.from(process?.env?.OWNER_PROFILE.matchAll(/[^,\s]+/g)).map((v) => v[0]) @@ -200,6 +201,10 @@ const getOptions = async (argv: any, version?: string) => { "--tx-gas ", "Option to set a static gas limit for all submitting txs. Will override the 'TX_GAS' in env variables", ) + .option( + "--quote-gas ", + "Option to set a static gas limit for quote read calls, default is 1 milion. Will override the 'QUOTE_GAS' in env variables", + ) .description( [ "A NodeJS app to find and take arbitrage trades for Rain Orderbook orders against some DeFi liquidity providers, requires NodeJS v18 or higher.", @@ -242,6 +247,7 @@ const getOptions = async (argv: any, version?: string) => { cmdOptions.gasLimitMultiplier = cmdOptions.gasLimitMultiplier || getEnv(ENV_OPTIONS.gasLimitMultiplier); cmdOptions.txGas = cmdOptions.txGas || getEnv(ENV_OPTIONS.txGas); + cmdOptions.quoteGas = cmdOptions.quoteGas || getEnv(ENV_OPTIONS.quoteGas); cmdOptions.botMinBalance = cmdOptions.botMinBalance || getEnv(ENV_OPTIONS.botMinBalance); cmdOptions.ownerProfile = cmdOptions.ownerProfile || getEnv(ENV_OPTIONS.ownerProfile); cmdOptions.route = cmdOptions.route || getEnv(ENV_OPTIONS.route); @@ -459,6 +465,15 @@ export async function startup(argv: any, version?: string, tracer?: Tracer, ctx? throw "invalid txGas value, must be an integer greater than zero"; } else throw "invalid txGas value, must be an integer greater than zero"; } + if (options.quoteGas) { + try { + options.quoteGas = BigInt(options.quoteGas); + } catch { + throw "invalid quoteGas value, must be an integer greater than equal zero"; + } + } else { + options.quoteGas = 1_000_000n; // default + } const poolUpdateInterval = _poolUpdateInterval * 60 * 1000; let ordersDetails: SgOrder[] = []; if (!process?.env?.CLI_STARTUP_TEST) { diff --git a/src/index.ts b/src/index.ts index b533d20d..cbdcef89 100644 --- a/src/index.ts +++ b/src/index.ts @@ -209,6 +209,7 @@ export async function getConfig( config.gasPriceMultiplier = options.gasPriceMultiplier; config.gasLimitMultiplier = options.gasLimitMultiplier; config.txGas = options.txGas; + config.quoteGas = options.quoteGas; // init accounts const { mainAccount, accounts } = await initAccounts(walletKey, config, options, tracer, ctx); diff --git a/src/modes/interOrderbook.ts b/src/modes/interOrderbook.ts index 8132d278..d272791d 100644 --- a/src/modes/interOrderbook.ts +++ b/src/modes/interOrderbook.ts @@ -292,12 +292,23 @@ export async function findOpp({ // filter out the same owner orders const opposingOrders = { ...v, - takeOrders: v.takeOrders.filter( - (e) => - e.takeOrder.order.owner.toLowerCase() !== - orderPairObject.takeOrders[0].takeOrder.order.owner.toLowerCase(), - ), + takeOrders: v.takeOrders + .filter( + (e) => + e.takeOrder.order.owner.toLowerCase() !== + orderPairObject.takeOrders[0].takeOrder.order.owner.toLowerCase() && + e.quote && + e.quote.maxOutput.gt(0), + ) + .sort((a, b) => + a.quote!.ratio.lt(b.quote!.ratio) + ? -1 + : a.quote!.ratio.gt(b.quote!.ratio) + ? 1 + : 0, + ), }; + if (!opposingOrders.takeOrders.length) throw ""; return dryrun({ orderPairObject, opposingOrders, diff --git a/src/modes/intraOrderbook.ts b/src/modes/intraOrderbook.ts index 4021b6b8..7d9441b1 100644 --- a/src/modes/intraOrderbook.ts +++ b/src/modes/intraOrderbook.ts @@ -293,6 +293,9 @@ export async function findOpp({ orderPairObject.takeOrders[0].takeOrder.order.owner.toLowerCase() && // only orders that (priceA x priceB < 1) can be profitbale v.quote!.ratio.mul(orderPairObject.takeOrders[0].quote!.ratio).div(ONE).lt(ONE), + ) + .sort((a, b) => + a.quote!.ratio.lt(b.quote!.ratio) ? -1 : a.quote!.ratio.gt(b.quote!.ratio) ? 1 : 0, ); if (!opposingOrders || !opposingOrders.length) throw undefined; diff --git a/src/processOrders.ts b/src/processOrders.ts index a4ec1b27..ffb3f53f 100644 --- a/src/processOrders.ts +++ b/src/processOrders.ts @@ -24,7 +24,6 @@ import { toNumber, getIncome, getEthPrice, - quoteOrders, routeExists, PoolBlackList, getMarketQuote, @@ -151,16 +150,6 @@ export const processOrders = async ( span.end(); }); - // batch quote orders to establish the orders to loop over - try { - await quoteOrders( - bundledOrders, - (config as any).isTest ? (config as any).quoteRpc : config.rpc, - ); - } catch (e) { - throw errorSnapshot("Failed to batch quote orders", e); - } - let avgGasCost: BigNumber | undefined; const reports: Report[] = []; for (const orderbookOrders of bundledOrders) { @@ -460,6 +449,8 @@ export async function processPair(args: { await quoteSingleOrder( orderPairObject, (config as any).isTest ? (config as any).quoteRpc : config.rpc, + undefined, + config.quoteGas, ); if (orderPairObject.takeOrders[0].quote?.maxOutput.isZero()) { result.report = { diff --git a/src/query.ts b/src/query.ts index 19f43c32..f1913d8a 100644 --- a/src/query.ts +++ b/src/query.ts @@ -257,6 +257,7 @@ export async function getOrderChanges( timeout?: number, span?: Span, ) { + timeout; let skip_ = skip; let count = 0; const allResults: SgTx[] = []; @@ -267,7 +268,7 @@ export async function getOrderChanges( const res = await axios.post( subgraph, { query: getTxsQuery(startTimestamp, skip_) }, - { headers: { "Content-Type": "application/json" }, timeout }, + { headers: { "Content-Type": "application/json" } }, ); if (typeof res?.data?.data?.transactions !== "undefined") { const txs = res.data.data.transactions; @@ -282,7 +283,7 @@ export async function getOrderChanges( break; } } catch (error) { - span?.addEvent(errorSnapshot(`Failed to get order changes ${subgraph}`, error)); + span?.addEvent(errorSnapshot(`Failed to get orders changes ${subgraph}`, error)); throw error; } } diff --git a/src/types.ts b/src/types.ts index d2067064..a8fec171 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,6 +49,7 @@ export type CliOptions = { gasPriceMultiplier: number; gasLimitMultiplier: number; txGas?: bigint; + quoteGas: bigint; }; export type TokenDetails = { @@ -176,6 +177,7 @@ export type BotConfig = { gasPriceMultiplier: number; gasLimitMultiplier: number; txGas?: bigint; + quoteGas: bigint; onFetchRequest?: (request: Request) => void; onFetchResponse?: (request: Response) => void; }; diff --git a/src/utils.ts b/src/utils.ts index e55991f7..cb3d7156 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -744,6 +744,7 @@ export async function quoteOrders( orderDetails: BundledOrders[][], rpcs: string[], blockNumber?: bigint, + gas?: bigint, multicallAddressOverride?: string, ): Promise { let quoteResults: any[] = []; @@ -762,6 +763,7 @@ export async function quoteOrders( targets, rpc, blockNumber, + gas, multicallAddressOverride, ); break; @@ -819,6 +821,7 @@ export async function quoteSingleOrder( orderDetails: BundledOrders, rpcs: string[], blockNumber?: bigint, + gas?: bigint, multicallAddressOverride?: string, ) { for (let i = 0; i < rpcs.length; i++) { @@ -834,6 +837,7 @@ export async function quoteSingleOrder( ] as any as QuoteTarget[], rpc, blockNumber, + gas, multicallAddressOverride, ) )[0]; diff --git a/test/e2e/e2e.test.js b/test/e2e/e2e.test.js index c67d3b7d..bfb4f65d 100644 --- a/test/e2e/e2e.test.js +++ b/test/e2e/e2e.test.js @@ -527,27 +527,6 @@ for (let i = 0; i < testData.length; i++) { } // mock quote responses - await mockServer - .forPost("/rpc") - .once() - .thenSendJsonRpcResult( - encodeQuoteResponse([ - ...tokens.slice(1).map((v) => [ - true, // success - v.depositAmount.mul("1" + "0".repeat(18 - v.decimals)), //maxout - ethers.constants.Zero, // ratio - ]), - ...tokens - .slice(1) - .map(() => [ - true, - tokens[0].depositAmount.mul( - "1" + "0".repeat(18 - tokens[0].decimals), - ), - ethers.constants.Zero, - ]), - ]), - ); for (let i = 1; i < tokens.length; i++) { const output = tokens[i].depositAmount.mul( "1" + "0".repeat(18 - tokens[i].decimals), @@ -590,6 +569,24 @@ for (let i = 0; i < testData.length; i++) { await getOrderbookOwnersProfileMapFromSg(orders, viemClient, []), false, ); + + // mock init quotes + orders.forEach((ob) => { + ob.forEach((pair) => { + pair.takeOrders.forEach((takeOrder) => { + takeOrder.quote = { + ratio: ethers.constants.Zero, + maxOutput: tokens + .find( + (t) => + t.contract.address.toLowerCase() === + pair.sellToken.toLowerCase(), + ) + ?.depositAmount.mul("1" + "0".repeat(18 - ob.decimals)), + }; + }); + }); + }); const { reports } = await clear(config, orders, tracer, ctx); // should have cleared correct number of orders @@ -888,20 +885,6 @@ for (let i = 0; i < testData.length; i++) { for (let i = 0; i < tokens.length - 1; i++) { t0.push(tokens[0]); } - await mockServer - .forPost("/rpc") - .once() - .thenSendJsonRpcResult( - encodeQuoteResponse([ - ...[tokens[1], ...t0, ...tokens.slice(2)].flatMap((v) => [ - [ - true, // success - v.depositAmount.mul("1" + "0".repeat(18 - v.decimals)), //maxout - ethers.constants.Zero, // ratio - ], - ]), - ]), - ); for (let i = 1; i < tokens.length; i++) { const output = tokens[i].depositAmount.mul( "1" + "0".repeat(18 - tokens[i].decimals), @@ -943,6 +926,24 @@ for (let i = 0; i < testData.length; i++) { await getOrderbookOwnersProfileMapFromSg(orders, viemClient, []), false, ); + + // mock init quotes + orders.forEach((ob) => { + ob.forEach((pair) => { + pair.takeOrders.forEach((takeOrder) => { + takeOrder.quote = { + ratio: ethers.constants.Zero, + maxOutput: tokens + .find( + (t) => + t.contract.address.toLowerCase() === + pair.sellToken.toLowerCase(), + ) + ?.depositAmount.mul("1" + "0".repeat(18 - ob.decimals)), + }; + }); + }); + }); const { reports } = await clear(config, orders, tracer, ctx); // should have cleared correct number of orders