diff --git a/README.md b/README.md index 8e419ba..8b6c26e 100644 --- a/README.md +++ b/README.md @@ -97,9 +97,12 @@ Other optional arguments are: - `--generic-arb-address`, Address of the deployed generic arb contract to perform inter-orderbook clears, Will override the 'GENERIC_ARB_ADDRESS' in env variables - `-l` or `--lps`, List of liquidity providers (dex) to use by the router as one quoted string seperated by a comma for each, example: 'SushiSwapV2,UniswapV3', Will override the 'LIQUIDITY_PROVIDERS' in env variables, if unset will use all available liquidty providers - `-g` or `--gas-coverage`, The percentage of gas to cover to be considered profitable for the transaction to be submitted, an integer greater than equal 0, default is 100 meaning full coverage, Will override the 'GAS_COVER' in env variables -- `--orderbook-address`, Option to filter the subgraph query results with address of the deployed orderbook contract, Will override the 'ORDERBOOK_ADDRESS' in env variables -- `--order-hash`, Option to filter the subgraph query results with a specific order hash, Will override the 'ORDER_HASH' in env variables -- `--order-owner`, Option to filter the subgraph query results with a specific order owner address, Will override the 'ORDER_OWNER' in env variables +- `--include-orders`, Option to only include the specified orders for processing, Will override the 'INCLUDE_ORDERS' in env variables +- `--include-owners`, Option to only include the specified owners' orders for processing, Will override the 'INCLUDE_OWNERS' in env variables +- `--include-orderbooks`, Option to only include the specified orderbooks for processing, Will override the 'INCLUDE_ORDERBOOKS' in env variables +- `--exclude-orders`, Option to exclude the specified orders from processing, Will override the 'EXCLUDE_ORDERS' in env variables +- `--exclude-owners`, Option to exclude the specified owners' orders from processing, Will override the 'EXCLUDE_OWNERS' in env variables +- `--exclude-orderbooks`, Option to exclude the specified orderbooks from processing, Will override the 'EXCLUDE_ORDERBOOKS' in env variables - `--sleep`, Seconds to wait between each arb round, default is 10, Will override the 'SLEEP' in env variables - `--max-ratio`, Option to maximize maxIORatio, Will override the 'MAX_RATIO' in env variables - `--timeout`, Optional seconds to wait for the transaction to mine before disregarding it, Will override the 'TIMEOUT' in env variables @@ -197,9 +200,6 @@ ARB_ADDRESS="0x123..." # generic arb contract address GENERIC_ARB_ADDRESS="0x123..." -# Option to filter the subgraph query results with orderbook contract address -ORDERBOOK_ADDRESS="0x123..." - # one or more subgraph urls to read orders details from, can be used in combination with ORDERS # for more than 1 subgraphs, seperate them by comma and a space SUBGRAPH="https://api.thegraph.com/subgraphs/name/org1/sg1, https://api.thegraph.com/subgraphs/name/org2/sg2" @@ -279,6 +279,14 @@ RP_ONLY="true" # Address of dispair (ExpressionDeployer contract) to use for tasks DISPAIR="address" + +# Filters for inc/exc orders, multiple items can be separated by a comma +INCLUDE_ORDERS= +INCLUDE_OWNERS= +INCLUDE_ORDERBOOKS= +EXCLUDE_ORDERS= +EXCLUDE_OWNERS= +EXCLUDE_ORDERBOOKS= ``` 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 82f40c5..2eec60f 100644 --- a/example.env +++ b/example.env @@ -20,9 +20,6 @@ ARB_ADDRESS="0x123..." # generic arb contract address GENERIC_ARB_ADDRESS="0x123..." -# Option to filter the subgraph query results with orderbook contract address -ORDERBOOK_ADDRESS="0x123..." - # one or more subgraph urls to read orders details from, can be used in combination with ORDERS # for more than 1 subgraphs, seperate them by comma and a space SUBGRAPH="https://api.thegraph.com/subgraphs/name/org1/sg1, https://api.thegraph.com/subgraphs/name/org2/sg2" @@ -103,6 +100,14 @@ RP_ONLY="true" # Address of dispair (ExpressionDeployer contract) to use for tasks DISPAIR="address" +# Filters for inc/exc orders, multiple items can be separated by a comma +INCLUDE_ORDERS= +INCLUDE_OWNERS= +INCLUDE_ORDERBOOKS= +EXCLUDE_ORDERS= +EXCLUDE_OWNERS= +EXCLUDE_ORDERBOOKS= + # test rpcs vars TEST_POLYGON_RPC= diff --git a/src/cli.ts b/src/cli.ts index d4afd03..f7a4bc7 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -62,11 +62,8 @@ const ENV_OPTIONS = { mnemonic: process?.env?.MNEMONIC, arbAddress: process?.env?.ARB_ADDRESS, genericArbAddress: process?.env?.GENERIC_ARB_ADDRESS, - orderbookAddress: process?.env?.ORDERBOOK_ADDRESS, lps: process?.env?.LIQUIDITY_PROVIDERS, gasCoverage: process?.env?.GAS_COVER || "100", - orderHash: process?.env?.ORDER_HASH, - orderOwner: process?.env?.ORDER_OWNER, sleep: process?.env?.SLEEP, maxRatio: process?.env?.MAX_RATIO?.toLowerCase() === "true" ? true : false, publicRpc: process?.env?.PUBLIC_RPC?.toLowerCase() === "true" ? true : false, @@ -85,18 +82,16 @@ const ENV_OPTIONS = { route: process?.env?.ROUTE, dispair: process?.env?.DISPAIR, rpOnly: process?.env?.RP_ONLY?.toLowerCase() === "true" ? true : false, - ownerProfile: process?.env?.OWNER_PROFILE - ? Array.from(process?.env?.OWNER_PROFILE.matchAll(/[^,\s]+/g)).map((v) => v[0]) - : undefined, - rpc: process?.env?.RPC_URL - ? Array.from(process?.env?.RPC_URL.matchAll(/[^,\s]+/g)).map((v) => v[0]) - : undefined, - writeRpc: process?.env?.WRITE_RPC - ? Array.from(process?.env?.WRITE_RPC.matchAll(/[^,\s]+/g)).map((v) => v[0]) - : undefined, - subgraph: process?.env?.SUBGRAPH - ? Array.from(process?.env?.SUBGRAPH.matchAll(/[^,\s]+/g)).map((v) => v[0]) - : undefined, + ownerProfile: parseArrayFromEnv(process?.env?.OWNER_PROFILE), + rpc: parseArrayFromEnv(process?.env?.RPC_URL), + writeRpc: parseArrayFromEnv(process?.env?.WRITE_RPC), + subgraph: parseArrayFromEnv(process?.env?.SUBGRAPH), + includeOrders: parseArrayFromEnv(process?.env?.INCLUDE_ORDERS), + includeOwners: parseArrayFromEnv(process?.env?.INCLUDE_OWNERS), + includeOrderbooks: parseArrayFromEnv(process?.env?.INCLUDE_ORDERBOOKS), + excludeOrders: parseArrayFromEnv(process?.env?.EXCLUDE_ORDERS), + excludeOwners: parseArrayFromEnv(process?.env?.EXCLUDE_OWNERS), + excludeOrderbooks: parseArrayFromEnv(process?.env?.EXCLUDE_ORDERBOOKS), }; const getOptions = async (argv: any, version?: string) => { @@ -117,10 +112,6 @@ const getOptions = async (argv: any, version?: string) => { "-s, --subgraph ", "Subgraph URL(s) to read orders details from, can be used in combination with --orders, Will override the 'SUBGRAPH' in env variables", ) - .option( - "--orderbook-address
", - "Option to filter the subgraph query results with address of the deployed orderbook contract, Will override the 'ORDERBOOK_ADDRESS' in env variables", - ) .option( "--arb-address
", "Address of the deployed arb contract, Will override the 'ARB_ADDRESS' in env variables", @@ -142,12 +133,28 @@ const getOptions = async (argv: any, version?: string) => { "The percentage of gas to cover to be considered profitable for the transaction to be submitted, an integer greater than equal 0, default is 100 meaning full coverage, Will override the 'GAS_COVER' in env variables", ) .option( - "--order-hash ", - "Option to filter the subgraph query results with a specific order hash, Will override the 'ORDER_HASH' in env variables", + "--include-orders ", + "Option to only include the specified orders for processing, Will override the 'INCLUDE_ORDERS' in env variables", + ) + .option( + "--include-owners ", + "Option to only include the specified owners' orders for processing, Will override the 'INCLUDE_OWNERS' in env variables", + ) + .option( + "--include-orderbooks ", + "Option to only include the specified orderbooks for processing, Will override the 'INCLUDE_ORDERBOOKS' in env variables", + ) + .option( + "--exclude-orders ", + "Option to exclude the specified orders from processing, Will override the 'EXCLUDE_ORDERS' in env variables", ) .option( - "--order-owner
", - "Option to filter the subgraph query results with a specific order owner address, Will override the 'ORDER_OWNER' in env variables", + "--exclude-owners ", + "Option to exclude the specified owners' orders from processing, Will override the 'EXCLUDE_OWNERS' in env variables", + ) + .option( + "--exclude-orderbooks ", + "Option to exclude the specified orderbooks from processing, Will override the 'EXCLUDE_ORDERBOOKS' in env variables", ) .option( "--sleep ", @@ -245,13 +252,17 @@ const getOptions = async (argv: any, version?: string) => { cmdOptions.arbAddress = cmdOptions.arbAddress || getEnv(ENV_OPTIONS.arbAddress); cmdOptions.genericArbAddress = cmdOptions.genericArbAddress || getEnv(ENV_OPTIONS.genericArbAddress); - cmdOptions.orderbookAddress = - cmdOptions.orderbookAddress || getEnv(ENV_OPTIONS.orderbookAddress); cmdOptions.subgraph = cmdOptions.subgraph || getEnv(ENV_OPTIONS.subgraph); cmdOptions.lps = cmdOptions.lps || getEnv(ENV_OPTIONS.lps); cmdOptions.gasCoverage = cmdOptions.gasCoverage || getEnv(ENV_OPTIONS.gasCoverage); - cmdOptions.orderHash = cmdOptions.orderHash || getEnv(ENV_OPTIONS.orderHash); - cmdOptions.orderOwner = cmdOptions.orderOwner || getEnv(ENV_OPTIONS.orderOwner); + cmdOptions.includeOrders = cmdOptions.includeOrders || getEnv(ENV_OPTIONS.includeOrders); + cmdOptions.includeOwners = cmdOptions.includeOwners || getEnv(ENV_OPTIONS.includeOwners); + cmdOptions.includeOrderbooks = + cmdOptions.includeOrderbooks || getEnv(ENV_OPTIONS.includeOrderbooks); + cmdOptions.excludeOrders = cmdOptions.excludeOrders || getEnv(ENV_OPTIONS.excludeOrders); + cmdOptions.excludeOwners = cmdOptions.excludeOwners || getEnv(ENV_OPTIONS.excludeOwners); + cmdOptions.excludeOrderbooks = + cmdOptions.excludeOrderbooks || getEnv(ENV_OPTIONS.excludeOrderbooks); cmdOptions.sleep = cmdOptions.sleep || getEnv(ENV_OPTIONS.sleep); cmdOptions.maxRatio = cmdOptions.maxRatio || getEnv(ENV_OPTIONS.maxRatio); cmdOptions.timeout = cmdOptions.timeout || getEnv(ENV_OPTIONS.timeout); @@ -316,6 +327,54 @@ const getOptions = async (argv: any, version?: string) => { }; }); } + if (cmdOptions.includeOrders) { + cmdOptions.includeOrders = cmdOptions.includeOrders.map((v: string) => { + if (!/^(0x)?[a-fA-F0-9]{64}$/.test(v)) { + throw `${v} is not a valid order hash`; + } + return v.toLowerCase(); + }); + } + if (cmdOptions.excludeOrders) { + cmdOptions.excludeOrders = cmdOptions.excludeOrders.map((v: string) => { + if (!/^(0x)?[a-fA-F0-9]{64}$/.test(v)) { + throw `${v} is not a valid order hash`; + } + return v.toLowerCase(); + }); + } + if (cmdOptions.includeOwners) { + cmdOptions.includeOwners = cmdOptions.includeOwners.map((v: string) => { + if (!ethers.utils.isAddress(v)) { + throw `${v} is not a valid address`; + } + return v.toLowerCase(); + }); + } + if (cmdOptions.excludeOwners) { + cmdOptions.excludeOwners = cmdOptions.excludeOwners.map((v: string) => { + if (!ethers.utils.isAddress(v)) { + throw `${v} is not a valid address`; + } + return v.toLowerCase(); + }); + } + if (cmdOptions.includeOrderbooks) { + cmdOptions.includeOrderbooks = cmdOptions.includeOrderbooks.map((v: string) => { + if (!ethers.utils.isAddress(v)) { + throw `${v} is not a valid address`; + } + return v.toLowerCase(); + }); + } + if (cmdOptions.excludeOrderbooks) { + cmdOptions.excludeOrderbooks = cmdOptions.excludeOrderbooks.map((v: string) => { + if (!ethers.utils.isAddress(v)) { + throw `${v} is not a valid address`; + } + return v.toLowerCase(); + }); + } return cmdOptions; }; @@ -402,6 +461,9 @@ export async function startup(argv: any, version?: string, tracer?: Tracer, ctx? throw "undefined wallet, only one of key or mnemonic should be specified"; } if (options.mnemonic) { + if (!ethers.utils.isValidMnemonic(options.mnemonic)) { + throw "provided mnemonic key is not valid"; + } if (!options.walletCount || !options.topupAmount) { throw "--wallet-count and --toptup-amount are required when using mnemonic option"; } @@ -505,9 +567,12 @@ export async function startup(argv: any, version?: string, tracer?: Tracer, ctx? for (let i = 0; i < 3; i++) { try { ordersDetails = await getOrderDetails(options.subgraph, { - orderHash: options.orderHash, - orderOwner: options.orderOwner, - orderbook: options.orderbookAddress, + includeOrders: options.includeOrders, + includeOwners: options.includeOwners, + excludeOrders: options.excludeOrders, + excludeOwners: options.excludeOwners, + includeOrderbooks: options.includeOrderbooks, + excludeOrderbooks: options.excludeOrderbooks, }); break; } catch (e) { @@ -949,3 +1014,7 @@ function getEnv(value: any): any { } return undefined; } + +function parseArrayFromEnv(value?: string): string[] | undefined { + return value ? Array.from(value.matchAll(/[^,\s]+/g)).map((v) => v[0]) : undefined; +} diff --git a/src/index.ts b/src/index.ts index 5d4c5c2..5d7d299 100644 --- a/src/index.ts +++ b/src/index.ts @@ -69,15 +69,7 @@ export async function getOrderDetails( ({ availableSgs } = checkSgStatus(validSgs, statusResult, span, hasjson)); availableSgs.forEach((v) => { - if (v && typeof v === "string") - promises.push( - querySgOrders( - v, - sgFilters?.orderHash, - sgFilters?.orderOwner, - sgFilters?.orderbook, - ), - ); + if (v && typeof v === "string") promises.push(querySgOrders(v, sgFilters)); }); } diff --git a/src/query.ts b/src/query.ts index 5fb4961..8c3486d 100644 --- a/src/query.ts +++ b/src/query.ts @@ -1,6 +1,7 @@ import axios from "axios"; import { errorSnapshot } from "./error"; import { Span } from "@opentelemetry/api"; +import { SgFilter } from "./types"; export type SgOrder = { id: string; @@ -55,22 +56,38 @@ export type SgOtherEvent = { /** * Method to get the subgraph query body with optional filters - * @param orderHash - The order hash to apply as filter - * @param owner - The order owner to apply as filter - * @param orderbook - The orderbook address + * @param skip - Number of results to skip + * @param filters - Applies the filters for query * @returns the query string */ -export function getQueryPaginated( - skip: number, - orderHash?: string, - owner?: string, - orderbook?: string, -): string { - const ownerFilter = owner ? `, owner: "${owner.toLowerCase()}"` : ""; - const orderHashFilter = orderHash ? `, orderHash: "${orderHash.toLowerCase()}"` : ""; - const orderbookFilter = orderbook ? `, orderbook: "${orderbook.toLowerCase()}"` : ""; +export function getQueryPaginated(skip: number, filters?: SgFilter): string { + const incOwnerFilter = filters?.includeOwners + ? `, owner_in: [${filters.includeOwners.map((v) => `"${v.toLowerCase()}"`).join(",")}]` + : ""; + const exOwnerFilter = filters?.excludeOwners + ? `, owner_not_in: [${filters.excludeOwners.map((v) => `"${v.toLowerCase()}"`).join(",")}]` + : ""; + const incOrderFilter = filters?.includeOrders + ? `, orderHash_in: [${filters.includeOrders.map((v) => `"${v.toLowerCase()}"`).join(",")}]` + : ""; + const exOrderFilter = filters?.excludeOrders + ? `, orderHash_not_in: [${filters.excludeOrders.map((v) => `"${v.toLowerCase()}"`).join(",")}]` + : ""; + const incOrderbookFilter = filters?.includeOrderbooks + ? `, orderbook_in: [${filters.includeOrderbooks.map((v) => `"${v.toLowerCase()}"`).join(",")}]` + : ""; + const exOrderbookFilter = filters?.excludeOrderbooks + ? `, orderbook_not_in: [${filters.excludeOrderbooks.map((v) => `"${v.toLowerCase()}"`).join(",")}]` + : ""; + return `{ - orders(first: 100, skip: ${skip}, orderBy: timestampAdded, orderDirection: desc, where: {active: true${orderbookFilter}${orderHashFilter}${ownerFilter}}) { + orders( + first: 100, + skip: ${skip}, + orderBy: timestampAdded, + orderDirection: desc, + where: {active: true${incOwnerFilter}${exOwnerFilter}${incOrderFilter}${exOrderFilter}${incOrderbookFilter}${exOrderbookFilter}} + ) { id owner orderHash @@ -112,9 +129,7 @@ export function getQueryPaginated( */ export async function querySgOrders( subgraph: string, - orderHash?: string, - owner?: string, - orderbook?: string, + filters?: SgFilter, timeout?: number, ): Promise { const result: any[] = []; @@ -123,7 +138,7 @@ export async function querySgOrders( const res = await axios.post( subgraph, { - query: getQueryPaginated(skip, orderHash, owner, orderbook), + query: getQueryPaginated(skip, filters), }, { headers: { "Content-Type": "application/json" }, timeout }, ); @@ -255,6 +270,7 @@ export async function getOrderChanges( startTimestamp: number, skip: number, span?: Span, + filters?: SgFilter, ) { let skip_ = skip; let count = 0; @@ -291,10 +307,13 @@ export async function getOrderChanges( if (event.__typename === "AddOrder") { if (typeof event?.order?.active === "boolean" && event.order.active) { if (!addOrders.find((e) => e.order.id === event.order.id)) { - addOrders.push({ + const newOrder = { order: event.order as SgOrder, timestamp: Number(tx.timestamp), - }); + }; + if (applyFilters(newOrder, filters)) { + addOrders.push(); + } } } } @@ -313,3 +332,49 @@ export async function getOrderChanges( }); return { addOrders, removeOrders, count }; } + +/** + * Applies the filters to the given new added order queried from subgraph tx events + * @param order - The new added order + * @param filters - The subgraph filters + */ +export function applyFilters(order: NewSgOrder, filters?: SgFilter): boolean { + if (!filters) return true; + else { + // apply include filter + if (filters.includeOrderbooks) { + if (!filters.includeOrderbooks.includes(order.order.orderbook.id)) { + return false; + } + } + if (filters.includeOrders) { + if (!filters.includeOrders.includes(order.order.orderHash)) { + return false; + } + } + if (filters.includeOwners) { + if (!filters.includeOwners.includes(order.order.owner)) { + return false; + } + } + + // apply exclude filters + if (filters.excludeOrderbooks) { + if (filters.excludeOrderbooks.includes(order.order.orderbook.id)) { + return false; + } + } + if (filters.excludeOrders) { + if (filters.excludeOrders.includes(order.order.orderHash)) { + return false; + } + } + if (filters.excludeOwners) { + if (filters.excludeOwners.includes(order.order.owner)) { + return false; + } + } + + return true; + } +} diff --git a/src/types.ts b/src/types.ts index 2394b43..15130c0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,12 +49,15 @@ export type CliOptions = { writeRpc?: string[]; arbAddress: string; genericArbAddress?: string; - orderbookAddress?: string; subgraph: string[]; lps?: string[]; gasCoverage: string; - orderHash?: string; - orderOwner?: string; + includeOrders?: string[]; + includeOwners?: string[]; + excludeOrders?: string[]; + excludeOwners?: string[]; + includeOrderbooks: string[]; + excludeOrderbooks: string[]; sleep: number; maxRatio: boolean; timeout?: number; @@ -308,9 +311,12 @@ export type OwnedOrder = { }; export type SgFilter = { - orderHash?: string; - orderOwner?: string; - orderbook?: string; + includeOrders?: string[]; + includeOwners?: string[]; + excludeOrders?: string[]; + excludeOwners?: string[]; + includeOrderbooks: string[]; + excludeOrderbooks: string[]; }; export type RpcRecord = { diff --git a/test/cli.test.js b/test/cli.test.js index b09d2e0..25d7ad8 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -41,7 +41,6 @@ describe("Test cli", async function () { const options = { rpc: ["http://localhost:8080/rpc-url"], key: "0x" + "1".repeat(64), - orderbookAddress: "0x" + "0".repeat(40), arbAddress: "0x" + "0".repeat(40), maxRatio: true, subgraph: ["http://localhost:8080/sg-url"], @@ -83,7 +82,20 @@ describe("Test cli", async function () { } try { - await startup(["", "", "-m", "some-mnemonic"]); + await startup(["", "", "-m", "some invalid mnemonic"]); + assert.fail("expected to fail, but resolved"); + } catch (error) { + const expected = "provided mnemonic key is not valid"; + assert.equal(error, expected); + } + + try { + await startup([ + "", + "", + "-m", + "test test test test test test test test test test test junk", + ]); assert.fail("expected to fail, but resolved"); } catch (error) { const expected = @@ -117,8 +129,6 @@ describe("Test cli", async function () { "some-rpc", "--arb-address", `0x${"0".repeat(64)}`, - "--orderbook-address", - `0x${"0".repeat(64)}`, "--sleep", "abcd", ]); @@ -138,8 +148,6 @@ describe("Test cli", async function () { "some-rpc", "--arb-address", `0x${"0".repeat(64)}`, - "--orderbook-address", - `0x${"0".repeat(64)}`, "--pool-update-interval", "abcd", ]); @@ -160,8 +168,6 @@ describe("Test cli", async function () { "some-rpc", "--arb-address", `0x${"0".repeat(64)}`, - "--orderbook-address", - `0x${"0".repeat(64)}`, "--pool-update-interval", "10", ]); @@ -182,8 +188,6 @@ describe("Test cli", async function () { "some-rpc", "--arb-address", `0x${"0".repeat(64)}`, - "--orderbook-address", - `0x${"0".repeat(64)}`, "--pool-update-interval", "10", "--bot-min-balance", @@ -205,8 +209,6 @@ describe("Test cli", async function () { "https://rpc.ankr.com/polygon", "--arb-address", `0x${"1".repeat(40)}`, - "--orderbook-address", - `0x${"2".repeat(40)}`, "--pool-update-interval", "10", "--bot-min-balance", @@ -220,6 +222,162 @@ describe("Test cli", async function () { assert.equal(error, expected); } + try { + await startup([ + "", + "", + "--key", + `0x${"1".repeat(64)}`, + "--rpc", + "https://rpc.ankr.com/polygon", + "--arb-address", + `0x${"1".repeat(40)}`, + "--pool-update-interval", + "10", + "--bot-min-balance", + "12", + "--dispair", + "0x783b82f0fBF6743882072AE2393B108F5938898B", + "--include-orders", + `0x${"1".repeat(64)}`, + `0x${"2".repeat(40)}`, + ]); + assert.fail("expected to fail, but resolved"); + } catch (error) { + const expected = `0x${"2".repeat(40)} is not a valid order hash`; + assert.equal(error, expected); + } + + try { + await startup([ + "", + "", + "--key", + `0x${"1".repeat(64)}`, + "--rpc", + "https://rpc.ankr.com/polygon", + "--arb-address", + `0x${"1".repeat(40)}`, + "--pool-update-interval", + "10", + "--bot-min-balance", + "12", + "--dispair", + "0x783b82f0fBF6743882072AE2393B108F5938898B", + "--exclude-orders", + `0x${"1".repeat(64)}`, + `0x${"2".repeat(40)}`, + ]); + assert.fail("expected to fail, but resolved"); + } catch (error) { + const expected = `0x${"2".repeat(40)} is not a valid order hash`; + assert.equal(error, expected); + } + + try { + await startup([ + "", + "", + "--key", + `0x${"1".repeat(64)}`, + "--rpc", + "https://rpc.ankr.com/polygon", + "--arb-address", + `0x${"1".repeat(40)}`, + "--pool-update-interval", + "10", + "--bot-min-balance", + "12", + "--dispair", + "0x783b82f0fBF6743882072AE2393B108F5938898B", + "--include-owners", + `0x${"1".repeat(40)}`, + `0x${"2".repeat(64)}`, + ]); + assert.fail("expected to fail, but resolved"); + } catch (error) { + const expected = `0x${"2".repeat(64)} is not a valid address`; + assert.equal(error, expected); + } + + try { + await startup([ + "", + "", + "--key", + `0x${"1".repeat(64)}`, + "--rpc", + "https://rpc.ankr.com/polygon", + "--arb-address", + `0x${"1".repeat(40)}`, + "--pool-update-interval", + "10", + "--bot-min-balance", + "12", + "--dispair", + "0x783b82f0fBF6743882072AE2393B108F5938898B", + "--exclude-owners", + `0x${"1".repeat(40)}`, + `0x${"2".repeat(64)}`, + ]); + assert.fail("expected to fail, but resolved"); + } catch (error) { + const expected = `0x${"2".repeat(64)} is not a valid address`; + assert.equal(error, expected); + } + + try { + await startup([ + "", + "", + "--key", + `0x${"1".repeat(64)}`, + "--rpc", + "https://rpc.ankr.com/polygon", + "--arb-address", + `0x${"1".repeat(40)}`, + "--pool-update-interval", + "10", + "--bot-min-balance", + "12", + "--dispair", + "0x783b82f0fBF6743882072AE2393B108F5938898B", + "--include-orderbooks", + `0x${"1".repeat(40)}`, + `0x${"2".repeat(64)}`, + ]); + assert.fail("expected to fail, but resolved"); + } catch (error) { + const expected = `0x${"2".repeat(64)} is not a valid address`; + assert.equal(error, expected); + } + + try { + await startup([ + "", + "", + "--key", + `0x${"1".repeat(64)}`, + "--rpc", + "https://rpc.ankr.com/polygon", + "--arb-address", + `0x${"1".repeat(40)}`, + "--pool-update-interval", + "10", + "--bot-min-balance", + "12", + "--dispair", + "0x783b82f0fBF6743882072AE2393B108F5938898B", + "--exclude-orderbooks", + `0x${"1".repeat(40)}`, + `0x${"2".repeat(64)}`, + ]); + assert.fail("expected to fail, but resolved"); + } catch (error) { + const expected = `0x${"2".repeat(64)} is not a valid address`; + assert.equal(error, expected); + } + const result = await startup([ "", "", @@ -229,8 +387,6 @@ describe("Test cli", async function () { "https://rpc.ankr.com/polygon", "--arb-address", `0x${"1".repeat(40)}`, - "--orderbook-address", - `0x${"2".repeat(40)}`, "--bot-min-balance", "0.123", "--gas-price-multiplier", @@ -244,6 +400,24 @@ describe("Test cli", async function () { "--rp-only", "--dispair", deployer, + "--include-orders", + `0x${"1".repeat(64)}`, + `0x${"2".repeat(64)}`, + "--exclude-orders", + `0x${"3".repeat(64)}`, + `0x${"4".repeat(64)}`, + "--include-owners", + `0x${"1".repeat(40)}`, + `0x${"2".repeat(40)}`, + "--exclude-owners", + `0x${"3".repeat(40)}`, + `0x${"4".repeat(40)}`, + "--include-orderbooks", + `0x${"5".repeat(40)}`, + `0x${"6".repeat(40)}`, + "--exclude-orderbooks", + `0x${"7".repeat(40)}`, + `0x${"8".repeat(40)}`, ]); const expected = { roundGap: 10000, @@ -251,7 +425,6 @@ describe("Test cli", async function () { config: { chain: { id: 137 }, rpc: ["https://rpc.ankr.com/polygon"], - orderbookAddress: `0x${"2".repeat(40)}`, arbAddress: `0x${"1".repeat(40)}`, route: "single", rpcRecords: { @@ -281,6 +454,12 @@ describe("Test cli", async function () { quoteGas: 7777n, rpOnly: true, dispair: deployer, + includeOrders: [`0x${"1".repeat(64)}`, `0x${"2".repeat(64)}`], + excludeOrders: [`0x${"3".repeat(64)}`, `0x${"4".repeat(64)}`], + includeOwners: [`0x${"1".repeat(40)}`, `0x${"2".repeat(40)}`], + excludeOwners: [`0x${"3".repeat(40)}`, `0x${"4".repeat(40)}`], + includeOrderbooks: [`0x${"5".repeat(40)}`, `0x${"6".repeat(40)}`], + excludeOrderbooks: [`0x${"7".repeat(40)}`, `0x${"8".repeat(40)}`], }, }; await sleep(1000); @@ -304,5 +483,11 @@ describe("Test cli", async function () { assert.equal(result.config.rpOnly, expected.config.rpOnly); assert.deepEqual(result.options.dispair, expected.options.dispair); assert.deepEqual(result.config.dispair, expected.config.dispair); + assert.deepEqual(result.options.includeOrders, expected.options.includeOrders); + assert.deepEqual(result.options.excludeOrders, expected.options.excludeOrders); + assert.deepEqual(result.options.includeOwners, expected.options.includeOwners); + assert.deepEqual(result.options.excludeOwners, expected.options.excludeOwners); + assert.deepEqual(result.options.includeOrderbooks, expected.options.includeOrderbooks); + assert.deepEqual(result.options.excludeOrderbooks, expected.options.excludeOrderbooks); }); }); diff --git a/test/sg.test.js b/test/sg.test.js index aec2563..c4dba53 100644 --- a/test/sg.test.js +++ b/test/sg.test.js @@ -1,6 +1,7 @@ const { assert } = require("chai"); const { AxiosError } = require("axios"); const { checkSgStatus, handleSgResults, getSgOrderbooks } = require("../src/sg"); +const { applyFilters } = require("../src/query"); describe("Test read subgraph", async function () { it("should check subgraph status", async function () { @@ -250,4 +251,61 @@ describe("Test read subgraph", async function () { assert.deepEqual(result, expected); await mockServer.stop(); }); + + it("should test applying sg filters to new orders", async function () { + const filters = { + includeOrders: [`0x${"1".repeat(64)}`, `0x${"2".repeat(64)}`], + excludeOrders: [`0x${"3".repeat(64)}`, `0x${"4".repeat(64)}`], + includeOwners: [`0x${"1".repeat(40)}`, `0x${"2".repeat(40)}`], + excludeOwners: [`0x${"3".repeat(40)}`, `0x${"4".repeat(40)}`], + includeOrderbooks: [`0x${"5".repeat(40)}`, `0x${"6".repeat(40)}`], + excludeOrderbooks: [`0x${"7".repeat(40)}`, `0x${"8".repeat(40)}`], + }; + const newOrder = { + order: { + orderHash: `0x${"1".repeat(64)}`, + owner: `0x${"1".repeat(40)}`, + orderbook: { + id: `0x${"7".repeat(40)}`, + }, + }, + }; + assert(!applyFilters(newOrder, filters)); + + newOrder.order = { + orderHash: `0x${"1".repeat(64)}`, + owner: `0x${"3".repeat(40)}`, + orderbook: { + id: `0x${"6".repeat(40)}`, + }, + }; + assert(!applyFilters(newOrder, filters)); + + newOrder.order = { + orderHash: `0x${"3".repeat(64)}`, + owner: `0x${"2".repeat(40)}`, + orderbook: { + id: `0x${"5".repeat(40)}`, + }, + }; + assert(!applyFilters(newOrder, filters)); + + newOrder.order = { + orderHash: `0x${"1".repeat(64)}`, + owner: `0x${"2".repeat(40)}`, + orderbook: { + id: `0x${"5".repeat(40)}`, + }, + }; + assert(applyFilters(newOrder, filters)); + + newOrder.order = { + orderHash: `0x${"7".repeat(64)}`, + owner: `0x${"7".repeat(40)}`, + orderbook: { + id: `0x${"2".repeat(40)}`, + }, + }; + assert(!applyFilters(newOrder, filters)); + }); });