From 839bd69fd3c3041e3ad7095fc5ec5c3895c91ecf Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Fri, 27 Oct 2023 00:35:49 +0530 Subject: [PATCH 1/3] Added timeout for transactions --- arb-bot.js | 13 ++++++++++++- example.env | 5 ++++- src/crouter.js | 9 ++++++--- src/curve.js | 6 ++++-- src/index.js | 6 ++++++ src/router.js | 6 ++++-- src/srouter.js | 6 ++++-- src/utils.js | 11 ++++++++++- src/zeroex.js | 5 +++-- 9 files changed, 53 insertions(+), 14 deletions(-) diff --git a/arb-bot.js b/arb-bot.js index 3e6ec567..d97a7ae5 100755 --- a/arb-bot.js +++ b/arb-bot.js @@ -29,6 +29,7 @@ const DEFAULT_OPTIONS = { maxProfit : process?.env?.MAX_PROFIT?.toLowerCase() === "true" ? true : false, maxRatio : process?.env?.MAX_RATIO?.toLowerCase() === "true" ? true : false, usePublicRpcs : process?.env?.USE_PUBLIC_RPCS?.toLowerCase() === "true" ? true : false, + timeout : process?.env?.TIMEOUT, rpc : process?.env?.RPC_URL ? Array.from(process?.env?.RPC_URL.matchAll(/[^,\s]+/g)).map(v => v[0]) : undefined, @@ -60,6 +61,7 @@ const getOptions = async argv => { .option("--max-profit", "Option to maximize profit for 'srouter' mode, comes at the cost of more RPC calls, Will override the 'MAX_PROFIT' in env variables") .option("--max-ratio", "Option to maximize maxIORatio for 'srouter' mode, Will override the 'MAX_RATIO' in env variables") .option("--flash-bot-rpc ", "Optional flashbot url to submit transaction to.") + .option("--timeout ", "Optional timeout to wait till the transaction is mined in milliseconds. Default is 5 minutes i.e 300000.") .option("--use-public-rpcs", "Option to use public rpcs as fallback option for 'srouter' and 'router' mode, Will override the 'USE_PUBLIC_RPCS' 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.", @@ -91,7 +93,9 @@ const getOptions = async argv => { cmdOptions.maxProfit = cmdOptions.maxProfit || DEFAULT_OPTIONS.maxProfit; cmdOptions.maxRatio = cmdOptions.maxRatio || DEFAULT_OPTIONS.maxRatio; cmdOptions.usePublicRpcs = cmdOptions.usePublicRpcs || DEFAULT_OPTIONS.usePublicRpcs; - cmdOptions.flashBotRpc = cmdOptions.flashBotRpc || DEFAULT_OPTIONS.flashBotRpc; + cmdOptions.flashBotRpc = cmdOptions.flashBotRpc || DEFAULT_OPTIONS.flashBotRpc; + cmdOptions.timeout = cmdOptions.timeout || DEFAULT_OPTIONS.timeout; + return cmdOptions; }; @@ -119,6 +123,7 @@ const arbRound = async options => { flashBotRpc : options.flashBotRpc, hideSensitiveData : false, shortenLargeLogs : false, + timeout : options.timeout, liquidityProviders : options.lps ? Array.from(options.lps.matchAll(/[^,\s]+/g)).map(v => v[0]) : undefined, @@ -152,6 +157,12 @@ const main = async argv => { let roundGap = 10000; let rpcTurn = 0; + if(!options.timeout){ + options.timeout = 300 * 1000 + }else{ + if (/^\d+$/.test(options.timeout)) options.timeout = Number(options.timeout) * 1000; + else throw "invalid timeout, must be an integer greater than equal 0"; + } if (options.repetitions) { if (/^\d+$/.test(options.repetitions)) repetitions = Number(options.repetitions); else throw "invalid repetitions, must be an integer greater than equal 0"; diff --git a/example.env b/example.env index ddfef15b..40fd02cd 100644 --- a/example.env +++ b/example.env @@ -65,4 +65,7 @@ SLEEP=10 MAX_RATIO="true" # Option to use public rpcs as fallback option for 'srouter' and 'router' mode -USE_PUBLIC_RPCS="true" \ No newline at end of file +USE_PUBLIC_RPCS="true" + +# Optional timeout to wait till the transaction is mined in milliseconds. Default is 5 minutes i.e 300 +TIMEOUT="" \ No newline at end of file diff --git a/src/crouter.js b/src/crouter.js index cb57f03c..ac4f009a 100644 --- a/src/crouter.js +++ b/src/crouter.js @@ -10,7 +10,8 @@ const { getActualPrice, visualizeRoute, bundleTakeOrders, - createViemClient + createViemClient, + awaitTransactionTimeout } = require("./utils"); @@ -653,8 +654,10 @@ const crouterClear = async( console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" - ); - const receipt = await tx.wait(); + ); + + const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) + // const receipt = await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, diff --git a/src/curve.js b/src/curve.js index f67d4a3b..bec0835f 100644 --- a/src/curve.js +++ b/src/curve.js @@ -8,7 +8,8 @@ const { getDataFetcher, getActualPrice, bundleTakeOrders, - createViemClient + createViemClient, + awaitTransactionTimeout } = require("./utils"); /** @@ -543,7 +544,8 @@ const curveClear = async( "\n" ); - const receipt = await tx.wait(); + // const receipt = await tx.wait(); + const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, diff --git a/src/index.js b/src/index.js index b87a4533..4eb7fd7e 100644 --- a/src/index.js +++ b/src/index.js @@ -21,6 +21,10 @@ const configOptions = { * The 0x API key */ zeroExApiKey: undefined, + /** + * The 0x API key + */ + timeout: undefined, /** * List of liquidity providers for router contract tomoperate on */ @@ -193,6 +197,8 @@ const getConfig = async( config.maxProfit = !!options?.maxProfit; config.maxRatio = !!options?.maxRatio; config.usePublicRpcs = !!options?.usePublicRpcs; + config.timeout = options.timeout; + return config; }; diff --git a/src/router.js b/src/router.js index 1fd6d0d9..a8cef738 100644 --- a/src/router.js +++ b/src/router.js @@ -8,7 +8,8 @@ const { getDataFetcher, getActualPrice, visualizeRoute, - bundleTakeOrders + bundleTakeOrders, + awaitTransactionTimeout } = require("./utils"); @@ -373,7 +374,8 @@ const routerClear = async( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + // const receipt = await tx.wait(); + const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, diff --git a/src/srouter.js b/src/srouter.js index c82a28de..22537db0 100644 --- a/src/srouter.js +++ b/src/srouter.js @@ -9,7 +9,8 @@ const { getActualPrice, visualizeRoute, bundleTakeOrders, - getActualClearAmount + getActualClearAmount, + awaitTransactionTimeout } = require("./utils"); @@ -270,7 +271,8 @@ const srouterClear = async( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + // const receipt = await tx.wait(); + const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) if (receipt.status === 1) { const clearActualAmount = getActualClearAmount( arbAddress, diff --git a/src/utils.js b/src/utils.js index fe113c45..f16b827a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1473,6 +1473,14 @@ const shuffleArray = (array) => { return array; }; + +function awaitTransactionTimeout(timeout, ...args) { + function timeOut() { + return new Promise((resolve, reject) => setTimeout(reject, timeout, new Error(`Transaction failed to mine after ${timeout}ms`))); + } + return Promise.race([...args, timeOut()]); + } + module.exports = { fallbacks, bnFromFloat, @@ -1498,5 +1506,6 @@ module.exports = { visualizeRoute, build0xQueries, shuffleArray, - createViemClient + createViemClient, + awaitTransactionTimeout }; diff --git a/src/zeroex.js b/src/zeroex.js index f32d0d9f..aea825b2 100644 --- a/src/zeroex.js +++ b/src/zeroex.js @@ -1,6 +1,6 @@ const axios = require("axios"); const ethers = require("ethers"); -const { bundleTakeOrders } = require("./utils"); +const { bundleTakeOrders, awaitTransactionTimeout } = require("./utils"); const { arbAbis, orderbookAbi } = require("./abis"); const { sleep, getIncome, getActualPrice } = require("./utils"); @@ -360,7 +360,8 @@ const zeroExClear = async( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) + // const receipt = await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, From 3062b6d563cf1c43541d000cca70d05840000425 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 28 Oct 2023 16:08:04 +0000 Subject: [PATCH 2/3] update --- README.md | 14 +++++++++++- arb-bot.js | 16 +++++--------- docs/html/crouter.js.html | 22 ++++++++++++++++--- docs/html/curve.js.html | 23 ++++++++++++++++---- docs/html/global.html | 45 +++++++++++++++++++-------------------- docs/html/index.html | 2 +- docs/html/index.js.html | 25 +++++++++++++++++++++- docs/html/query.js.html | 2 +- docs/html/router.js.html | 30 ++++++++++++++++++++------ docs/html/srouter.js.html | 21 +++++++++++++++--- docs/html/utils.js.html | 12 +++++------ docs/html/zeroex.js.html | 23 +++++++++++++++----- example.env | 4 ++-- src/crouter.js | 27 +++++++++++++++++------ src/curve.js | 21 ++++++++++++++---- src/index.js | 23 ++++++++++++++------ src/router.js | 23 +++++++++++++++----- src/srouter.js | 23 +++++++++++++++----- src/utils.js | 18 ++++------------ src/zeroex.js | 22 ++++++++++++++----- 20 files changed, 282 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 4870e4d9..e2b3a8fd 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ Other optional arguments are: - `--max-profit`, Option to maximize profit for 'srouter' mode, comes at the cost of more RPC calls, Will override the 'MAX_PROFIT' in env variables - `--max-ratio`, Option to maximize maxIORatio for 'srouter' mode, Will override the 'MAX_RATIO' in env variables - `--use-public-rpcs`, Option to use public rpcs as fallback option for 'srouter' and 'router' mode, Will override the 'USE_PUBLIC_RPCS' in env variables +- `--timeout`, Optional seconds to wait for the transaction to mine before disregarding it, Will override the 'TIMEOUT' in env variables +- `--flashbot-rpc`, Optional flashbot rpc url to submit transaction to, Will override the 'FLASHBOT_RPC' in env variables - `-V` or `--version`, output the version number - `-h` or `--help`, output usage information @@ -102,7 +104,7 @@ which will show: Options: -k, --key Private key of wallet that performs the transactions. Will override the 'BOT_WALLET_PRIVATEKEY' in env variables - -r, --rpc RPC URL(s) that will be provider for interacting with evm, use different providers if more than 1 is specified to prevent banning. Will override the 'RPC_URL' in env variables + -r, --rpc RPC URL(s) that will be provider for interacting with evm, use different providers if more than 1 is specified to prevent banning. Will override the 'RPC_URL' in env variables -m, --mode Running mode of the bot, must be one of: `0x` or `curve` or `router` or `crouter` or `srouter`, Will override the 'MODE' in env variables -o, --orders The path to a local json file containing the orders details, can be used in combination with --subgraph, Will override the 'ORDERS' in env variables -s, --subgraph Subgraph URL(s) to read orders details from, can be used in combination with --orders, Will override the 'SUBGRAPH' in env variables @@ -118,6 +120,8 @@ which will show: --order-interpreter
Option to filter the subgraph query results with a specific order's interpreter address, Will override the 'ORDER_INTERPRETER' in env variables --monthly-ratelimit 0x monthly rate limit, if not specified will not respect any 0x monthly ratelimit, Will override the 'MONTHLY_RATELIMIT' in env variables --sleep Seconds to wait between each arb round, default is 10, Will override the 'SLEPP' in env variables + --flashbot-rpc Optional flashbot rpc url to submit transaction to, Will override the 'FLASHBOT_RPC' in env variables + --timeout Optional seconds to wait for the transaction to mine before disregarding it, Will override the 'TIMEOUT' in env variables --max-profit Option to maximize profit for 'srouter' mode, comes at the cost of more RPC calls, Will override the 'MAX_PROFIT' in env variables --max-ratio Option to maximize maxIORatio for 'srouter' mode, Will override the 'MAX_RATIO' in env variables --use-public-rpcs Option to use public rpcs as fallback option for 'srouter' and 'router' mode, Will override the 'USE_PUBLIC_RPCS' in env variables @@ -134,6 +138,9 @@ BOT_WALLET_PRIVATEKEY="123..." # for specifying more than 1 RPC in the env, separate them by a comma and a space RPC_URL="https://polygon-mainnet.g.alchemy.com/v2/{API_KEY}, https://rpc.ankr.com/polygon/{API_KEY}" +# Option to submit transactions using the flashbot RPC. +FLASHBOT_RPC="" + # bot running mode, one of "router", "0x", "curve", "crouter", "srouter" MODE="router" @@ -189,6 +196,9 @@ MAX_RATIO="true" # Option to use public rpcs as fallback option for 'srouter' and 'router' mode USE_PUBLIC_RPCS="true" + +# Optional seconds to wait for the transaction to mine before disregarding it +TIMEOUT="" ``` If both env variables and CLI argument are set, the CLI arguments will be prioritized and override the env variables. @@ -213,6 +223,8 @@ const configOptions = { maxProfit : true, // option to maximize profit for 'srouter' mode maxRatio : true, // option to maximize the maxIORatio in "srouter" mode usePublicRpcs : false, // option to fallback to public rpcs + flashbotRpc : "https://flashbot-rpc-url", // Optional Flashbot RPC URL + timeout : 300, // seconds to wait for tx to mine before disregarding it liquidityProviders : [ // list of liquidity providers for "router" mode to get quotes from (optional) "sushiswapv2", "uniswapv2" diff --git a/arb-bot.js b/arb-bot.js index d97a7ae5..60585f80 100755 --- a/arb-bot.js +++ b/arb-bot.js @@ -30,10 +30,10 @@ const DEFAULT_OPTIONS = { maxRatio : process?.env?.MAX_RATIO?.toLowerCase() === "true" ? true : false, usePublicRpcs : process?.env?.USE_PUBLIC_RPCS?.toLowerCase() === "true" ? true : false, timeout : process?.env?.TIMEOUT, + flashbotRpc : process?.env?.FLASHBOT_RPC, rpc : process?.env?.RPC_URL ? Array.from(process?.env?.RPC_URL.matchAll(/[^,\s]+/g)).map(v => v[0]) : undefined, - flashBotRpc : process?.env?.FLASHBOT_RPC, subgraph : process?.env?.SUBGRAPH ? Array.from(process?.env?.SUBGRAPH.matchAll(/[^,\s]+/g)).map(v => v[0]) : undefined @@ -58,10 +58,10 @@ const getOptions = async argv => { .option("--order-interpreter
", "Option to filter the subgraph query results with a specific order's interpreter address, Will override the 'ORDER_INTERPRETER' in env variables") .option("--monthly-ratelimit ", "0x monthly rate limit, if not specified will not respect any 0x monthly ratelimit, Will override the 'MONTHLY_RATELIMIT' in env variables") .option("--sleep ", "Seconds to wait between each arb round, default is 10, Will override the 'SLEPP' in env variables") + .option("--flashbot-rpc ", "Optional flashbot rpc url to submit transaction to, Will override the 'FLASHBOT_RPC' in env variables") + .option("--timeout ", "Optional seconds to wait for the transaction to mine before disregarding it, Will override the 'TIMEOUT' in env variables") .option("--max-profit", "Option to maximize profit for 'srouter' mode, comes at the cost of more RPC calls, Will override the 'MAX_PROFIT' in env variables") .option("--max-ratio", "Option to maximize maxIORatio for 'srouter' mode, Will override the 'MAX_RATIO' in env variables") - .option("--flash-bot-rpc ", "Optional flashbot url to submit transaction to.") - .option("--timeout ", "Optional timeout to wait till the transaction is mined in milliseconds. Default is 5 minutes i.e 300000.") .option("--use-public-rpcs", "Option to use public rpcs as fallback option for 'srouter' and 'router' mode, Will override the 'USE_PUBLIC_RPCS' 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.", @@ -93,7 +93,7 @@ const getOptions = async argv => { cmdOptions.maxProfit = cmdOptions.maxProfit || DEFAULT_OPTIONS.maxProfit; cmdOptions.maxRatio = cmdOptions.maxRatio || DEFAULT_OPTIONS.maxRatio; cmdOptions.usePublicRpcs = cmdOptions.usePublicRpcs || DEFAULT_OPTIONS.usePublicRpcs; - cmdOptions.flashBotRpc = cmdOptions.flashBotRpc || DEFAULT_OPTIONS.flashBotRpc; + cmdOptions.flashbotRpc = cmdOptions.flashbotRpc || DEFAULT_OPTIONS.flashbotRpc; cmdOptions.timeout = cmdOptions.timeout || DEFAULT_OPTIONS.timeout; @@ -120,7 +120,7 @@ const arbRound = async options => { maxProfit : options.maxProfit, maxRatio : options.maxRatio, usePublicRpcs : options.usePublicRpcs, - flashBotRpc : options.flashBotRpc, + flashbotRpc : options.flashbotRpc, hideSensitiveData : false, shortenLargeLogs : false, timeout : options.timeout, @@ -157,12 +157,6 @@ const main = async argv => { let roundGap = 10000; let rpcTurn = 0; - if(!options.timeout){ - options.timeout = 300 * 1000 - }else{ - if (/^\d+$/.test(options.timeout)) options.timeout = Number(options.timeout) * 1000; - else throw "invalid timeout, must be an integer greater than equal 0"; - } if (options.repetitions) { if (/^\d+$/.test(options.repetitions)) repetitions = Number(options.repetitions); else throw "invalid repetitions, must be an integer greater than equal 0"; diff --git a/docs/html/crouter.js.html b/docs/html/crouter.js.html index 869c31b1..597a5de4 100644 --- a/docs/html/crouter.js.html +++ b/docs/html/crouter.js.html @@ -37,6 +37,7 @@

Source: crouter.js

getDataFetcher, getActualPrice, visualizeRoute, + promiseTimeout, bundleTakeOrders, createViemClient } = require("./utils"); @@ -191,6 +192,12 @@

Source: crouter.js

const arbAddress = config.arbAddress; const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis[arbType], signer); @@ -676,13 +683,22 @@

Source: crouter.js

] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, @@ -805,7 +821,7 @@

Home

Global

  • diff --git a/docs/html/curve.js.html b/docs/html/curve.js.html index 581fa9ef..91e113fd 100644 --- a/docs/html/curve.js.html +++ b/docs/html/curve.js.html @@ -35,8 +35,9 @@

    Source: curve.js

    getEthPrice, getDataFetcher, getActualPrice, + promiseTimeout, bundleTakeOrders, - createViemClient + createViemClient, } = require("./utils"); /** @@ -241,6 +242,12 @@

    Source: curve.js

    const arbAddress = config.arbAddress; const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis[arbType], signer); @@ -564,14 +571,22 @@

    Source: curve.js

    ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, @@ -693,7 +708,7 @@

    Home

    Global

    • diff --git a/docs/html/global.html b/docs/html/global.html index 0a6957ab..3373cc81 100644 --- a/docs/html/global.html +++ b/docs/html/global.html @@ -204,7 +204,7 @@

      (constant) Source:
      @@ -500,7 +500,7 @@
      Parameters:
      Source:
      @@ -1389,7 +1389,7 @@
      Parameters:
      Source:
      @@ -1798,7 +1798,7 @@
      Parameters:
      Source:
      @@ -2008,7 +2008,7 @@
      Parameters:
      Source:
      @@ -2997,7 +2997,7 @@
      Parameters:
      Source:
      @@ -3249,7 +3249,7 @@
      Parameters:
      Source:
      @@ -3396,7 +3396,7 @@
      Parameters:
      Source:
      @@ -3556,7 +3556,7 @@
      Parameters:
      Source:
      @@ -3785,7 +3785,7 @@
      Parameters:
      Source:
      @@ -4161,7 +4161,7 @@
      Parameters:
      Source:
      @@ -4308,7 +4308,7 @@
      Parameters:
      Source:
      @@ -4445,7 +4445,7 @@
      Parameters:
      Source:
      @@ -5608,7 +5608,7 @@
      Parameters:
      Source:
      @@ -5768,7 +5768,7 @@
      Parameters:
      Source:
      @@ -5922,8 +5922,7 @@
      Parameters:
      - The exception value to reject with if the -promise is not settled within time + The exception value to reject with if the promise is not settled within time @@ -6175,7 +6174,7 @@
      Parameters:
      Source:
      @@ -6415,7 +6414,7 @@
      Parameters:
      Source:
      @@ -6752,7 +6751,7 @@
      Parameters:
      Source:
      @@ -7069,7 +7068,7 @@
      Parameters:
      Source:
      @@ -7451,7 +7450,7 @@
      Parameters:
      Source:
      @@ -7513,7 +7512,7 @@

      Home

      Global

      • diff --git a/docs/html/index.html b/docs/html/index.html index b06c6670..25c0cc27 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -56,7 +56,7 @@

        Home

        Global

        • diff --git a/docs/html/index.js.html b/docs/html/index.js.html index ce0f7177..6c1b44bd 100644 --- a/docs/html/index.js.html +++ b/docs/html/index.js.html @@ -49,10 +49,18 @@

          Source: index.js

          * The 0x API key */ zeroExApiKey: undefined, + /** + * Seconds to wait for the transaction to mine before disregarding it + */ + timeout: undefined, /** * List of liquidity providers for router contract tomoperate on */ liquidityProviders: undefined, + /** + * Flashbot rpc url + */ + flashbotRpc: undefined, /** * 0x monthly rate limit number, if not specified will not respect 0x monthly rate limit */ @@ -194,6 +202,18 @@

          Source: index.js

          const AddressPattern = /^0x[a-fA-F0-9]{40}$/; if (!/^(0x)?[a-fA-F0-9]{64}$/.test(walletPrivateKey)) throw "invalid wallet private key"; + if (options.timeout !== undefined){ + if (typeof options.timeout === "number") { + if (!Number.isInteger(options.timeout) || options.timeout == 0) throw "invalid timeout, must be an integer greater than 0"; + else options.timeout = options.timeout * 1000; + } + else if (typeof options.timeout === "string") { + if (/^\d+$/.test(options.timeout)) options.timeout = Number(options.timeout) * 1000; + else throw "invalid timeout, must be an integer greater than 0"; + if (options.timeout == 0) throw "invalid timeout, must be an integer greater than 0"; + } + else throw "invalid timeout, must be an integer greater than 0"; + } const provider = new ethers.providers.JsonRpcProvider(rpcUrl); const signer = new ethers.Wallet(walletPrivateKey, provider); @@ -212,9 +232,12 @@

          Source: index.js

          config.lps = options?.liquidityProviders; config.apiKey = options?.zeroExApiKey; config.monthlyRatelimit = options?.monthlyRatelimit; + config.timeout = options?.timeout; + config.flashbotRpc = options?.flashbotRpc; config.maxProfit = !!options?.maxProfit; config.maxRatio = !!options?.maxRatio; config.usePublicRpcs = !!options?.usePublicRpcs; + return config; }; @@ -315,7 +338,7 @@

          Home

          Global

          • diff --git a/docs/html/query.js.html b/docs/html/query.js.html index cb941d7f..0240e5b1 100644 --- a/docs/html/query.js.html +++ b/docs/html/query.js.html @@ -165,7 +165,7 @@

            Home

            Global

            • diff --git a/docs/html/router.js.html b/docs/html/router.js.html index ba892dc5..c08b989a 100644 --- a/docs/html/router.js.html +++ b/docs/html/router.js.html @@ -36,6 +36,7 @@

              Source: router.js

              getDataFetcher, getActualPrice, visualizeRoute, + promiseTimeout, bundleTakeOrders } = require("./utils"); @@ -65,6 +66,12 @@

              Source: router.js

              const arbAddress = config.arbAddress; const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis[arbType], signer); @@ -160,6 +167,11 @@

              Source: router.js

              else { console.log(">>> Getting best route for this token pair", "\n"); + let cumulativeAmountFixed = ethers.constants.Zero; + bundledOrders[i].takeOrders.forEach(v => { + cumulativeAmountFixed = cumulativeAmountFixed.add(v.quoteAmount); + }); + console.log( ">>> getting market rate for " + ethers.utils.formatUnits(cumulativeAmountFixed) + @@ -167,10 +179,6 @@

              Source: router.js

              bundledOrders[i].sellTokenSymbol ); - let cumulativeAmountFixed = ethers.constants.Zero; - bundledOrders[i].takeOrders.forEach(v => { - cumulativeAmountFixed = cumulativeAmountFixed.add(v.quoteAmount); - }); const cumulativeAmount = cumulativeAmountFixed.div( "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) ); @@ -394,13 +402,21 @@

              Source: router.js

              ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, @@ -523,7 +539,7 @@

              Home

              Global

              • diff --git a/docs/html/srouter.js.html b/docs/html/srouter.js.html index 914fee62..cb1f6a2a 100644 --- a/docs/html/srouter.js.html +++ b/docs/html/srouter.js.html @@ -36,6 +36,7 @@

                Source: srouter.js

                getDataFetcher, getActualPrice, visualizeRoute, + promiseTimeout, bundleTakeOrders, getActualClearAmount } = require("./utils"); @@ -67,6 +68,12 @@

                Source: srouter.js

                const orderbookAddress = config.orderbookAddress; const maxProfit = config.maxProfit; const maxRatio = config.maxRatio; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis["srouter"], signer); @@ -291,14 +298,22 @@

                Source: srouter.js

                ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); if (receipt.status === 1) { const clearActualAmount = getActualClearAmount( arbAddress, @@ -455,7 +470,7 @@

                Home

                Global

                • diff --git a/docs/html/utils.js.html b/docs/html/utils.js.html index 2d45c912..ea7c7a90 100644 --- a/docs/html/utils.js.html +++ b/docs/html/utils.js.html @@ -464,10 +464,10 @@

                  Source: utils.js

                  * Waits for provided miliseconds * @param {number} ms - Miliseconds to wait */ -const sleep = async(ms) => { +const sleep = async(ms, msg = "") => { let _timeoutReference; return new Promise( - resolve => _timeoutReference = setTimeout(resolve, ms) + resolve => _timeoutReference = setTimeout(resolve(msg), ms), ).finally( () => clearTimeout(_timeoutReference) ); @@ -818,7 +818,8 @@

                  Source: utils.js

                  [...rpcs.map(v => http(v)), ...fallbacks[chainId].transport], { rank: true } ) - : rpcs.map(v => http(v)); + : rpcs.map(v => http(v))[0]; + return createPublicClient({ chain: viemConfig[chainId]?.chain, transport @@ -1258,8 +1259,7 @@

                  Source: utils.js

                  * * @param {Promise} promise - The Promise to put timeout on * @param {number} time - The time in milliseconds - * @param {string | number | bigint | symbol | boolean} exception - The exception value to reject with if the - * promise is not settled within time + * @param {string | number | bigint | symbol | boolean} exception - The exception value to reject with if the promise is not settled within time * @returns A new promise that gets settled with initial promise settlement or rejected with exception value * if the time runs out before the main promise settlement */ @@ -1543,7 +1543,7 @@

                  Home

                  Global

                  • diff --git a/docs/html/zeroex.js.html b/docs/html/zeroex.js.html index 12dad124..af21eb30 100644 --- a/docs/html/zeroex.js.html +++ b/docs/html/zeroex.js.html @@ -28,9 +28,8 @@

                    Source: zeroex.js

                    const axios = require("axios");
                     const ethers = require("ethers");
                    -const { bundleTakeOrders } = require("./utils");
                     const { arbAbis, orderbookAbi } = require("./abis");
                    -const { sleep, getIncome, getActualPrice } = require("./utils");
                    +const { sleep, getIncome, getActualPrice, bundleTakeOrders, promiseTimeout } = require("./utils");
                     
                     
                     const HEADERS = { headers: { "accept-encoding": "null" } };
                    @@ -69,6 +68,12 @@ 

                    Source: zeroex.js

                    const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; // const nativeToken = config.nativeWrappedToken; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // set the api key in headers if (config.apiKey) HEADERS.headers["0x-api-key"] = config.apiKey; @@ -381,14 +386,22 @@

                    Source: zeroex.js

                    ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await tx.wait(); + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, @@ -531,7 +544,7 @@

                    Home

                    Global

                    • diff --git a/example.env b/example.env index 40fd02cd..83e061c0 100644 --- a/example.env +++ b/example.env @@ -8,7 +8,7 @@ BOT_WALLET_PRIVATEKEY="123..." # for specifying more than 1 RPC in the env, separate them by a comma and a space RPC_URL="https://polygon-mainnet.g.alchemy.com/v2/{API_KEY}, https://rpc.ankr.com/polygon/{API_KEY}" -# Option to submit transactions to the flashbot RPC. +# Option to submit transactions using the flashbot RPC. FLASHBOT_RPC="" # bot running mode, one of "router", "0x", "curve", "crouter", "srouter" @@ -67,5 +67,5 @@ MAX_RATIO="true" # Option to use public rpcs as fallback option for 'srouter' and 'router' mode USE_PUBLIC_RPCS="true" -# Optional timeout to wait till the transaction is mined in milliseconds. Default is 5 minutes i.e 300 +# Optional seconds to wait for the transaction to mine before disregarding it TIMEOUT="" \ No newline at end of file diff --git a/src/crouter.js b/src/crouter.js index ac4f009a..8cd58dd9 100644 --- a/src/crouter.js +++ b/src/crouter.js @@ -9,9 +9,9 @@ const { getDataFetcher, getActualPrice, visualizeRoute, + promiseTimeout, bundleTakeOrders, - createViemClient, - awaitTransactionTimeout + createViemClient } = require("./utils"); @@ -164,6 +164,12 @@ const crouterClear = async( const arbAddress = config.arbAddress; const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis[arbType], signer); @@ -649,15 +655,22 @@ const crouterClear = async( ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" - ); - - const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) - // const receipt = await tx.wait(); + ); + + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, diff --git a/src/curve.js b/src/curve.js index bec0835f..579e57ca 100644 --- a/src/curve.js +++ b/src/curve.js @@ -7,9 +7,9 @@ const { getEthPrice, getDataFetcher, getActualPrice, + promiseTimeout, bundleTakeOrders, createViemClient, - awaitTransactionTimeout } = require("./utils"); /** @@ -214,6 +214,12 @@ const curveClear = async( const arbAddress = config.arbAddress; const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis[arbType], signer); @@ -537,15 +543,22 @@ const curveClear = async( ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - // const receipt = await tx.wait(); - const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, diff --git a/src/index.js b/src/index.js index 4eb7fd7e..db65445c 100644 --- a/src/index.js +++ b/src/index.js @@ -22,7 +22,7 @@ const configOptions = { */ zeroExApiKey: undefined, /** - * The 0x API key + * Seconds to wait for the transaction to mine before disregarding it */ timeout: undefined, /** @@ -32,7 +32,7 @@ const configOptions = { /** * Flashbot rpc url */ - flashBotRpc: undefined, + flashbotRpc: undefined, /** * 0x monthly rate limit number, if not specified will not respect 0x monthly rate limit */ @@ -174,10 +174,20 @@ const getConfig = async( const AddressPattern = /^0x[a-fA-F0-9]{40}$/; if (!/^(0x)?[a-fA-F0-9]{64}$/.test(walletPrivateKey)) throw "invalid wallet private key"; + if (options.timeout !== undefined){ + if (typeof options.timeout === "number") { + if (!Number.isInteger(options.timeout) || options.timeout == 0) throw "invalid timeout, must be an integer greater than 0"; + else options.timeout = options.timeout * 1000; + } + else if (typeof options.timeout === "string") { + if (/^\d+$/.test(options.timeout)) options.timeout = Number(options.timeout) * 1000; + else throw "invalid timeout, must be an integer greater than 0"; + if (options.timeout == 0) throw "invalid timeout, must be an integer greater than 0"; + } + else throw "invalid timeout, must be an integer greater than 0"; + } - const provider = options.flashBotRpc ? - new ethers.providers.JsonRpcProvider(options.flashBotRpc) : - new ethers.providers.JsonRpcProvider(rpcUrl); + const provider = new ethers.providers.JsonRpcProvider(rpcUrl); const signer = new ethers.Wallet(walletPrivateKey, provider); const chainId = await signer.getChainId(); const config = CONFIG.find(v => v.chainId === chainId); @@ -194,10 +204,11 @@ const getConfig = async( config.lps = options?.liquidityProviders; config.apiKey = options?.zeroExApiKey; config.monthlyRatelimit = options?.monthlyRatelimit; + config.timeout = options?.timeout; + config.flashbotRpc = options?.flashbotRpc; config.maxProfit = !!options?.maxProfit; config.maxRatio = !!options?.maxRatio; config.usePublicRpcs = !!options?.usePublicRpcs; - config.timeout = options.timeout; return config; }; diff --git a/src/router.js b/src/router.js index a8cef738..1dd7b5af 100644 --- a/src/router.js +++ b/src/router.js @@ -8,8 +8,8 @@ const { getDataFetcher, getActualPrice, visualizeRoute, - bundleTakeOrders, - awaitTransactionTimeout + promiseTimeout, + bundleTakeOrders } = require("./utils"); @@ -38,6 +38,12 @@ const routerClear = async( const arbAddress = config.arbAddress; const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis[arbType], signer); @@ -368,14 +374,21 @@ const routerClear = async( ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - // const receipt = await tx.wait(); - const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, diff --git a/src/srouter.js b/src/srouter.js index 22537db0..58890924 100644 --- a/src/srouter.js +++ b/src/srouter.js @@ -8,9 +8,9 @@ const { getDataFetcher, getActualPrice, visualizeRoute, + promiseTimeout, bundleTakeOrders, - getActualClearAmount, - awaitTransactionTimeout + getActualClearAmount } = require("./utils"); @@ -40,6 +40,12 @@ const srouterClear = async( const orderbookAddress = config.orderbookAddress; const maxProfit = config.maxProfit; const maxRatio = config.maxRatio; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbis["srouter"], signer); @@ -264,15 +270,22 @@ const srouterClear = async( ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - // const receipt = await tx.wait(); - const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); if (receipt.status === 1) { const clearActualAmount = getActualClearAmount( arbAddress, diff --git a/src/utils.js b/src/utils.js index f16b827a..6e935661 100644 --- a/src/utils.js +++ b/src/utils.js @@ -436,10 +436,10 @@ const getOrderStruct = (orderDetails) => { * Waits for provided miliseconds * @param {number} ms - Miliseconds to wait */ -const sleep = async(ms) => { +const sleep = async(ms, msg = "") => { let _timeoutReference; return new Promise( - resolve => _timeoutReference = setTimeout(resolve, ms) + resolve => _timeoutReference = setTimeout(resolve(msg), ms), ).finally( () => clearTimeout(_timeoutReference) ); @@ -1231,8 +1231,7 @@ const appGlobalLogger = (scrub, ...data) => { * * @param {Promise} promise - The Promise to put timeout on * @param {number} time - The time in milliseconds - * @param {string | number | bigint | symbol | boolean} exception - The exception value to reject with if the - * promise is not settled within time + * @param {string | number | bigint | symbol | boolean} exception - The exception value to reject with if the promise is not settled within time * @returns A new promise that gets settled with initial promise settlement or rejected with exception value * if the time runs out before the main promise settlement */ @@ -1473,14 +1472,6 @@ const shuffleArray = (array) => { return array; }; - -function awaitTransactionTimeout(timeout, ...args) { - function timeOut() { - return new Promise((resolve, reject) => setTimeout(reject, timeout, new Error(`Transaction failed to mine after ${timeout}ms`))); - } - return Promise.race([...args, timeOut()]); - } - module.exports = { fallbacks, bnFromFloat, @@ -1506,6 +1497,5 @@ module.exports = { visualizeRoute, build0xQueries, shuffleArray, - createViemClient, - awaitTransactionTimeout + createViemClient }; diff --git a/src/zeroex.js b/src/zeroex.js index aea825b2..082d3e4b 100644 --- a/src/zeroex.js +++ b/src/zeroex.js @@ -1,8 +1,7 @@ const axios = require("axios"); const ethers = require("ethers"); -const { bundleTakeOrders, awaitTransactionTimeout } = require("./utils"); const { arbAbis, orderbookAbi } = require("./abis"); -const { sleep, getIncome, getActualPrice } = require("./utils"); +const { sleep, getIncome, getActualPrice, bundleTakeOrders, promiseTimeout } = require("./utils"); const HEADERS = { headers: { "accept-encoding": "null" } }; @@ -41,6 +40,12 @@ const zeroExClear = async( const orderbookAddress = config.orderbookAddress; const arbType = config.arbType; // const nativeToken = config.nativeWrappedToken; + const flashbotSigner = config.flashbotRpc + ? new ethers.Wallet( + signer.privateKey, + new ethers.providers.JsonRpcProvider(config.flashbotRpc) + ) + : undefined; // set the api key in headers if (config.apiKey) HEADERS.headers["0x-api-key"] = config.apiKey; @@ -353,15 +358,22 @@ const zeroExClear = async( ] ); console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n"); - const tx = await signer.sendTransaction(rawtx); + const tx = flashbotSigner !== undefined + ? await flashbotSigner.sendTransaction(rawtx) + : await signer.sendTransaction(rawtx); console.log("\x1b[33m%s\x1b[0m", config.explorer + "tx/" + tx.hash, "\n"); console.log( ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", "\n" ); - const receipt = await awaitTransactionTimeout(config.timeout,tx.wait()) - // const receipt = await tx.wait(); + const receipt = config.timeout + ? await promiseTimeout( + tx.wait(), + config.timeout, + `Transaction failed to mine after ${config.timeout}ms` + ) + : await tx.wait(); const income = getIncome(signer, receipt); const clearActualPrice = getActualPrice( receipt, From d892e6fcfa5ea6cad6637d4b20f3103ae258ef9a Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 28 Oct 2023 16:14:59 +0000 Subject: [PATCH 3/3] Update utils.js --- src/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.js b/src/utils.js index 6e935661..86795ec9 100644 --- a/src/utils.js +++ b/src/utils.js @@ -790,7 +790,7 @@ const createViemClient = (chainId, rpcs, useFallbacs = false) => { [...rpcs.map(v => http(v)), ...fallbacks[chainId].transport], { rank: true } ) - : rpcs.map(v => http(v))[0]; + : fallback(rpcs.map(v => http(v))); return createPublicClient({ chain: viemConfig[chainId]?.chain,