diff --git a/README.md b/README.md
index 7417ad16..6d6f7378 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# Rain Orderbook Arbitrage Bot
-NodeJS app that clears Rain orderbook orders against major DeFi platforms liquidity by finding arbitrage trades for token pairs of orders details queried from a subgraph or from file containing array of `Order Struct`, bundling them as `takeOrders` and submitting them to [Rain GenericPoolOrderBookFlashBorrower contract](https://github.com/rainprotocol/rain.orderbook.flashborrower.zeroex).
+NodeJS app that clears Rain orderbook orders against major DeFi platforms liquidity by finding arbitrage trades for token pairs of orders details queried from a subgraph or from file containing array of `Order Struct`, bundling them as `takeOrders` and submitting them to one of [Rain Arb Contracts](https://github.com/rainprotocol/rain.orderbook/tree/main/src/concrete).
This app requires NodeJS v18 or higher to run and is docker ready.
This app can also be run in Github Actions with a cron job, please read below for more details.
@@ -211,15 +211,14 @@ const configOptions = {
monthlyRatelimit : 1000000, // 0x monthly rate limit, only used for 0x mode
hideSensitiveData : true, // set to true to hide sensitive data such as wallet private key or rpc url from apearing in logs
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
+ maxRatio : true, // option to maximize the maxIORatio in "srouter" mode
+ usePublicRpcs : false, // option to fallback to public rpcs
liquidityProviders : [ // list of liquidity providers for "router" mode to get quotes from (optional)
"sushiswapv2",
"uniswapv2"
]
}
-const clearOptions = {
- prioritization : true, // clear better deals first
+const clearOptions = {s
gasCoveragePercentage : "500" // percentage of the transaction gas cost denominated in receiving ERC20 to be earned from the transaction in order for it to be successfull, as an example a value of 500 means atleast 5x the amount of transaction gas cost needs to be earned for the transaction to be successfull
}
diff --git a/arb-bot.js b/arb-bot.js
index 89f7580f..6fc7428b 100755
--- a/arb-bot.js
+++ b/arb-bot.js
@@ -127,7 +127,8 @@ const arbRound = async options => {
{
orderHash : options.orderHash,
orderOwner : options.orderOwner,
- orderInterpreter: options.orderInterpreter
+ orderInterpreter: options.orderInterpreter,
+ shuffle : options.shuffle
}
);
await clear(
@@ -147,6 +148,7 @@ const main = async argv => {
const rpcs = [...options.rpc];
let roundGap = 10000;
let rpcTurn = 0;
+ let shuffle = 0;
if (options.repetitions) {
if (/^\d+$/.test(options.repetitions)) repetitions = Number(options.repetitions);
@@ -167,6 +169,7 @@ const main = async argv => {
// eslint-disable-next-line no-constant-condition
if (repetitions === -1) while (true) {
options.rpc = rpcs[rpcTurn];
+ options.shuffle = shuffle;
try {
await arbRound(options);
console.log("\x1b[32m%s\x1b[0m", "Round finished successfully!");
@@ -178,10 +181,13 @@ const main = async argv => {
}
if (rpcTurn === rpcs.length - 1) rpcTurn = 0;
else rpcTurn++;
+ if (shuffle === 3) shuffle = 0;
+ else shuffle++;
await sleep(roundGap);
}
else for (let i = 1; i <= repetitions; i++) {
options.rpc = rpcs[rpcTurn];
+ options.shuffle = shuffle;
try {
await arbRound(options);
console.log("\x1b[32m%s\x1b[0m", `Round ${i} finished successfully!`);
@@ -195,6 +201,8 @@ const main = async argv => {
}
if (rpcTurn === rpcs.length - 1) rpcTurn = 0;
else rpcTurn++;
+ if (shuffle === 3) shuffle = 0;
+ else shuffle++;
await sleep(roundGap);
}
};
diff --git a/config.json b/config.json
index d9bf290e..4720369e 100644
--- a/config.json
+++ b/config.json
@@ -121,6 +121,16 @@
"address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
"decimals": 18
},
+ "liquidityProviders": [
+ "apeswap",
+ "dfyn",
+ "elk",
+ "jetswap",
+ "quickswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv3"
+ ],
"stableTokens": [
{
"symbol": "USDT",
@@ -222,6 +232,16 @@
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"decimals": 18
},
+ "liquidityProviders": [
+ "apeswap",
+ "curveswap",
+ "elk",
+ "pancakeswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv2",
+ "uniswapv3"
+ ],
"stableTokens": [
{
"symbol": "USDT",
@@ -305,28 +325,6 @@
}
]
},
- {
- "network": "goerli",
- "chainId": 5,
- "explorer": "https://goerli.etherscan.io/",
- "zeroEx": {
- "apiUrl": "https://goerli.api.0x.org/",
- "proxyAddress": "0xf91bb752490473b8342a3e964e855b9f9a2a668e"
- },
- "curve": {
- "pools": []
- },
- "nativeToken": {
- "symbol": "ETH",
- "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
- "decimals": 18
- },
- "nativeWrappedToken": {
- "symbol": "WETH",
- "address": "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6",
- "decimals": 18
- }
- },
{
"network": "avalanche",
"chainId": 43114,
@@ -350,6 +348,12 @@
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"decimals": 18
},
+ "liquidityProviders": [
+ "elk",
+ "traderjoe",
+ "sushiswapv3",
+ "sushiswapv2"
+ ],
"stableTokens": [
{
"symbol": "USDT.e",
@@ -436,6 +440,13 @@
"address": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
"decimals": 18
},
+ "liquidityProviders": [
+ "dfyn",
+ "elk",
+ "sushiswapv3",
+ "uniswapv3",
+ "sushiswapv2"
+ ],
"stableTokens": [
{
"symbol": "USDT",
@@ -507,6 +518,14 @@
"address": "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83",
"decimals": 18
},
+ "liquidityProviders": [
+ "dfyn",
+ "elk",
+ "jetswap",
+ "spookyswap",
+ "sushiswapv3",
+ "sushiswapv2"
+ ],
"stableTokens": [
{
"symbol": "fUSDT",
@@ -588,6 +607,16 @@
"address": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
"decimals": 18
},
+ "liquidityProviders": [
+ "apeswap",
+ "biswap",
+ "elk",
+ "jetswap",
+ "pancakeswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv3"
+ ],
"stableTokens": [
{
"symbol": "USDT",
@@ -669,6 +698,10 @@
"address": "0x471EcE3750Da237f93B8E339c536989b8978a438",
"decimals": 18
},
+ "liquidityProviders": [
+ "ubeswap",
+ "sushiswapv2"
+ ],
"stableTokens": [
{
"symbol": "MAI",
@@ -700,6 +733,11 @@
"address": "0x4200000000000000000000000000000000000006",
"decimals": 18
},
+ "liquidityProviders": [
+ "elk",
+ "sushiswapv3",
+ "uniswapv3"
+ ],
"stableTokens": {
}
@@ -726,5 +764,27 @@
"address": "0x5B67676a984807a212b1c59eBFc9B3568a474F0a",
"decimals": 18
}
+ },
+ {
+ "network": "goerli",
+ "chainId": 5,
+ "explorer": "https://goerli.etherscan.io/",
+ "zeroEx": {
+ "apiUrl": "https://goerli.api.0x.org/",
+ "proxyAddress": "0xf91bb752490473b8342a3e964e855b9f9a2a668e"
+ },
+ "curve": {
+ "pools": []
+ },
+ "nativeToken": {
+ "symbol": "ETH",
+ "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
+ "decimals": 18
+ },
+ "nativeWrappedToken": {
+ "symbol": "WETH",
+ "address": "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6",
+ "decimals": 18
+ }
}
]
\ No newline at end of file
diff --git a/docs/html/curve.js.html b/docs/html/curve.js.html
index 9f54f2df..218f30e2 100644
--- a/docs/html/curve.js.html
+++ b/docs/html/curve.js.html
@@ -34,7 +34,6 @@
Source: curve.js
getEthPrice,
getDataFetcher,
getActualPrice,
- // estimateProfit,
bundleTakeOrders
} = require("./utils");
@@ -155,7 +154,7 @@ Source: curve.js
* @param {ethers.Signer} - The ethersjs signer
* @param {boolean} sort - (optional) Sort based on best deals or not
*/
-const prepare = async(bundledOrders, availableSwaps, config, signer, sort = true) => {
+const prepare = (bundledOrders, availableSwaps, config, signer) => {
for (let i = 0; i < bundledOrders.length; i++) {
let pairFormat;
const bOrder = bundledOrders[i];
@@ -187,61 +186,62 @@ Source: curve.js
: pool.underlyingCoinsUnwrapped.findIndex(
v => v.symbol === bOrder.sellTokenSymbol
);
- try {
- let rate;
- // let cumulativeAmountFixed = ethers.constants.Zero;
- // bOrder.takeOrders.forEach(v => {
- // cumulativeAmountFixed = cumulativeAmountFixed.add(v.quoteAmount);
- // });
- // const cumulativeAmount = cumulativeAmountFixed.div("1" + "0".repeat(18 - bOrder.sellTokenDecimals));
- if (pairFormat === "c") {
- rate = await bOrder.poolContract.get_dy(
- bOrder.sellTokenIndex,
- bOrder.buyTokenIndex,
- // cumulativeAmount
- "1" + "0".repeat(bOrder.sellTokenDecimals)
- );
- }
- else {
- rate = await bOrder.poolContract.get_dy_underlying(
- bOrder.sellTokenIndex,
- bOrder.buyTokenIndex,
- // cumulativeAmount
- "1" + "0".repeat(bOrder.sellTokenDecimals)
- );
- }
- // const rateFixed = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- // const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
- const price = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- bOrder.initPrice = price;
-
- console.log(`Current market price for ${pair}: ${ethers.utils.formatEther(price)}`);
- console.log("Current ratio of the orders in this token pair:");
- bOrder.takeOrders.forEach(v => {
- console.log(ethers.utils.formatEther(v.ratio));
- });
- bOrder.takeOrders = bOrder.takeOrders.filter(
- v => price.gte(v.ratio)
- );
- console.log("\n");
- }
- catch(error) {
- console.log(`>>> could not get price for this ${pair} due to:`);
- console.log(error);
- }
+ // try {
+ // let rate;
+ // // let cumulativeAmountFixed = ethers.constants.Zero;
+ // // bOrder.takeOrders.forEach(v => {
+ // // cumulativeAmountFixed = cumulativeAmountFixed.add(v.quoteAmount);
+ // // });
+ // // const cumulativeAmount = cumulativeAmountFixed.div("1" + "0".repeat(18 - bOrder.sellTokenDecimals));
+ // if (pairFormat === "c") {
+ // rate = await bOrder.poolContract.get_dy(
+ // bOrder.sellTokenIndex,
+ // bOrder.buyTokenIndex,
+ // // cumulativeAmount
+ // "1" + "0".repeat(bOrder.sellTokenDecimals)
+ // );
+ // }
+ // else {
+ // rate = await bOrder.poolContract.get_dy_underlying(
+ // bOrder.sellTokenIndex,
+ // bOrder.buyTokenIndex,
+ // // cumulativeAmount
+ // "1" + "0".repeat(bOrder.sellTokenDecimals)
+ // );
+ // }
+ // // const rateFixed = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
+ // // const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
+ // const price = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
+ // bOrder.initPrice = price;
+
+ // console.log(`Current market price for ${pair}: ${ethers.utils.formatEther(price)}`);
+ // console.log("Current ratio of the orders in this token pair:");
+ // bOrder.takeOrders.forEach(v => {
+ // console.log(ethers.utils.formatEther(v.ratio));
+ // });
+ // bOrder.takeOrders = bOrder.takeOrders.filter(
+ // v => price.gte(v.ratio)
+ // );
+ // console.log("\n");
+ // }
+ // catch(error) {
+ // console.log(`>>> could not get price for this ${pair} due to:`);
+ // console.log(error);
+ // }
}
}
- console.log(
- ">>> Filtering bundled orders with lower ratio than current market price...",
- "\n"
- );
- bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
+ // console.log(
+ // ">>> Filtering bundled orders with lower ratio than current market price...",
+ // "\n"
+ // );
+ // bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
+ // if (sort) {
+ // console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
+ // bundledOrders.sort(
+ // (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
+ // );
+ // }
+ bundledOrders = bundledOrders.filter(v => v.poolIndex !== undefined);
return bundledOrders;
};
@@ -252,20 +252,17 @@ Source: curve.js
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction
* for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const curveClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
const signer = config.signer;
const arbAddress = config.arbAddress;
@@ -278,10 +275,10 @@ Source: curve.js
// instantiating orderbook contract
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
- let gasPrice = await signer.provider.getGasPrice();
-
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32mCURVE.FI\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
@@ -294,17 +291,12 @@ Source: curve.js
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb);
- console.log(
- "------------------------- Getting Best Deals From Curve -------------------------",
- "\n"
- );
const availableSwaps = getAvailableSwaps(config);
- bundledOrders = await prepare(
+ bundledOrders = prepare(
bundledOrders,
availableSwaps,
config,
- signer,
- prioritization
+ signer
);
}
else {
@@ -317,16 +309,14 @@ Source: curve.js
return;
}
- console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
-
const report = [];
- const dataFetcher = getDataFetcher(config, processLps(config.lps), !!config.usePublicRpc);
+ const dataFetcher = getDataFetcher(
+ config,
+ processLps(config.lps, config.chainId),
+ !!config.usePublicRpc
+ );
for (let i = 0; i < bundledOrders.length; i++) {
try {
- gasPrice = await signer.provider.getGasPrice();
console.log(
`------------------------- Trying To Clear ${
bundledOrders[i].buyTokenSymbol
@@ -476,14 +466,16 @@ Source: curve.js
}
if (arbType === "order-taker") takeOrdersConfigStruct.data = exchangeData;
- // console.log(">>> Estimating the profit for this token pair...", "\n");
- const ethPrice = await getEthPrice(
- config,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- gasPrice,
- dataFetcher
- );
+ const gasPrice = await signer.provider.getGasPrice();
+ const ethPrice = gasCoveragePercentage !== "0"
+ ? "0"
+ : await getEthPrice(
+ config,
+ bundledOrders[i].buyToken,
+ bundledOrders[i].buyTokenDecimals,
+ gasPrice,
+ dataFetcher
+ );
if (ethPrice === undefined) console.log("can not get ETH price, skipping...", "\n");
else {
const rawtx = {
@@ -505,48 +497,9 @@ Source: curve.js
};
console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
let gasLimit = await signer.estimateGas(rawtx);
- gasLimit = gasLimit.mul("11").div("10");
+ gasLimit = gasLimit.mul("112").div("100");
rawtx.gasLimit = gasLimit;
const gasCost = gasLimit.mul(gasPrice);
- // let gasLimit;
- // console.log("Block Number: " + await signer.provider.getBlockNumber());
- // if (arbType === "order-taker") gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // // set to zero for estimation
- // ethers.constants.Zero,
- // { gasPrice }
- // );
- // else gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // // set to zero for estimation
- // ethers.constants.Zero,
- // data,
- // { gasPrice }
- // );
- // gasLimit = gasLimit.mul("11").div("10");
- // const gasCost = gasLimit.mul(gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // ethers.utils.formatEther(bundledOrders[i].initPrice),
- // ethPrice,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
-
- // if (maxEstimatedProfit.isNegative()) console.log(
- // ">>> Skipping because estimated negative profit for this token pair",
- // "\n"
- // );
- // else {
- console.log(">>> Trying to submit the transaction for this token pair...", "\n");
const gasCostInToken = ethers.utils.parseUnits(
ethPrice
).mul(
@@ -556,43 +509,50 @@ Source: curve.js
36 - bundledOrders[i].buyTokenDecimals
)
);
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- arbType === "order-taker"
- ? [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- : [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100),
- exchangeData
- ]
- );
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- const tx = await signer.sendTransaction(rawtx);
- // if (arbType === "order-taker") tx = await arb.arb(
- // takeOrdersConfigStruct,
- // // set to zero because only profitable transactions are submitted
- // gasCostInToken.mul(gasCoveragePercentage).div(100),
- // { gasPrice, gasLimit }
- // );
- // else tx = await arb.arb(
- // takeOrdersConfigStruct,
- // // set to zero because only profitable transactions are submitted
- // gasCostInToken.mul(gasCoveragePercentage).div(100),
- // data,
- // { gasPrice, gasLimit }
- // );
- 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"
- );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.15
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100"),
+ exchangeData
+ ]
+ );
+ await signer.estimateGas(rawtx);
+ }
try {
+ console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100"),
+ exchangeData
+ ]
+ );
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = 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();
- // console.log(receipt);
const income = getIncome(signer, receipt);
const clearActualPrice = getActualPrice(
receipt,
@@ -621,10 +581,10 @@ Source: curve.js
`${bundledOrders[i].takeOrders.length} orders cleared successfully of this token pair!`,
"\n"
);
- console.log(
- "\x1b[36m%s\x1b[0m",
- `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
- );
+ // console.log(
+ // "\x1b[36m%s\x1b[0m",
+ // `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
+ // );
console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
ethers.utils.formatUnits(
@@ -659,15 +619,10 @@ Source: curve.js
sellToken: bundledOrders[i].sellToken,
sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
clearedAmount: bundledQuoteAmount.toString(),
- clearPrice: ethers.utils.formatEther(
- bundledOrders[i].initPrice
- ),
- // clearGuaranteedPrice: ethers.utils.formatUnits(
- // guaranteedAmount,
- // bundledOrders[i].buyTokenDecimals
+ // clearPrice: ethers.utils.formatEther(
+ // bundledOrders[i].initPrice
// ),
clearActualPrice,
- // maxEstimatedProfit,
gasUsed: receipt.gasUsed,
gasCost: actualGasCost,
income,
@@ -679,12 +634,16 @@ Source: curve.js
console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
console.log(error, "\n");
}
- // }
}
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
+ if (error === "dryrun" || error === "nomatch") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction dry run failed, skipping...");
+ }
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ }
}
}
}
@@ -708,13 +667,13 @@ Source: curve.js
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/global.html b/docs/html/global.html
index af4b25b2..59e52d05 100644
--- a/docs/html/global.html
+++ b/docs/html/global.html
@@ -204,7 +204,7 @@ (constant) P
Source:
@@ -266,7 +266,7 @@ (constant) ZAP
Source:
@@ -408,13 +408,13 @@ (constant) (constant) fallbackTransports
+(constant) fallbacks
- Fallback transports for viem client
+ Chain specific fallback data
@@ -624,7 +624,7 @@ Parameters:
Source:
@@ -796,7 +796,7 @@ Parameters:
Source:
@@ -842,7 +842,7 @@ Returns:
- (async) bundleTakeOrders(ordersDetails, orderbook, arb)
+ build0xQueries(api, queries, tokenAddress, tokenDecimals, tokenSymbol)
@@ -850,7 +850,14 @@ (async)
- Builds and bundles orders which their details are queried from a orderbook subgraph by checking the vault balances and evaling
+ Builds initial 0x requests bodies from token addresses that is required
+for getting token prices with least amount of hits possible and that is
+to pair up tokens in a way that each show up only once in a request body
+so that the number of requests will be: "number-of-tokens / 2" at best or
+"(number-of-tokens / 2) + 1" at worst if the number of tokens is an odd digit.
+This way the responses will include the "rate" for sell/buy tokens to native
+network token which will be used to estimate the initial price of all possible
+token pair combinations.
@@ -886,7 +893,30 @@ Parameters:
- ordersDetails
+ api
+
+
+
+
+
+string
+
+
+
+
+
+
+
+
+
+ The 0x API endpoint URL
+
+
+
+
+
+
+ queries
@@ -902,20 +932,20 @@ Parameters:
- Orders details queried from subgraph
+ The array that keeps the 0x query text
- orderbook
+ tokenAddress
-ethers.Contract
+string
@@ -925,20 +955,20 @@ Parameters:
- The Orderbook EthersJS contract instance with signer
+ The token address
- arb
+ tokenDecimals
-ethers.Contract
+number
@@ -948,7 +978,30 @@ Parameters:
- The Arb EthersJS contract instance with signer
+ The token decimals
+
+
+
+
+
+
+ tokenSymbol
+
+
+
+
+
+string
+
+
+
+
+
+
+
+
+
+ The token symbol
@@ -989,7 +1042,7 @@ Parameters:
Source:
@@ -1014,16 +1067,6 @@ Parameters:
-Returns:
-
-
-
- Array of bundled take orders
-
-
-
-
-
@@ -1035,7 +1078,7 @@ Returns:
- (async) clear(mode, config, ordersDetails, options)
+ (async) bundleTakeOrders(ordersDetails, orderbook, arb)
@@ -1043,7 +1086,7 @@ (async) clear
- Method to find and take arbitrage trades for Rain Orderbook orders against some liquidity providers
+ Builds and bundles orders which their details are queried from a orderbook subgraph by checking the vault balances and evaling
@@ -1079,36 +1122,13 @@ Parameters:
- mode
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- The mode for clearing, either "0x" or "curve" or "router"
-
-
-
-
-
-
- config
+ ordersDetails
-object
+Array.<any>
@@ -1118,20 +1138,20 @@ Parameters:
- The configuration object
+ Orders details queried from subgraph
- ordersDetails
+ orderbook
-Array.<any>
+ethers.Contract
@@ -1141,20 +1161,20 @@ Parameters:
- The order details queried from subgraph
+ The Orderbook EthersJS contract instance with signer
- options
+ arb
-clearOptions
+ethers.Contract
@@ -1164,7 +1184,7 @@ Parameters:
- The options for clear, 'slippage',' gasCoveragePercentage' and 'prioritization'
+ The Arb EthersJS contract instance with signer
@@ -1205,7 +1225,7 @@ Parameters:
Source:
@@ -1234,7 +1254,7 @@ Returns:
- The report of details of cleared orders
+ Array of bundled take orders
@@ -1251,7 +1271,7 @@ Returns:
- (async) curveClear(config, ordersDetails, gasCoveragePercentage, prioritization)
+ (async) clear(mode, config, ordersDetails, options)
@@ -1259,7 +1279,7 @@ (async) cur
- Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with curve
+ Method to find and take arbitrage trades for Rain Orderbook orders against some liquidity providers
@@ -1285,8 +1305,6 @@ Parameters:
- Default
-
Description
@@ -1297,13 +1315,13 @@ Parameters:
- config
+ mode
-object
+string
@@ -1312,25 +1330,21 @@ Parameters:
-
-
-
-
- The configuration object
+ The mode for clearing, either "0x" or "curve" or "router"
- ordersDetails
+ config
-Array.<any>
+object
@@ -1339,25 +1353,21 @@ Parameters:
-
-
-
-
- The order details queried from subgraph
+ The configuration object
- gasCoveragePercentage
+ ordersDetails
-string
+Array.<any>
@@ -1366,28 +1376,21 @@ Parameters:
-
-
- 100
-
-
-
- (optional) The percentage of the gas cost to cover on each transaction
-for it to be considered profitable and get submitted
+ The order details queried from subgraph
- prioritization
+ options
-boolean
+clearOptions
@@ -1396,14 +1399,8 @@ Parameters:
-
-
- true
-
-
-
- (optional) Prioritize better deals to get cleared first, default is true
+ The options for clear, such as 'gasCoveragePercentage''
@@ -1444,7 +1441,7 @@ Parameters:
Source:
@@ -1490,7 +1487,7 @@ Returns:
- estimateProfit(pairPrice, ethPrice, bundledOrder, gas, gasCoveragePercentage)
+ (async) curveClear(config, ordersDetails, gasCoveragePercentage)
@@ -1498,7 +1495,7 @@ estimat
- Estimates the profit for a single bundled orders struct
+ Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with curve
@@ -1536,61 +1533,7 @@ Parameters:
- pairPrice
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
-
-
-
-
- The price token pair
-
-
-
-
-
-
- ethPrice
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
-
-
-
-
- Price of ETH to buy token
-
-
-
-
-
-
- bundledOrder
+ config
@@ -1610,20 +1553,20 @@ Parameters:
- The bundled order object
+ The configuration object
- gas
+ ordersDetails
-ethers.BigNumber
+Array.<any>
@@ -1637,7 +1580,7 @@ Parameters:
- The estimated gas cost in ETH
+ The order details queried from subgraph
@@ -1666,7 +1609,8 @@ Parameters:
- Percentage of gas to cover, default is 100,i.e. full gas coverage
+ (optional) The percentage of the gas cost to cover on each transaction
+for it to be considered profitable and get submitted
@@ -1707,7 +1651,7 @@ Parameters:
Source:
@@ -1736,7 +1680,7 @@ Returns:
- The estimated profit
+ The report of details of cleared orders
@@ -1753,7 +1697,7 @@ Returns:
- (async) fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken, excludePools)
+ estimateProfit(pairPrice, ethPrice, bundledOrder, gas, gasCoveragePercentage)
@@ -1761,7 +1705,7 @@ (as
- A wrapper for DataFetcher fetchPoolsForToken() to avoid any errors for liquidity providers that are not available for target chain
+ Estimates the profit for a single bundled orders struct
@@ -1787,6 +1731,8 @@ Parameters:
+ Default
+
Description
@@ -1797,13 +1743,13 @@ Parameters:
- dataFetcher
+ pairPrice
-DataFetcher
+string
@@ -1812,21 +1758,25 @@ Parameters:
-
- DataFetcher instance
+
+
+
+
+
+ The price token pair
- fromToken
+ ethPrice
-Token
+string
@@ -1835,21 +1785,25 @@ Parameters:
+
+
+
+
- The from token
+ Price of ETH to buy token
- toToken
+ bundledOrder
-Token
+object
@@ -1858,21 +1812,52 @@ Parameters:
+
+
+
+
- The to token
+ The bundled order object
- excludePools
+ gas
-Array.<string>
+ethers.BigNumber
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The estimated gas cost in ETH
+
+
+
+
+
+
+ gasCoveragePercentage
+
+
+
+
+
+string
@@ -1881,8 +1866,14 @@ Parameters:
+
+
+ 100
+
+
+
- Set of pools to exclude
+ Percentage of gas to cover, default is 100,i.e. full gas coverage
@@ -1923,7 +1914,7 @@ Parameters:
Source:
@@ -1948,6 +1939,16 @@ Parameters:
+Returns:
+
+
+
+ The estimated profit
+
+
+
+
+
@@ -1959,7 +1960,7 @@ Parameters:
- fromFixed18(bn, decimals)
+ (async) fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken, excludePools)
@@ -1967,7 +1968,7 @@ fromFixed1
- Convert a 18 fixed point BigNumber to a BigNumber with some other decimals point
+ A wrapper for DataFetcher fetchPoolsForToken() to avoid any errors for liquidity providers that are not available for target chain
@@ -2003,13 +2004,13 @@ Parameters:
- bn
+ dataFetcher
-BigNumber
+DataFetcher
@@ -2019,20 +2020,20 @@ Parameters:
- The BigNumber to convert
+ DataFetcher instance
- decimals
+ fromToken
-number
+Token
@@ -2042,7 +2043,53 @@ Parameters:
- The decimals point of convert the given BigNumber
+ The from token
+
+
+
+
+
+
+ toToken
+
+
+
+
+
+Token
+
+
+
+
+
+
+
+
+
+ The to token
+
+
+
+
+
+
+ excludePools
+
+
+
+
+
+Array.<string>
+
+
+
+
+
+
+
+
+
+ Set of pools to exclude
@@ -2083,7 +2130,7 @@ Parameters:
Source:
@@ -2108,16 +2155,6 @@ Parameters:
-Returns:
-
-
-
- A decimals point BigNumber
-
-
-
-
-
@@ -2129,7 +2166,7 @@ Returns:
- getActualClearAmount(arbAddress, receipt)
+ fromFixed18(bn, decimals)
@@ -2137,7 +2174,7 @@ g
- Extracts the actual clear amount (received token value) from transaction receipt
+ Convert a 18 fixed point BigNumber to a BigNumber with some other decimals point
@@ -2173,13 +2210,13 @@ Parameters:
- arbAddress
+ bn
-string
+BigNumber
@@ -2189,20 +2226,20 @@ Parameters:
- The arb contract address
+ The BigNumber to convert
- receipt
+ decimals
-any
+number
@@ -2212,7 +2249,7 @@ Parameters:
- The transaction receipt
+ The decimals point of convert the given BigNumber
@@ -2253,7 +2290,7 @@ Parameters:
Source:
@@ -2282,7 +2319,7 @@ Returns:
- The actual clear amount
+ A decimals point BigNumber
@@ -2299,7 +2336,7 @@ Returns:
- getActualPrice(receipt, orderbook, arb, amount, buyDecimals)
+ getActualClearAmount(arbAddress, receipt)
@@ -2307,7 +2344,7 @@ getActu
- Calculates the actual clear price from transactioin event
+ Extracts the actual clear amount (received token value) from transaction receipt
@@ -2343,76 +2380,7 @@ Parameters:
- receipt
-
-
-
-
-
-any
-
-
-
-
-
-
-
-
-
- The transaction receipt
-
-
-
-
-
-
- orderbook
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- The Orderbook contract address
-
-
-
-
-
-
- arb
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- The Arb contract address
-
-
-
-
-
-
- amount
+ arbAddress
@@ -2428,20 +2396,20 @@ Parameters:
- The clear amount
+ The arb contract address
- buyDecimals
+ receipt
-number
+any
@@ -2451,7 +2419,7 @@ Parameters:
- The buy token decimals
+ The transaction receipt
@@ -2492,7 +2460,7 @@ Parameters:
Source:
@@ -2521,7 +2489,7 @@ Returns:
- The actual clear price or undefined if necessary info not found in transaction events
+ The actual clear amount
@@ -2538,7 +2506,7 @@ Returns:
- getAvailableSwaps(config)
+ getActualPrice(receipt, orderbook, arb, amount, buyDecimals)
@@ -2546,7 +2514,7 @@ getA
- Returns array of available swaps pairs from specified curve pools in config file
+ Calculates the actual clear price from transactioin event
@@ -2582,7 +2550,246 @@ Parameters:
- config
+ receipt
+
+
+
+
+
+any
+
+
+
+
+
+
+
+
+
+ The transaction receipt
+
+
+
+
+
+
+ orderbook
+
+
+
+
+
+string
+
+
+
+
+
+
+
+
+
+ The Orderbook contract address
+
+
+
+
+
+
+ arb
+
+
+
+
+
+string
+
+
+
+
+
+
+
+
+
+ The Arb contract address
+
+
+
+
+
+
+ amount
+
+
+
+
+
+string
+
+
+
+
+
+
+
+
+
+ The clear amount
+
+
+
+
+
+
+ buyDecimals
+
+
+
+
+
+number
+
+
+
+
+
+
+
+
+
+ The buy token decimals
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ The actual clear price or undefined if necessary info not found in transaction events
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAvailableSwaps(config)
+
+
+
+
+
+
+
+ Returns array of available swaps pairs from specified curve pools in config file
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name
+
+
+ Type
+
+
+
+
+
+ Description
+
+
+
+
+
+
+
+
+ config
@@ -2639,7 +2846,7 @@ Parameters:
Source:
@@ -2891,7 +3098,7 @@ Parameters:
Source:
@@ -3061,7 +3268,7 @@ Parameters:
Source:
@@ -3290,7 +3497,7 @@ Parameters:
Source:
@@ -3450,7 +3657,7 @@ Parameters:
Source:
@@ -3813,7 +4020,7 @@ Parameters:
Source:
@@ -3950,7 +4157,7 @@ Parameters:
Source:
@@ -4097,7 +4304,7 @@ Parameters:
Source:
@@ -4143,7 +4350,7 @@ Returns:
- getQuery(orderHash, owner, interpreter)
+ getQuery(orderHash, owner, interpreter, shuffle)
@@ -4177,6 +4384,8 @@ Parameters:
+ Default
+
Description
@@ -4202,6 +4411,10 @@ Parameters:
+
+
+
+
The order hash to apply as filter
@@ -4225,6 +4438,10 @@ Parameters:
+
+
+
+
The order owner to apply as filter
@@ -4248,13 +4465,48 @@ Parameters:
+
+
+
+
The interpreter to apply as filter
-
-
+
+
+
+ shuffle
+
+
+
+
+
+number
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ (optional) A number in range of 0 - 3 that
+will change the order of getting order details from subgraph, mostly
+used for continuous bot running
+
+
+
+
+
@@ -4290,7 +4542,7 @@ Parameters:
Source:
@@ -4564,983 +4816,7 @@ Parameters:
- abiencoded
-
-
-
-
-
-boolean
-
-
-
-
-
-
-
-
-
- If the result should be abi encoded or not
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- initRequests(api, quotes, tokenAddress, tokenDecimals, tokenSymbol)
-
-
-
-
-
-
-
- Builds initial 0x requests bodies from token addresses that is required
-for getting token prices with least amount of hits possible and that is
-to pair up tokens in a way that each show up only once in a request body
-so that the number of requests will be: "number-of-tokens / 2" at best or
-"(number-of-tokens / 2) + 1" at worst if the number of tokens is an odd digit.
-This way the responses will include the "rate" for sell/buy tokens to native
-network token which will be used to estimate the initial price of all possible
-token pair combinations.
-
-
-
-
-
-
-
-
-
-
- Parameters:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- api
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- The 0x API endpoint URL
-
-
-
-
-
-
- quotes
-
-
-
-
-
-Array.<any>
-
-
-
-
-
-
-
-
-
- The array that keeps the quotes
-
-
-
-
-
-
- tokenAddress
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- The token address
-
-
-
-
-
-
- tokenDecimals
-
-
-
-
-
-number
-
-
-
-
-
-
-
-
-
- The token decimals
-
-
-
-
-
-
- tokenSymbol
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- The token symbol
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (async) interpreterEval(interpreter, arbAddress, obAddress, order, inputIndex, outputIndex)
-
-
-
-
-
-
-
- Calls eval for a specific order to get its max output and ratio
-
-
-
-
-
-
-
-
-
-
- Parameters:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- interpreter
-
-
-
-
-
-ethers.Contract
-
-
-
-
-
-
-
-
-
- The interpreter ethersjs contract instance with signer
-
-
-
-
-
-
- arbAddress
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- Arb contract address
-
-
-
-
-
-
- obAddress
-
-
-
-
-
-string
-
-
-
-
-
-
-
-
-
- OrderBook contract address
-
-
-
-
-
-
- order
-
-
-
-
-
-object
-
-
-
-
-
-
-
-
-
- The order details fetched from sg
-
-
-
-
-
-
- inputIndex
-
-
-
-
-
-number
-
-
-
-
-
-
-
-
-
- The input token index
-
-
-
-
-
-
- outputIndex
-
-
-
-
-
-number
-
-
-
-
-
-
-
-
-
- The ouput token index
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Returns:
-
-
-
- The ratio and maxOuput as BigNumber
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (async) prepare(bundledOrders, availableSwaps, config, sort)
-
-
-
-
-
-
-
- Prepares the bundled orders by getting the best deals from Curve pools and sorting the
-bundled orders based on the best deals
-
-
-
-
-
-
-
-
-
-
- Parameters:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- bundledOrders
-
-
-
-
-
-Array.<any>
-
-
-
-
-
-
-
-
-
- The bundled orders array
-
-
-
-
-
-
- availableSwaps
-
-
-
-
-
-Array.<any>
-
-
-
-
-
-
-
-
-
- The available swaps from Curve specofied pools
-
-
-
-
-
-
- config
-
-
-
-
-
-any
-
-
-
-
-
-
-
-
-
- The network config data
-
-
-
-
-
-
-
-
-
-
-
-
-ethers.Signer
-
-
-
-
-
-
-
-
-
- The ethersjs signer
-
-
-
-
-
-
- sort
-
-
-
-
-
-boolean
-
-
-
-
-
-
-
-
-
- (optional) Sort based on best deals or not
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (async) prepare(bundledOrders, dataFetcher, config, gasPrice, sort)
-
-
-
-
-
-
-
- Prepares the bundled orders by getting the best deals from Router and sorting the
-bundled orders based on the best deals
-
-
-
-
-
-
-
-
-
-
- Parameters:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
- Default
-
-
- Description
-
-
-
-
-
-
-
-
- bundledOrders
-
-
-
-
-
-Array.<any>
-
-
-
-
-
-
-
-
-
-
-
-
-
- The bundled orders array
-
-
-
-
-
-
- dataFetcher
-
-
-
-
-
-any
-
-
-
-
-
-
-
-
-
-
-
-
-
- The DataFetcher instance
-
-
-
-
-
-
- config
-
-
-
-
-
-any
-
-
-
-
-
-
-
-
-
-
-
-
-
- The network config data
-
-
-
-
-
-
- gasPrice
-
-
-
-
-
-ethers.BigNumber
-
-
-
-
-
-
-
-
-
-
-
-
-
- The network gas price
-
-
-
-
-
-
- sort
+ abiencoded
@@ -5555,14 +4831,8 @@ Parameters:
-
-
- true
-
-
-
- (optional) Sort based on best deals or not
+ If the result should be abi encoded or not
@@ -5603,7 +4873,7 @@ Parameters:
Source:
@@ -5639,7 +4909,7 @@ Parameters:
- (async) prepare(bundledOrders, dataFetcher, config, gasPrice, sort)
+ (async) interpreterEval(interpreter, arbAddress, obAddress, order, inputIndex, outputIndex)
@@ -5647,8 +4917,7 @@ (async) prepar
- Prepares the bundled orders by getting the best deals from Router and sorting the
-bundled orders based on the best deals
+ Calls eval for a specific order to get its max output and ratio
@@ -5674,8 +4943,6 @@ Parameters:
- Default
-
Description
@@ -5686,13 +4953,13 @@ Parameters:
- bundledOrders
+ interpreter
-Array.<any>
+ethers.Contract
@@ -5701,25 +4968,21 @@ Parameters:
-
-
-
-
- The bundled orders array
+ The interpreter ethersjs contract instance with signer
- dataFetcher
+ arbAddress
-any
+string
@@ -5728,25 +4991,21 @@ Parameters:
-
-
-
-
- The DataFetcher instance
+ Arb contract address
- config
+ obAddress
-any
+string
@@ -5755,25 +5014,21 @@ Parameters:
-
-
-
-
- The network config data
+ OrderBook contract address
- gasPrice
+ order
-ethers.BigNumber
+object
@@ -5782,25 +5037,21 @@ Parameters:
-
-
-
-
- The network gas price
+ The order details fetched from sg
- sort
+ inputIndex
-boolean
+number
@@ -5809,14 +5060,31 @@ Parameters:
-
-
- true
+
+ The input token index
+
+
+
+
+
+
+ outputIndex
+
+
+
+
-
+number
+
+
+
+
+
- (optional) Sort based on best deals or not
+
+
+ The ouput token index
@@ -5857,7 +5125,7 @@ Parameters:
Source:
@@ -5882,6 +5150,16 @@ Parameters:
+Returns:
+
+
+
+ The ratio and maxOuput as BigNumber
+
+
+
+
+
@@ -5893,7 +5171,7 @@ Parameters:
- (async) prepare(quotes, bundledOrders, sort)
+ prepare(bundledOrders, availableSwaps, config, sort)
@@ -5901,7 +5179,7 @@ (async) prepar
- Prepares the bundled orders by getting the best deals from 0x and sorting the
+ Prepares the bundled orders by getting the best deals from Curve pools and sorting the
bundled orders based on the best deals
@@ -5928,8 +5206,6 @@ Parameters:
- Default
-
Description
@@ -5940,13 +5216,13 @@ Parameters:
- quotes
+ bundledOrders
-Array.<string>
+Array.<any>
@@ -5955,25 +5231,44 @@ Parameters:
-
+
+ The bundled orders array
+
+
+
+
+
+
+ availableSwaps
+
+
+
+
-
+Array.<any>
+
+
+
+
+
+
+
- The 0x request quote bodies
+ The available swaps from Curve specofied pools
- bundledOrders
+ config
-Array.<any>
+any
@@ -5982,12 +5277,31 @@ Parameters:
-
+
+ The network config data
+
+
+
+
+
+
+
+
+
+
+
-
+ethers.Signer
+
+
+
- The bundled orders array
+
+
+
+
+ The ethersjs signer
@@ -6009,12 +5323,6 @@ Parameters:
-
-
- true
-
-
-
(optional) Sort based on best deals or not
@@ -6057,7 +5365,7 @@ Parameters:
Source:
@@ -6093,7 +5401,7 @@ Parameters:
- processLps(liquidityProviders)
+ processLps(liquidityProviders, chainId)
@@ -6157,6 +5465,29 @@ Parameters:
+
+
+
+ chainId
+
+
+
+
+
+number
+
+
+
+
+
+
+
+
+
+ The chain id
+
+
+
@@ -6194,7 +5525,7 @@ Parameters:
Source:
@@ -6390,7 +5721,7 @@ Parameters:
Source:
@@ -6437,7 +5768,7 @@ Returns:
- (async) routerClear(config, ordersDetails, gasCoveragePercentage, prioritization)
+ (async) routerClear(config, ordersDetails, gasCoveragePercentage)
@@ -6564,35 +5895,6 @@ Parameters:
-
-
-
- prioritization
-
-
-
-
-
-boolean
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
- (optional) Prioritize better deals to get cleared first, default is true
-
-
-
@@ -6630,7 +5932,7 @@ Parameters:
Source:
@@ -6777,7 +6079,7 @@ Parameters:
Source:
@@ -6813,7 +6115,7 @@ Parameters:
- (async) srouterClear(config, ordersDetails, gasCoveragePercentage, prioritization)
+ (async) srouterClear(config, ordersDetails, gasCoveragePercentage)
@@ -6940,35 +6242,6 @@ Parameters:
-
-
-
- prioritization
-
-
-
-
-
-boolean
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
- (optional) Prioritize better deals to get cleared first, default is true
-
-
-
@@ -7006,7 +6279,7 @@ Parameters:
Source:
@@ -7176,7 +6449,7 @@ Parameters:
Source:
@@ -7323,7 +6596,7 @@ Parameters:
Source:
@@ -7506,7 +6779,7 @@ Parameters:
Source:
@@ -7542,7 +6815,7 @@ Parameters:
- (async) zeroExClear(config, ordersDetails, gasCoveragePercentage, prioritization)
+ (async) zeroExClear(config, ordersDetails, gasCoveragePercentage)
@@ -7668,35 +6941,6 @@ Parameters:
-
-
-
- prioritization
-
-
-
-
-
-boolean
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
- (optional) Prioritize better deals to get cleared first, default is true
-
-
-
@@ -7734,7 +6978,7 @@ Parameters:
Source:
@@ -7790,13 +7034,13 @@ Returns:
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/index.html b/docs/html/index.html
index b8f69248..8ba6fb79 100644
--- a/docs/html/index.html
+++ b/docs/html/index.html
@@ -50,13 +50,13 @@
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/index.js.html b/docs/html/index.js.html
index cc8d71da..18b2a18e 100644
--- a/docs/html/index.js.html
+++ b/docs/html/index.js.html
@@ -87,10 +87,10 @@ Source: index.js
* for it to be considered profitable and get submitted
*/
gasCoveragePercentage: "100",
- /**
- * Prioritize better deals to get cleared first, default is true
- */
- prioritization: true
+ // /**
+ // * Prioritize better deals to get cleared first, default is true
+ // */
+ // prioritization: true
};
/**
@@ -129,7 +129,8 @@ Source: index.js
query: getQuery(
sgFilters?.orderHash,
sgFilters?.orderOwner,
- sgFilters?.orderInterpreter
+ sgFilters?.orderInterpreter,
+ sgFilters?.shuffle
)
},
{ headers: { "Content-Type": "application/json" } }
@@ -223,7 +224,7 @@ Source: index.js
* @param {string} mode - The mode for clearing, either "0x" or "curve" or "router"
* @param {object} config - The configuration object
* @param {any[]} ordersDetails - The order details queried from subgraph
- * @param {clearOptions} options - The options for clear, 'slippage',' gasCoveragePercentage' and 'prioritization'
+ * @param {clearOptions} options - The options for clear, such as 'gasCoveragePercentage''
* @returns The report of details of cleared orders
*/
const clear = async(
@@ -235,9 +236,9 @@ Source: index.js
const _mode = mode.toLowerCase();
const version = versions.node;
const majorVersion = Number(version.slice(0, version.indexOf(".")));
- const prioritization = options.prioritization !== undefined
- ? options.prioritization
- : clearOptions.prioritization;
+ // const prioritization = options.prioritization !== undefined
+ // ? !!options.prioritization
+ // : clearOptions.prioritization;
const gasCoveragePercentage = options.gasCoveragePercentage !== undefined
? options.gasCoveragePercentage
: clearOptions.gasCoveragePercentage;
@@ -253,14 +254,14 @@ Source: index.js
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else if (_mode === "curve") {
if (majorVersion >= 18) return await curveClear(
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else throw `NodeJS v18 or higher is required for running the app in "curve" mode, current version: ${version}`;
}
@@ -269,7 +270,7 @@ Source: index.js
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else throw `NodeJS v18 or higher is required for running the app in "router" mode, current version: ${version}`;
}
@@ -278,7 +279,7 @@ Source: index.js
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else throw `NodeJS v18 or higher is required for running the app in "router" mode, current version: ${version}`;
}
@@ -299,13 +300,13 @@ Source: index.js
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/query.js.html b/docs/html/query.js.html
index d357fb02..99624a60 100644
--- a/docs/html/query.js.html
+++ b/docs/html/query.js.html
@@ -77,15 +77,36 @@ Source: query.js
* @param {string} orderHash - The order hash to apply as filter
* @param {string} owner - The order owner to apply as filter
* @param {string} interpreter - The interpreter to apply as filter
+ * @param {number} shuffle - (optional) A number in range of 0 - 3 that
+ * will change the order of getting order details from subgraph, mostly
+ * used for continuous bot running
* @returns the query string
*/
-const getQuery = (orderHash, owner, interpreter) => {
+const getQuery = (orderHash, owner, interpreter, shuffle = 0) => {
const orderHashFilter = orderHash ? `, id :"${orderHash.toLowerCase()}"` : "";
const ownerFilter = owner ? `, owner :"${owner.toLowerCase()}"` : "";
const interpreterFilter = interpreter ? `, interpreter :"${interpreter.toLowerCase()}"` : "";
+ let orderingProp, orderingDir;
+ const _turn = shuffle % 4;
+ if (_turn === 0) {
+ orderingProp = "id";
+ orderingDir = "asc";
+ }
+ if (_turn === 1) {
+ orderingProp = "id";
+ orderingDir = "desc";
+ }
+ if (_turn === 2) {
+ orderingProp = "timestamp";
+ orderingDir = "asc";
+ }
+ if (_turn === 3) {
+ orderingProp = "timestamp";
+ orderingDir = "desc";
+ }
return `{
orders(
- where: {orderActive: true${orderHashFilter}${ownerFilter}${interpreterFilter}}
+ orderBy: ${orderingProp}, orderDirection: ${orderingDir}, where: {orderActive: true${orderHashFilter}${ownerFilter}${interpreterFilter}}
) {
id
handleIO
@@ -140,13 +161,13 @@ Source: query.js
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/router.js.html b/docs/html/router.js.html
index 6b3ee032..2b0f08e0 100644
--- a/docs/html/router.js.html
+++ b/docs/html/router.js.html
@@ -42,82 +42,6 @@ Source: router.js
} = require("./utils");
-/**
- * Prepares the bundled orders by getting the best deals from Router and sorting the
- * bundled orders based on the best deals
- *
- * @param {any[]} bundledOrders - The bundled orders array
- * @param {any} dataFetcher - The DataFetcher instance
- * @param {any} config - The network config data
- * @param {ethers.BigNumber} gasPrice - The network gas price
- * @param {boolean} sort - (optional) Sort based on best deals or not
- */
-const prepare = async(bundledOrders, dataFetcher, config, gasPrice, sort = true) => {
- for (let i = 0; i < bundledOrders.length; i++) {
- const bOrder = bundledOrders[i];
- const pair = bOrder.buyTokenSymbol + "/" + bOrder.sellTokenSymbol;
- try {
- const fromToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.sellTokenDecimals,
- address: bOrder.sellToken,
- symbol: bOrder.sellTokenSymbol
- });
- const toToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.buyTokenDecimals,
- address: bOrder.buyToken,
- symbol: bOrder.buyTokenSymbol
- });
- await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
- const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken, toToken);
- const route = Router.findBestRoute(
- pcMap,
- config.chainId,
- fromToken,
- // cumulativeAmount,
- "1" + "0".repeat(bOrder.sellTokenDecimals),
- toToken,
- gasPrice.toNumber(),
- // providers,
- // poolFilter
- );
- if (route.status == "NoWay") throw "could not find any route for this token pair";
-
- // const rateFixed = route.amountOutBN.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- // const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
- const price = route.amountOutBN.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- bOrder.initPrice = price;
-
- console.log(`Current market price for ${pair} for: ${ethers.utils.formatEther(price)}`);
- console.log("Current ratio of the orders in this token pair:");
- bOrder.takeOrders.forEach(v => {
- console.log(ethers.utils.formatEther(v.ratio));
- });
- bOrder.takeOrders = bOrder.takeOrders.filter(
- v => price.gte(v.ratio)
- );
- console.log("\n");
- }
- catch(error) {
- console.log(`>>> could not get price for this ${pair} due to:`);
- console.log(error, "\n");
- }
- }
- console.log(
- ">>> Filtering bundled orders with lower ratio than current market price...",
- "\n"
- );
- bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
- return bundledOrders;
-};
-
/**
* Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with router contract
*
@@ -125,22 +49,19 @@ Source: router.js
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction
* for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const routerClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
- const lps = processLps(config.lps);
+ const lps = processLps(config.lps, config.chainId);
const dataFetcher = getDataFetcher(config, lps, !!config.usePublicRpc);
const signer = config.signer;
const arbAddress = config.arbAddress;
@@ -153,10 +74,10 @@ Source: router.js
// instantiating orderbook contract
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
- let gasPrice = await signer.provider.getGasPrice();
-
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32mROUTER\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
@@ -169,11 +90,6 @@ Source: router.js
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb);
- console.log(
- "------------------------- Getting Best Deals From RouteProcessor3 -------------------------",
- "\n"
- );
- bundledOrders = await prepare(bundledOrders, dataFetcher, config, gasPrice, prioritization);
}
else {
console.log("No orders found, exiting...", "\n");
@@ -185,15 +101,9 @@ Source: router.js
return;
}
- console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
-
const report = [];
for (let i = 0; i < bundledOrders.length; i++) {
try {
- gasPrice = await signer.provider.getGasPrice();
console.log(
`------------------------- Trying To Clear ${
bundledOrders[i].buyTokenSymbol
@@ -275,6 +185,7 @@ Source: router.js
await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken,toToken);
+ const gasPrice = await signer.provider.getGasPrice();
const route = Router.findBestRoute(
pcMap,
config.chainId,
@@ -292,7 +203,11 @@ Source: router.js
"1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
);
const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
- console.log(`Current best route price for this token pair: ${ethers.utils.formatEther(price)}`, "\n");
+ console.log(
+ "Current best route price for this token pair:",
+ `\x1b[33m${ethers.utils.formatEther(price)}\x1b[0m`,
+ "\n"
+ );
// filter take orders based on curent price and calculate final bundle quote amount
bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter(
@@ -329,11 +244,6 @@ Source: router.js
v => console.log("\x1b[36m%s\x1b[0m", v)
);
console.log("");
- // console.log(
- // "\x1b[36m%s\x1b[0m",
- // visualizeRoute(fromToken.address, toToken.address, route.legs),
- // "\n"
- // );
const rpParams = Router.routeProcessor2Params(
pcMap,
@@ -345,7 +255,6 @@ Source: router.js
// permits
// "0.005"
);
-
const takeOrdersConfigStruct = {
output: bundledOrders[i].buyToken,
input: bundledOrders[i].sellToken,
@@ -390,14 +299,15 @@ Source: router.js
);
if (arbType === "order-taker") takeOrdersConfigStruct.data = exchangeData;
- // console.log(">>> Estimating the profit for this token pair...", "\n");
- const ethPrice = await getEthPrice(
- config,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- gasPrice,
- dataFetcher
- );
+ const ethPrice = gasCoveragePercentage !== "0"
+ ? "0"
+ : await getEthPrice(
+ config,
+ bundledOrders[i].buyToken,
+ bundledOrders[i].buyTokenDecimals,
+ gasPrice,
+ dataFetcher
+ );
if (ethPrice === undefined) console.log("can not get ETH price, skipping...", "\n");
else {
const rawtx = {
@@ -419,31 +329,9 @@ Source: router.js
};
console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
let gasLimit = await signer.estimateGas(rawtx);
- gasLimit = gasLimit.mul("11").div("10");
+ gasLimit = gasLimit.mul("112").div("100");
rawtx.gasLimit = gasLimit;
const gasCost = gasLimit.mul(gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // ethers.utils.formatEther(bundledOrders[i].initPrice),
- // ethPrice,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
-
- // if (maxEstimatedProfit.isNegative()) console.log(
- // ">>> Skipping because estimated negative profit for this token pair",
- // "\n"
- // );
- // else {
- console.log(">>> Trying to submit the transaction for this token pair...", "\n");
const gasCostInToken = ethers.utils.parseUnits(
ethPrice
).mul(
@@ -453,28 +341,48 @@ Source: router.js
36 - bundledOrders[i].buyTokenDecimals
)
);
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- arbType === "order-taker"
- ? [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- : [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100),
- exchangeData
- ]
- );
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- const tx = 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"
- );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.15
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100"),
+ exchangeData
+ ]
+ );
+ await signer.estimateGas(rawtx);
+ }
try {
+ console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100"),
+ exchangeData
+ ]
+ );
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = 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 income = getIncome(signer, receipt);
const clearActualPrice = getActualPrice(
@@ -506,7 +414,7 @@ Source: router.js
);
console.log(
"\x1b[36m%s\x1b[0m",
- `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
+ `Clear Initial Price: ${ethers.utils.formatEther(price)}`
);
console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
@@ -543,14 +451,9 @@ Source: router.js
sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
clearedAmount: bundledQuoteAmount.toString(),
clearPrice: ethers.utils.formatEther(
- bundledOrders[i].initPrice
+ price
),
- // clearGuaranteedPrice: ethers.utils.formatUnits(
- // guaranteedAmount,
- // bundledOrders[i].buyTokenDecimals
- // ),
clearActualPrice,
- // maxEstimatedProfit,
gasUsed: receipt.gasUsed,
gasCost: actualGasCost,
income,
@@ -562,13 +465,17 @@ Source: router.js
console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
console.log(error, "\n");
}
- // }
}
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
- // reason, code, method, transaction, error, stack, message
+ if (error === "dryrun" || error === "nomatch") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction dry run failed, skipping...");
+ }
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ // reason, code, method, transaction, error, stack, message
+ }
}
}
}
@@ -593,13 +500,13 @@ Source: router.js
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/srouter.js.html b/docs/html/srouter.js.html
index 2e19ea7c..34638a9b 100644
--- a/docs/html/srouter.js.html
+++ b/docs/html/srouter.js.html
@@ -43,80 +43,6 @@ Source: srouter.js
} = require("./utils");
-/**
- * Prepares the bundled orders by getting the best deals from Router and sorting the
- * bundled orders based on the best deals
- *
- * @param {any[]} bundledOrders - The bundled orders array
- * @param {any} dataFetcher - The DataFetcher instance
- * @param {any} config - The network config data
- * @param {ethers.BigNumber} gasPrice - The network gas price
- * @param {boolean} sort - (optional) Sort based on best deals or not
- */
-const prepare = async(bundledOrders, dataFetcher, config, gasPrice, sort = true) => {
- for (let i = 0; i < bundledOrders.length; i++) {
- const bOrder = bundledOrders[i];
- const pair = bOrder.buyTokenSymbol + "/" + bOrder.sellTokenSymbol;
- try {
- const fromToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.sellTokenDecimals,
- address: bOrder.sellToken,
- symbol: bOrder.sellTokenSymbol
- });
- const toToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.buyTokenDecimals,
- address: bOrder.buyToken,
- symbol: bOrder.buyTokenSymbol
- });
- await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
- const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken, toToken);
- const route = Router.findBestRoute(
- pcMap,
- config.chainId,
- fromToken,
- // cumulativeAmount,
- "1" + "0".repeat(bOrder.sellTokenDecimals),
- toToken,
- gasPrice.toNumber(),
- // providers,
- // poolFilter
- );
- if (route.status == "NoWay") throw "could not find any route for this token pair";
-
- const price = route.amountOutBN.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- bOrder.initPrice = price;
-
- console.log(`Current market price for ${pair} for: ${ethers.utils.formatEther(price)}`);
- console.log("Current ratio of the orders in this token pair:");
- bOrder.takeOrders.forEach(v => {
- if (v.ratio) console.log(ethers.utils.formatEther(v.ratio));
- });
- bOrder.takeOrders = bOrder.takeOrders.filter(
- v => v.ratio !== undefined ? price.gte(v.ratio) : true
- );
- console.log("\n");
- }
- catch(error) {
- console.log(`>>> could not get price for this ${pair} due to:`);
- console.log(error, "\n");
- }
- }
- console.log(
- ">>> Filtering bundled orders with lower ratio than current market price...",
- "\n"
- );
- bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
- return bundledOrders;
-};
-
/**
* Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with specialized router contract
*
@@ -124,22 +50,19 @@ Source: srouter.js
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction
* for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const srouterClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
- const lps = processLps(config.lps);
+ const lps = processLps(config.lps, config.chainId);
const dataFetcher = getDataFetcher(config, lps, !!config.usePublicRpc);
const signer = config.signer;
const arbAddress = config.arbAddress;
@@ -153,10 +76,10 @@ Source: srouter.js
// instantiating orderbook contract
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
- let gasPrice = await signer.provider.getGasPrice();
-
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32mS-ROUTER\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
@@ -169,11 +92,6 @@ Source: srouter.js
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb, maxProfit);
- console.log(
- "------------------------- Getting Best Deals From RouteProcessor3 -------------------------",
- "\n"
- );
- bundledOrders = await prepare(bundledOrders, dataFetcher, config, gasPrice, prioritization);
}
else {
console.log("No orders found, exiting...", "\n");
@@ -185,15 +103,9 @@ Source: srouter.js
return;
}
- console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
-
const report = [];
for (let i = 0; i < bundledOrders.length; i++) {
try {
- gasPrice = await signer.provider.getGasPrice();
console.log(
`------------------------- Trying To Clear ${
bundledOrders[i].buyTokenSymbol
@@ -205,305 +117,322 @@ Source: srouter.js
console.log(`Buy Token Address: ${bundledOrders[i].buyToken}`);
console.log(`Sell Token Address: ${bundledOrders[i].sellToken}`, "\n");
- if (!bundledOrders[i].takeOrders.length) console.log(
- "All orders of this token pair have empty vault balance, skipping...",
- "\n"
- );
- else {
- const fromToken = new Token({
- chainId: config.chainId,
- decimals: bundledOrders[i].sellTokenDecimals,
- address: bundledOrders[i].sellToken,
- symbol: bundledOrders[i].sellTokenSymbol
- });
- const toToken = new Token({
- chainId: config.chainId,
- decimals: bundledOrders[i].buyTokenDecimals,
- address: bundledOrders[i].buyToken,
- symbol: bundledOrders[i].buyTokenSymbol
- });
-
- const obSellTokenBalance = ethers.BigNumber.from(await signer.call({
- data: "0x70a08231000000000000000000000000" + orderbookAddress.slice(2),
- to: bundledOrders[i].sellToken
- }));
- const quoteChunks = obSellTokenBalance.div("5");
- let ethPrice;
-
- for (let j = 5; j > 0; j--) {
- const maximumInput = j === 5 ? obSellTokenBalance : quoteChunks.mul(j);
- const maximumInputFixed = maximumInput.mul(
- "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals)
- );
+ if (!bundledOrders[i].takeOrders.length) throw "All orders of this token pair have empty vault balance, skipping...";
- console.log(`>>> Trying to arb with ${
+ const fromToken = new Token({
+ chainId: config.chainId,
+ decimals: bundledOrders[i].sellTokenDecimals,
+ address: bundledOrders[i].sellToken,
+ symbol: bundledOrders[i].sellTokenSymbol
+ });
+ const toToken = new Token({
+ chainId: config.chainId,
+ decimals: bundledOrders[i].buyTokenDecimals,
+ address: bundledOrders[i].buyToken,
+ symbol: bundledOrders[i].buyTokenSymbol
+ });
+
+ const obSellTokenBalance = ethers.BigNumber.from(await signer.call({
+ data: "0x70a08231000000000000000000000000" + orderbookAddress.slice(2),
+ to: bundledOrders[i].sellToken
+ }));
+ const quoteChunks = obSellTokenBalance.div("5");
+
+ if (obSellTokenBalance.isZero()) throw `Orderbook has no ${
+ bundledOrders[i].sellTokenSymbol
+ } balance, skipping...`;
+
+ let ethPrice;
+ const gasPrice = await signer.provider.getGasPrice();
+ try {
+ if (gasCoveragePercentage !== "0") ethPrice = await getEthPrice(
+ config,
+ bundledOrders[i].buyToken,
+ bundledOrders[i].buyTokenDecimals,
+ gasPrice,
+ dataFetcher
+ );
+ else ethPrice = "0";
+ if (ethPrice === undefined) throw "could not find a route for ETH price, skipping...";
+ }
+ catch {
+ throw "could not get ETH price, skipping...";
+ }
+ for (let j = 5; j > 0; j--) {
+ const maximumInput = j === 5 ? obSellTokenBalance : quoteChunks.mul(j);
+ const maximumInputFixed = maximumInput.mul(
+ "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals)
+ );
+
+ console.log(`>>> Trying to arb with ${
+ ethers.utils.formatEther(maximumInputFixed)
+ } ${
+ bundledOrders[i].sellTokenSymbol
+ } as maximum input`);
+ console.log(">>> Getting best route", "\n");
+
+ await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
+ const pcMap = dataFetcher.getCurrentPoolCodeMap(
+ fromToken,
+ toToken
+ );
+ const route = Router.findBestRoute(
+ pcMap,
+ config.chainId,
+ fromToken,
+ maximumInput,
+ toToken,
+ gasPrice.toNumber(),
+ // 30e9,
+ // providers,
+ // poolFilter
+ );
+ if (route.status == "NoWay") console.log(
+ "\x1b[31m%s\x1b[0m",
+ `could not find any route for this token pair for ${
ethers.utils.formatEther(maximumInputFixed)
} ${
bundledOrders[i].sellTokenSymbol
- } as maximum input`);
- console.log(">>> Getting best route", "\n");
- await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
- const pcMap = dataFetcher.getCurrentPoolCodeMap(
- fromToken,
- toToken
+ }, trying with a lower amount...`
+ );
+ else {
+ const rateFixed = route.amountOutBN.mul(
+ "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
+ );
+ const price = rateFixed.mul("1" + "0".repeat(18)).div(maximumInputFixed);
+ if (maxProfit) bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter(
+ v => v.ratio !== undefined ? price.mul("102").div("100").gte(v.ratio) : true
+ );
+ console.log(
+ "Current best route price for this token pair:",
+ `\x1b[33m${ethers.utils.formatEther(price)}\x1b[0m`,
+ "\n"
+ );
+ console.log(">>> Route portions: ", "\n");
+ visualizeRoute(fromToken, toToken, route.legs).forEach(
+ v => console.log("\x1b[36m%s\x1b[0m", v)
);
- const route = Router.findBestRoute(
+ console.log("");
+
+ const rpParams = Router.routeProcessor2Params(
pcMap,
- config.chainId,
+ route,
fromToken,
- maximumInput,
toToken,
- gasPrice.toNumber(),
- // 30e9,
- // providers,
- // poolFilter
- );
- if (route.status == "NoWay") console.log(
- "could not find any route for this token pair with this certain amount"
+ arb.address,
+ config.routeProcessor3Address,
+ // permits
+ // "0.005"
);
- else {
- const rateFixed = route.amountOutBN.mul(
- "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- );
- const price = rateFixed.mul("1" + "0".repeat(18)).div(maximumInputFixed);
- console.log(`Current best route price for this token pair: ${ethers.utils.formatEther(price)}`, "\n");
- console.log(">>> Route portions: ", "\n");
- visualizeRoute(fromToken, toToken, route.legs).forEach(
- v => console.log("\x1b[36m%s\x1b[0m", v)
- );
- console.log("");
-
- const rpParams = Router.routeProcessor2Params(
- pcMap,
- route,
- fromToken,
- toToken,
- arb.address,
- config.routeProcessor3Address,
- // permits
- // "0.005"
- );
- const takeOrdersConfigStruct = {
- minimumInput: ethers.constants.One,
- maximumInput,
- maximumIORatio: maxRatio ? ethers.constants.MaxUint256 : price,
- orders: bundledOrders[i].takeOrders.map(v => v.takeOrder),
- data: ethers.utils.defaultAbiCoder.encode(
- ["bytes"],
- [rpParams.routeCode]
- )
+ const takeOrdersConfigStruct = {
+ minimumInput: ethers.constants.One,
+ maximumInput,
+ maximumIORatio: maxRatio ? ethers.constants.MaxUint256 : price,
+ orders: bundledOrders[i].takeOrders.map(v => v.takeOrder),
+ data: ethers.utils.defaultAbiCoder.encode(
+ ["bytes"],
+ [rpParams.routeCode]
+ )
+ };
+
+ // building and submit the transaction
+ try {
+ const rawtx = {
+ data: arb.interface.encodeFunctionData("arb", [takeOrdersConfigStruct, "0"]),
+ to: arb.address,
+ gasPrice
};
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ let gasLimit;
+ try {
+ gasLimit = await signer.estimateGas(rawtx);
+ }
+ catch {
+ throw "nomatch";
+ }
+ gasLimit = gasLimit.mul("112").div("100");
+ rawtx.gasLimit = gasLimit;
+ const gasCost = gasLimit.mul(gasPrice);
+ const gasCostInToken = ethers.utils.parseUnits(
+ ethPrice
+ ).mul(
+ gasCost
+ ).div(
+ "1" + "0".repeat(
+ 36 - bundledOrders[i].buyTokenDecimals
+ )
+ );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.2
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ );
+ try {
+ await signer.estimateGas(rawtx);
+ }
+ catch {
+ throw "dryrun";
+ }
+ }
- // building and submit the transaction
+ // submit the tx only if dry runs with headroom is passed
try {
- if (ethPrice === undefined) ethPrice = await getEthPrice(
- config,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- gasPrice,
- dataFetcher
+ console.log(">>> Trying to submit the transaction...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
);
- if (ethPrice === undefined) console.log("can not get ETH price, skipping...", "\n");
- else {
- const rawtx = {
- data: arb.interface.encodeFunctionData("arb", [takeOrdersConfigStruct, "0"]),
- to: arb.address,
- gasPrice
- };
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- let gasLimit;
- try {
- gasLimit = await signer.estimateGas(rawtx);
- }
- catch {
- // console.log(err);
- throw "nomatch";
- }
- gasLimit = gasLimit.mul("11").div("10");
- rawtx.gasLimit = gasLimit;
- const gasCost = gasLimit.mul(gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // ethers.utils.formatEther(bundledOrders[i].initPrice),
- // ethPrice,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
-
- // if (maxEstimatedProfit.isNegative()) console.log(
- // ">>> Skipping because estimated negative profit for this token pair",
- // "\n"
- // );
- // else {
- console.log(">>> Trying to submit the transaction...", "\n");
- const gasCostInToken = ethers.utils.parseUnits(
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = 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();
+ if (receipt.status === 1) {
+ const clearActualAmount = getActualClearAmount(
+ arbAddress,
+ orderbookAddress,
+ receipt
+ );
+ const income = getIncome(signer, receipt);
+ const clearActualPrice = getActualPrice(
+ receipt,
+ orderbookAddress,
+ arbAddress,
+ clearActualAmount.mul("1" + "0".repeat(
+ 18 - bundledOrders[i].sellTokenDecimals
+ )),
+ bundledOrders[i].buyTokenDecimals
+ );
+ const actualGasCost = ethers.BigNumber.from(
+ receipt.effectiveGasPrice
+ ).mul(receipt.gasUsed);
+ const actualGasCostInToken = ethers.utils.parseUnits(
ethPrice
).mul(
- gasCost
+ actualGasCost
).div(
"1" + "0".repeat(
36 - bundledOrders[i].buyTokenDecimals
)
);
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- );
- const tx = await signer.sendTransaction(rawtx);
+ const netProfit = income
+ ? income.sub(actualGasCostInToken)
+ : undefined;
- 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"
+ "\x1b[36m%s\x1b[0m",
+ `Clear Initial Price: ${ethers.utils.formatEther(price)}`
);
-
- try {
- const receipt = await tx.wait();
- // console.log(receipt);
- if (receipt.status === 1) {
- const clearActualAmount = getActualClearAmount(
- arbAddress,
- orderbookAddress,
- receipt
- );
- const income = getIncome(signer, receipt);
- const clearActualPrice = getActualPrice(
- receipt,
- orderbookAddress,
- arbAddress,
- clearActualAmount.mul("1" + "0".repeat(
- 18 - bundledOrders[i].sellTokenDecimals
- )),
- bundledOrders[i].buyTokenDecimals
- );
- const actualGasCost = ethers.BigNumber.from(
- receipt.effectiveGasPrice
- ).mul(receipt.gasUsed);
- const actualGasCostInToken = ethers.utils.parseUnits(
- ethPrice
- ).mul(
- actualGasCost
- ).div(
- "1" + "0".repeat(
- 36 - bundledOrders[i].buyTokenDecimals
- )
- );
- const netProfit = income
- ? income.sub(actualGasCostInToken)
- : undefined;
-
- console.log(
- "\x1b[36m%s\x1b[0m",
- `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
- );
- console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
- console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
- ethers.utils.formatUnits(
- clearActualAmount,
- bundledOrders[i].sellTokenDecimals
- )
- } ${bundledOrders[i].sellTokenSymbol}`);
- console.log("\x1b[36m%s\x1b[0m", `Consumed Gas: ${
- ethers.utils.formatEther(actualGasCost)
- } ${
- config.nativeToken.symbol
- }`, "\n");
- if (income) {
- console.log("\x1b[35m%s\x1b[0m", `Gross Income: ${ethers.utils.formatUnits(
- income,
- bundledOrders[i].buyTokenDecimals
- )} ${bundledOrders[i].buyTokenSymbol}`);
- console.log("\x1b[35m%s\x1b[0m", `Net Profit: ${ethers.utils.formatUnits(
- netProfit,
- bundledOrders[i].buyTokenDecimals
- )} ${bundledOrders[i].buyTokenSymbol}`, "\n");
- }
-
- report.push({
- transactionHash: receipt.transactionHash,
- tokenPair:
- bundledOrders[i].buyTokenSymbol +
- "/" +
- bundledOrders[i].sellTokenSymbol,
- buyToken: bundledOrders[i].buyToken,
- buyTokenDecimals: bundledOrders[i].buyTokenDecimals,
- sellToken: bundledOrders[i].sellToken,
- sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
- clearedAmount: clearActualAmount.toString(),
- clearPrice: ethers.utils.formatEther(
- bundledOrders[i].initPrice
- ),
- clearActualPrice,
- // maxEstimatedProfit,
- gasUsed: receipt.gasUsed,
- gasCost: actualGasCost,
- income,
- netProfit,
- clearedOrders: bundledOrders[i].takeOrders.map(
- v => v.id
- ),
- });
- j = 0;
- }
- else if (j > 1) console.log(
- `could not clear with ${ethers.utils.formatEther(
- maximumInputFixed
- )} ${
- bundledOrders[i].sellTokenSymbol
- } as max input, trying with lower amount...`
- );
- else console.log("could not arb this pair");
- }
- catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
- console.log(error, "\n");
- if (j > 1) console.log(
- "\x1b[34m%s\x1b[0m",
- `could not clear with ${ethers.utils.formatEther(
- maximumInputFixed
- )} ${
- bundledOrders[i].sellTokenSymbol
- } as max input, trying with lower amount...`, "\n"
- );
- else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
+ console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
+ console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
+ ethers.utils.formatUnits(
+ clearActualAmount,
+ bundledOrders[i].sellTokenDecimals
+ )
+ } ${bundledOrders[i].sellTokenSymbol}`);
+ console.log("\x1b[36m%s\x1b[0m", `Consumed Gas: ${
+ ethers.utils.formatEther(actualGasCost)
+ } ${
+ config.nativeToken.symbol
+ }`, "\n");
+ if (income) {
+ console.log("\x1b[35m%s\x1b[0m", `Gross Income: ${ethers.utils.formatUnits(
+ income,
+ bundledOrders[i].buyTokenDecimals
+ )} ${bundledOrders[i].buyTokenSymbol}`);
+ console.log("\x1b[35m%s\x1b[0m", `Net Profit: ${ethers.utils.formatUnits(
+ netProfit,
+ bundledOrders[i].buyTokenDecimals
+ )} ${bundledOrders[i].buyTokenSymbol}`, "\n");
}
+
+ report.push({
+ transactionHash: receipt.transactionHash,
+ tokenPair:
+ bundledOrders[i].buyTokenSymbol +
+ "/" +
+ bundledOrders[i].sellTokenSymbol,
+ buyToken: bundledOrders[i].buyToken,
+ buyTokenDecimals: bundledOrders[i].buyTokenDecimals,
+ sellToken: bundledOrders[i].sellToken,
+ sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
+ clearedAmount: clearActualAmount.toString(),
+ clearPrice: ethers.utils.formatEther(price),
+ clearActualPrice,
+ gasUsed: receipt.gasUsed,
+ gasCost: actualGasCost,
+ income,
+ netProfit,
+ clearedOrders: bundledOrders[i].takeOrders.map(
+ v => v.id
+ ),
+ });
+ j = 0;
}
- }
- catch (error) {
- if (error !== "nomatch") {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
- // reason, code, method, transaction, error, stack, message
- }
- if (j > 1) console.log(
- "\x1b[34m%s\x1b[0m",
+ else if (j > 1) console.log(
`could not clear with ${ethers.utils.formatEther(
maximumInputFixed
)} ${
bundledOrders[i].sellTokenSymbol
- } as max input, trying with lower amount...`, "\n"
+ } as max input, trying with lower amount...`
);
- else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
+ else console.log("could not arb this pair");
}
+ catch (error) {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
+ console.log(error, "\n");
+ throw "failed-exec";
+ // if (j > 1) console.log(
+ // "\x1b[34m%s\x1b[0m",
+ // `could not clear with ${ethers.utils.formatEther(
+ // maximumInputFixed
+ // )} ${
+ // bundledOrders[i].sellTokenSymbol
+ // } as max input, trying with lower amount...`, "\n"
+ // );
+ // else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
+ }
+
+ }
+ catch (error) {
+ if (error !== "nomatch" && error !== "dryrun" && error !== "failed-exec") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ // reason, code, method, transaction, error, stack, message
+ }
+ if (error === "failed-exec") throw "Transaction execution failed, skipping this pair...";
+ if (j > 1) console.log(
+ "\x1b[34m%s\x1b[0m",
+ `could not clear with ${ethers.utils.formatEther(
+ maximumInputFixed
+ )} ${
+ bundledOrders[i].sellTokenSymbol
+ } as max input, trying with lower amount...`, "\n"
+ );
+ else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
}
}
}
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Something went wrong, reason:", "\n");
- console.log(error);
+ if (typeof error === "string") console.log("\x1b[31m%s\x1b[0m", error, "\n");
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Something went wrong, reason:", "\n");
+ console.log(error);
+ }
}
}
return report;
@@ -521,13 +450,13 @@ Source: srouter.js
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/utils.js.html b/docs/html/utils.js.html
index 9f2f4c2d..e7c718a4 100644
--- a/docs/html/utils.js.html
+++ b/docs/html/utils.js.html
@@ -36,11 +36,15 @@ Source: utils.js
/**
- * Fallback transports for viem client
+ * Chain specific fallback data
*/
-const fallbackTransports = {
+const fallbacks = {
[ChainId.ARBITRUM_NOVA]: {
transport: http("https://nova.arbitrum.io/rpc"),
+ liquidityProviders: [
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.ARBITRUM]: {
transport: [
@@ -53,30 +57,53 @@ Source: utils.js
http("https://arbitrum.blockpi.network/v1/rpc/public"),
http("https://arb-mainnet-public.unifra.io"),
],
+ liquidityProviders: [
+ "dfyn",
+ "elk",
+ "sushiswapv3",
+ "uniswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.AVALANCHE]: {
transport: [
http("https://api.avax.network/ext/bc/C/rpc"),
http("https://rpc.ankr.com/avalanche")
],
+ liquidityProviders: [
+ "elk",
+ "traderjoe",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.BOBA]: {
transport: [
http("https://mainnet.boba.network"),
http("https://lightning-replica.boba.network")
],
+ liquidityProviders: [
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.BOBA_AVAX]: {
transport: [
http("https://avax.boba.network"),
http("https://replica.avax.boba.network")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.BOBA_BNB]: {
transport: [
http("https://bnb.boba.network"),
http("https://replica.bnb.boba.network")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.BSC]: {
transport: [
@@ -86,12 +113,26 @@ Source: utils.js
http("https://bsc-dataseed1.binance.org"),
http("https://bsc-dataseed2.binance.org"),
],
+ liquidityProviders: [
+ "apeswap",
+ "biswap",
+ "elk",
+ "jetswap",
+ "pancakeswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv3"
+ ]
},
[ChainId.BTTC]: {
transport: http("https://rpc.bittorrentchain.io"),
},
[ChainId.CELO]: {
- transport: http("https://forno.celo.org")
+ transport: http("https://forno.celo.org"),
+ liquidityProviders: [
+ "ubeswap",
+ "sushiswapv2"
+ ]
},
[ChainId.ETHEREUM]: {
transport: [
@@ -104,6 +145,16 @@ Source: utils.js
http("https://1rpc.io/eth"),
http("https://ethereum.publicnode.com"),
http("https://cloudflare-eth.com"),
+ ],
+ liquidityProviders: [
+ "apeswap",
+ "curveswap",
+ "elk",
+ "pancakeswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv2",
+ "uniswapv3"
]
},
[ChainId.FANTOM]: {
@@ -112,33 +163,66 @@ Source: utils.js
http("https://rpc.fantom.network"),
http("https://rpc2.fantom.network"),
],
+ liquidityProviders: [
+ "dfyn",
+ "elk",
+ "jetswap",
+ "spookyswap",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.FUSE]: {
transport: http("https://rpc.fuse.io"),
+ liquidityProviders: [
+ "elk",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.GNOSIS]: {
transport: http("https://rpc.ankr.com/gnosis"),
+ liquidityProviders: [
+ "elk",
+ "honeyswap",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.HARMONY]: {
transport: [
http("https://api.harmony.one"),
http("https://rpc.ankr.com/harmony")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.KAVA]: {
transport: [
http("https://evm.kava.io"),
- http("https://evm2.kava.io")
+ http("https://evm2.kava.io"),
],
+ liquidityProviders: [
+ "elk"
+ ]
},
[ChainId.MOONBEAM]: {
transport: [
http("https://rpc.api.moonbeam.network"),
http("https://rpc.ankr.com/moonbeam")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.MOONRIVER]: {
transport: http("https://rpc.api.moonriver.moonbeam.network"),
+ liquidityProviders: [
+ "elk",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.OPTIMISM]: {
transport: [
@@ -149,6 +233,11 @@ Source: utils.js
http("https://optimism.blockpi.network/v1/rpc/public"),
http("https://mainnet.optimism.io"),
],
+ liquidityProviders: [
+ "elk",
+ "sushiswapv3",
+ "uniswapv3"
+ ]
},
[ChainId.POLYGON]: {
transport: [
@@ -164,6 +253,16 @@ Source: utils.js
http("https://rpc-mainnet.maticvigil.com"),
// ...polygon.rpcUrls.default.http.map((url) => http(url)),
],
+ liquidityProviders: [
+ "apeswap",
+ "dfyn",
+ "elk",
+ "jetswap",
+ "quickswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv3"
+ ]
},
[ChainId.POLYGON_ZKEVM]: {
transport: [
@@ -171,6 +270,10 @@ Source: utils.js
http("https://rpc.ankr.com/polygon_zkevm"),
http("https://rpc.polygon-zkevm.gateway.fm"),
],
+ liquidityProviders: [
+ "dovishv3",
+ "sushiswapv3"
+ ]
},
[ChainId.THUNDERCORE]: {
transport: [
@@ -178,6 +281,10 @@ Source: utils.js
http("https://mainnet-rpc.thundercore.io"),
http("https://mainnet-rpc.thundertoken.net"),
],
+ liquidityProviders: [
+ "laserswap",
+ "sushiswapv3"
+ ]
},
};
@@ -709,11 +816,11 @@ Source: utils.js
transport: config.rpc && config.rpc !== "test"
? useFallbacks
? fallback(
- [config.rpc, ...fallbackTransports[config.chainId].transport],
+ [config.rpc, ...fallbacks[config.chainId].transport],
{rank: true}
)
: http(config.rpc)
- : fallback(fallbackTransports[config.chainId].transport, {rank: true}),
+ : fallback(fallbacks[config.chainId].transport, {rank: true}),
// batch: {
// multicall: {
// batchSize: 512
@@ -832,8 +939,9 @@ Source: utils.js
* Resolves an array of case-insensitive names to LiquidityProviders, ignores the ones that are not valid
*
* @param {string[]} liquidityProviders - List of liquidity providers
+ * @param {number} chainId - The chain id
*/
-const processLps = (liquidityProviders) => {
+const processLps = (liquidityProviders, chainId) => {
if (
!liquidityProviders ||
!Array.isArray(liquidityProviders) ||
@@ -843,7 +951,12 @@ Source: utils.js
const _lps = [];
const LP = Object.values(LiquidityProviders);
for (let i = 0; i < liquidityProviders.length; i++) {
- const index = LP.findIndex(v => v.toLowerCase() === liquidityProviders[i].toLowerCase());
+ const index = LP.findIndex(
+ v => v.toLowerCase() === liquidityProviders[i].toLowerCase()
+ && !!fallbacks[chainId]?.liquidityProviders.includes(
+ liquidityProviders[i].toLowerCase()
+ )
+ );
if (index > -1 && !_lps.includes(LP[index])) _lps.push(LP[index]);
}
return _lps.length ? _lps : undefined;
@@ -1282,8 +1395,66 @@ Source: utils.js
);
};
+/**
+ * Builds initial 0x requests bodies from token addresses that is required
+ * for getting token prices with least amount of hits possible and that is
+ * to pair up tokens in a way that each show up only once in a request body
+ * so that the number of requests will be: "number-of-tokens / 2" at best or
+ * "(number-of-tokens / 2) + 1" at worst if the number of tokens is an odd digit.
+ * This way the responses will include the "rate" for sell/buy tokens to native
+ * network token which will be used to estimate the initial price of all possible
+ * token pair combinations.
+ *
+ * @param {string} api - The 0x API endpoint URL
+ * @param {any[]} queries - The array that keeps the 0x query text
+ * @param {string} tokenAddress - The token address
+ * @param {number} tokenDecimals - The token decimals
+ * @param {string} tokenSymbol - The token symbol
+ */
+const build0xQueries = (api, queries, tokenAddress, tokenDecimals, tokenSymbol) => {
+ tokenAddress = tokenAddress.toLowerCase();
+ if (queries.length === 0) queries.push([
+ tokenAddress,
+ tokenDecimals,
+ tokenSymbol
+ ]);
+ else if (!Array.isArray(queries[queries.length - 1])) {
+ if(!queries.find(v => v.quote.includes(tokenAddress))) queries.push([
+ tokenAddress,
+ tokenDecimals,
+ tokenSymbol
+ ]);
+ }
+ else {
+ if(
+ queries[queries.length - 1][0] !== tokenAddress &&
+ !queries.slice(0, -1).find(v => v.quote.includes(tokenAddress))
+ ) {
+ queries[queries.length - 1] = {
+ quote: `${
+ api
+ }swap/v1/price?buyToken=${
+ queries[queries.length - 1][0]
+ }&sellToken=${
+ tokenAddress
+ }&sellAmount=${
+ "1" + "0".repeat(tokenDecimals)
+ }`,
+ tokens: [
+ queries[queries.length - 1][2],
+ tokenSymbol,
+ queries[queries.length - 1][0],
+ tokenAddress,
+ queries[queries.length - 1][1],
+ tokenDecimals
+ ]
+ };
+ }
+ }
+};
+
module.exports = {
- fallbackTransports,
+ fallbacks,
bnFromFloat,
toFixed18,
fromFixed18,
@@ -1305,7 +1476,8 @@ Source: utils.js
promiseTimeout,
getActualClearAmount,
getRouteForTokens,
- visualizeRoute
+ visualizeRoute,
+ build0xQueries
};
@@ -1317,13 +1489,13 @@ Source: utils.js
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/docs/html/zeroex.js.html b/docs/html/zeroex.js.html
index 0910efba..df012dff 100644
--- a/docs/html/zeroex.js.html
+++ b/docs/html/zeroex.js.html
@@ -35,146 +35,23 @@ Source: zeroex.js
const HEADERS = { headers: { "accept-encoding": "null" } };
-/**
- * Builds initial 0x requests bodies from token addresses that is required
- * for getting token prices with least amount of hits possible and that is
- * to pair up tokens in a way that each show up only once in a request body
- * so that the number of requests will be: "number-of-tokens / 2" at best or
- * "(number-of-tokens / 2) + 1" at worst if the number of tokens is an odd digit.
- * This way the responses will include the "rate" for sell/buy tokens to native
- * network token which will be used to estimate the initial price of all possible
- * token pair combinations.
- *
- * @param {string} api - The 0x API endpoint URL
- * @param {any[]} quotes - The array that keeps the quotes
- * @param {string} tokenAddress - The token address
- * @param {number} tokenDecimals - The token decimals
- * @param {string} tokenSymbol - The token symbol
- */
-const initRequests = (api, quotes, tokenAddress, tokenDecimals, tokenSymbol) => {
- if (quotes.length === 0) quotes.push([
- tokenAddress,
- tokenDecimals,
- tokenSymbol
- ]);
- else if (!Array.isArray(quotes[quotes.length - 1])) {
- if(!quotes.find(v => v.quote.includes(tokenAddress))) quotes.push([
- tokenAddress,
- tokenDecimals,
- tokenSymbol
- ]);
- }
- else {
- if(
- quotes[quotes.length - 1][0] !== tokenAddress &&
- !quotes.slice(0, -1).find(v => v.quote.includes(tokenAddress))
- ) {
- quotes[quotes.length - 1] = {
- quote: `${
- api
- }swap/v1/price?buyToken=${
- quotes[quotes.length - 1][0]
- }&sellToken=${
- tokenAddress
- }&sellAmount=${
- "1" + "0".repeat(tokenDecimals)
- }`,
- tokens: [quotes[quotes.length - 1][2], tokenSymbol]
- };
- }
- }
-};
-
-/**
- * Prepares the bundled orders by getting the best deals from 0x and sorting the
- * bundled orders based on the best deals
- *
- * @param {string[]} quotes - The 0x request quote bodies
- * @param {any[]} bundledOrders - The bundled orders array
- * @param {boolean} sort - (optional) Sort based on best deals or not
- */
-const prepare = async(quotes, bundledOrders, sort = true) => {
- try {
- console.log(">>> Getting initial prices from 0x");
- const promises = [];
- for (let i = 0; i < quotes.length; i++) {
- promises.push(axios.get(quotes[i].quote, HEADERS));
- await sleep(1000);
- }
- const responses = await Promise.allSettled(promises);
-
- let prices = [];
- responses.forEach((v, i) => {
- if (v.status == "fulfilled") prices.push([
- {
- token: v.value.data.buyTokenAddress,
- rate: v.value.data.buyTokenToEthRate
- },
- {
- token: v.value.data.sellTokenAddress,
- rate: v.value.data.sellTokenToEthRate
- }
- ]);
- else {
- console.log(`Could not get prices for ${quotes[i].tokens[0]} and ${quotes[i].tokens[1]}, reason:`);
- console.log(v.reason.message);
- }
- });
- prices = prices.flat();
-
- bundledOrders.forEach(v => {
- console.log(`\nCalculating initial price for ${v.buyTokenSymbol}/${v.sellTokenSymbol} ...`);
- const sellTokenPrice = prices.find(
- e => e.token.toLowerCase() === v.sellToken.toLowerCase()
- )?.rate;
- const buyTokenPrice = prices.find(
- e => e.token.toLowerCase() === v.buyToken.toLowerCase()
- )?.rate;
- if (sellTokenPrice && buyTokenPrice) {
- v.initPrice = ethers.utils.parseUnits(buyTokenPrice)
- .mul(ethers.utils.parseUnits("1"))
- .div(ethers.utils.parseUnits(sellTokenPrice));
- console.log(`result: ${ethers.utils.formatEther(v.initPrice)}`);
- }
- else console.log("Could not calculate initial price for this token pair due to lack of required data!");
- });
- bundledOrders = bundledOrders.filter(v => v.initPrice !== undefined);
-
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
- return bundledOrders;
- }
- catch (error) {
- console.log("something went wrong during the process of getting initial prices!");
- console.log(error);
- return [];
- }
-};
-
/**
* Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with 0x
*
* @param {object} config - The configuration object
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const zeroExClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
let rateLimit;
if (config.monthlyRatelimit !== undefined) {
@@ -190,8 +67,8 @@ Source: zeroex.js
const proxyAddress = config.zeroEx.proxyAddress;
const arbAddress = config.arbAddress;
const orderbookAddress = config.orderbookAddress;
- const nativeToken = config.nativeToken;
const arbType = config.arbType;
+ // const nativeToken = config.nativeWrappedToken;
// set the api key in headers
if (config.apiKey) HEADERS.headers["0x-api-key"] = config.apiKey;
@@ -204,14 +81,16 @@ Source: zeroex.js
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32m0x\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
console.log("Arb Contract Address: " , arbAddress);
console.log("OrderBook Contract Address: " , orderbookAddress, "\n");
- const initQuotes = [];
+ // const initPriceQueries = [];
let bundledOrders = [];
if (ordersDetails.length) {
@@ -219,22 +98,22 @@ Source: zeroex.js
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb);
- for (let i = 0; i < bundledOrders.length; i++) {
- initRequests(
- api,
- initQuotes,
- bundledOrders[i].sellToken,
- bundledOrders[i].sellTokenDecimals,
- bundledOrders[i].sellTokenSymbol
- );
- initRequests(
- api,
- initQuotes,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- bundledOrders[i].buyTokenSymbol
- );
- }
+ // for (let i = 0; i < bundledOrders.length; i++) {
+ // build0xQueries(
+ // api,
+ // initPriceQueries,
+ // bundledOrders[i].sellToken,
+ // bundledOrders[i].sellTokenDecimals,
+ // bundledOrders[i].sellTokenSymbol
+ // );
+ // build0xQueries(
+ // api,
+ // initPriceQueries,
+ // bundledOrders[i].buyToken,
+ // bundledOrders[i].buyTokenDecimals,
+ // bundledOrders[i].buyTokenSymbol
+ // );
+ // }
}
else {
console.log("No orders found, exiting...", "\n");
@@ -246,36 +125,38 @@ Source: zeroex.js
return;
}
- console.log(
- "------------------------- Getting Best Deals From 0x -------------------------",
- "\n"
- );
- if (Array.isArray(initQuotes[initQuotes.length - 1])) {
- initQuotes[initQuotes.length - 1] = {
- quote: `${
- api
- }swap/v1/price?buyToken=${
- nativeToken.address.toLowerCase()
- }&sellToken=${
- initQuotes[initQuotes.length - 1][0]
- }&sellAmount=${
- "1" + "0".repeat(initQuotes[initQuotes.length - 1][1])
- }`,
- tokens: ["ETH", initQuotes[initQuotes.length - 1][2]]
- };
- }
- hits += initQuotes.length;
- bundledOrders = await prepare(
- initQuotes,
- bundledOrders,
- prioritization
- );
-
- if (bundledOrders.length) console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
- else {
+ // console.log(
+ // "------------------------- Getting Best Deals From 0x -------------------------",
+ // "\n"
+ // );
+ // if (Array.isArray(initPriceQueries[initPriceQueries.length - 1])) {
+ // initPriceQueries[initPriceQueries.length - 1] = {
+ // quote: `${
+ // api
+ // }swap/v1/price?buyToken=${
+ // nativeToken.address.toLowerCase()
+ // }&sellToken=${
+ // initPriceQueries[initPriceQueries.length - 1][0]
+ // }&sellAmount=${
+ // "1" + "0".repeat(initPriceQueries[initPriceQueries.length - 1][1])
+ // }`,
+ // tokens: [
+ // nativeToken.symbol,
+ // initPriceQueries[initPriceQueries.length - 1][2],
+ // nativeToken.address.toLowerCase(),
+ // initPriceQueries[initPriceQueries.length - 1][0],
+ // nativeToken.decimals,
+ // initPriceQueries[initPriceQueries.length - 1][1],
+ // ]
+ // };
+ // }
+ // hits += initPriceQueries.length;
+ // bundledOrders = await prepare(
+ // initPriceQueries,
+ // bundledOrders
+ // );
+
+ if (bundledOrders.length === 0) {
console.log("Could not find any order to clear for current market price, exiting...", "\n");
return;
}
@@ -383,7 +264,6 @@ Source: zeroex.js
);
if (bundledOrders[i].takeOrders.length) {
-
cumulativeAmount = ethers.constants.Zero;
bundledOrders[i].takeOrders.forEach(v => {
cumulativeAmount = cumulativeAmount.add(v.quoteAmount);
@@ -422,10 +302,9 @@ Source: zeroex.js
orders: bundledOrders[i].takeOrders.map(v => v.takeOrder),
};
if (/^flash-loan-v3$|^order-taker$/.test(arbType)) {
- takeOrdersConfigStruct.data = "0x00";
+ takeOrdersConfigStruct.data = "0x";
delete takeOrdersConfigStruct.output;
delete takeOrdersConfigStruct.input;
- if (arbType === "flash-loan-v3") takeOrdersConfigStruct.data = "0x";
}
// submit the transaction
@@ -435,8 +314,6 @@ Source: zeroex.js
[txQuote.allowanceTarget, proxyAddress, txQuote.data]
);
if (arbType === "order-taker") takeOrdersConfigStruct.data = exchangeData;
-
- // console.log(">>> Estimating the profit for this token pair...", "\n");
const rawtx = {
data: arb.interface.encodeFunctionData(
"arb",
@@ -456,43 +333,9 @@ Source: zeroex.js
};
console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
let gasLimit = await signer.estimateGas(rawtx);
- gasLimit = gasLimit.mul("11").div("10");
+ gasLimit = gasLimit.mul("112").div("100");
rawtx.gasLimit = gasLimit;
- const gasCost = gasLimit.mul(gasPrice);
-
- // let gasLimit;
- // console.log("Block Number: " + await signer.provider.getBlockNumber());
- // if (arbType === "order-taker") gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // ethers.constants.Zero,
- // { gasPrice: txQuote.gasPrice }
- // );
- // else gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // ethers.constants.Zero,
- // exchangeData,
- // { gasPrice: txQuote.gasPrice }
- // );
-
- // gasLimit = gasLimit.mul("11").div("10");
- // const gasCost = gasLimit.mul(txQuote.gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // txQuote.price,
- // txQuote.buyTokenToEthRate,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
- // if (!maxEstimatedProfit.isNegative()) {
- console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ const gasCost = gasLimit.mul(txQuote.gasPrice);
const gasCostInToken = ethers.utils.parseUnits(
txQuote.buyTokenToEthRate
).mul(
@@ -502,31 +345,50 @@ Source: zeroex.js
36 - bundledOrders[i].buyTokenDecimals
)
);
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- arbType === "order-taker"
- ? [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- : [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100),
- exchangeData
- ]
- );
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- const tx = 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"
- );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.2
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100"),
+ exchangeData
+ ]
+ );
+ await signer.estimateGas(rawtx);
+ }
try {
+ console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100"),
+ exchangeData
+ ]
+ );
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = 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();
- // console.log(receipt);
const income = getIncome(signer, receipt);
const clearActualPrice = getActualPrice(
receipt,
@@ -593,7 +455,6 @@ Source: zeroex.js
clearPrice: txQuote.price,
clearGuaranteedPrice: txQuote.guaranteedPrice,
clearActualPrice,
- // maxEstimatedProfit,
gasUsed: receipt.gasUsed,
gasCost: actualGasCost,
income,
@@ -607,12 +468,15 @@ Source: zeroex.js
console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
console.log(error, "\n");
}
- // }
- // else console.log(">>> Skipping because estimated negative profit for this token pair", "\n");
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
+ if (error === "dryrun" || error === "nomatch") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction dry run failed, skipping...");
+ }
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ }
}
}
else console.log("\x1b[31m%s\x1b[0m", "Failed to get quote from 0x", "\n");
@@ -661,13 +525,13 @@ Source: zeroex.js
- Global
+ Global
- Documentation generated by JSDoc 4.0.2 on Thu Sep 14 2023 13:47:17 GMT+0000 (Coordinated Universal Time)
+ Documentation generated by JSDoc 4.0.2 on Sat Sep 16 2023 00:48:16 GMT+0000 (Coordinated Universal Time)
diff --git a/src/curve.js b/src/curve.js
index f96e49ab..95408dbb 100644
--- a/src/curve.js
+++ b/src/curve.js
@@ -6,7 +6,6 @@ const {
getEthPrice,
getDataFetcher,
getActualPrice,
- // estimateProfit,
bundleTakeOrders
} = require("./utils");
@@ -127,7 +126,7 @@ const getAvailableSwaps = (config) => {
* @param {ethers.Signer} - The ethersjs signer
* @param {boolean} sort - (optional) Sort based on best deals or not
*/
-const prepare = async(bundledOrders, availableSwaps, config, signer, sort = true) => {
+const prepare = (bundledOrders, availableSwaps, config, signer) => {
for (let i = 0; i < bundledOrders.length; i++) {
let pairFormat;
const bOrder = bundledOrders[i];
@@ -159,61 +158,62 @@ const prepare = async(bundledOrders, availableSwaps, config, signer, sort = true
: pool.underlyingCoinsUnwrapped.findIndex(
v => v.symbol === bOrder.sellTokenSymbol
);
- try {
- let rate;
- // let cumulativeAmountFixed = ethers.constants.Zero;
- // bOrder.takeOrders.forEach(v => {
- // cumulativeAmountFixed = cumulativeAmountFixed.add(v.quoteAmount);
- // });
- // const cumulativeAmount = cumulativeAmountFixed.div("1" + "0".repeat(18 - bOrder.sellTokenDecimals));
- if (pairFormat === "c") {
- rate = await bOrder.poolContract.get_dy(
- bOrder.sellTokenIndex,
- bOrder.buyTokenIndex,
- // cumulativeAmount
- "1" + "0".repeat(bOrder.sellTokenDecimals)
- );
- }
- else {
- rate = await bOrder.poolContract.get_dy_underlying(
- bOrder.sellTokenIndex,
- bOrder.buyTokenIndex,
- // cumulativeAmount
- "1" + "0".repeat(bOrder.sellTokenDecimals)
- );
- }
- // const rateFixed = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- // const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
- const price = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- bOrder.initPrice = price;
+ // try {
+ // let rate;
+ // // let cumulativeAmountFixed = ethers.constants.Zero;
+ // // bOrder.takeOrders.forEach(v => {
+ // // cumulativeAmountFixed = cumulativeAmountFixed.add(v.quoteAmount);
+ // // });
+ // // const cumulativeAmount = cumulativeAmountFixed.div("1" + "0".repeat(18 - bOrder.sellTokenDecimals));
+ // if (pairFormat === "c") {
+ // rate = await bOrder.poolContract.get_dy(
+ // bOrder.sellTokenIndex,
+ // bOrder.buyTokenIndex,
+ // // cumulativeAmount
+ // "1" + "0".repeat(bOrder.sellTokenDecimals)
+ // );
+ // }
+ // else {
+ // rate = await bOrder.poolContract.get_dy_underlying(
+ // bOrder.sellTokenIndex,
+ // bOrder.buyTokenIndex,
+ // // cumulativeAmount
+ // "1" + "0".repeat(bOrder.sellTokenDecimals)
+ // );
+ // }
+ // // const rateFixed = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
+ // // const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
+ // const price = rate.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
+ // bOrder.initPrice = price;
- console.log(`Current market price for ${pair}: ${ethers.utils.formatEther(price)}`);
- console.log("Current ratio of the orders in this token pair:");
- bOrder.takeOrders.forEach(v => {
- console.log(ethers.utils.formatEther(v.ratio));
- });
- bOrder.takeOrders = bOrder.takeOrders.filter(
- v => price.gte(v.ratio)
- );
- console.log("\n");
- }
- catch(error) {
- console.log(`>>> could not get price for this ${pair} due to:`);
- console.log(error);
- }
+ // console.log(`Current market price for ${pair}: ${ethers.utils.formatEther(price)}`);
+ // console.log("Current ratio of the orders in this token pair:");
+ // bOrder.takeOrders.forEach(v => {
+ // console.log(ethers.utils.formatEther(v.ratio));
+ // });
+ // bOrder.takeOrders = bOrder.takeOrders.filter(
+ // v => price.gte(v.ratio)
+ // );
+ // console.log("\n");
+ // }
+ // catch(error) {
+ // console.log(`>>> could not get price for this ${pair} due to:`);
+ // console.log(error);
+ // }
}
}
- console.log(
- ">>> Filtering bundled orders with lower ratio than current market price...",
- "\n"
- );
- bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
+ // console.log(
+ // ">>> Filtering bundled orders with lower ratio than current market price...",
+ // "\n"
+ // );
+ // bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
+ // if (sort) {
+ // console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
+ // bundledOrders.sort(
+ // (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
+ // );
+ // }
+ bundledOrders = bundledOrders.filter(v => v.poolIndex !== undefined);
return bundledOrders;
};
@@ -224,20 +224,17 @@ const prepare = async(bundledOrders, availableSwaps, config, signer, sort = true
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction
* for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const curveClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
const signer = config.signer;
const arbAddress = config.arbAddress;
@@ -250,10 +247,10 @@ const curveClear = async(
// instantiating orderbook contract
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
- let gasPrice = await signer.provider.getGasPrice();
-
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32mCURVE.FI\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
@@ -266,17 +263,12 @@ const curveClear = async(
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb);
- console.log(
- "------------------------- Getting Best Deals From Curve -------------------------",
- "\n"
- );
const availableSwaps = getAvailableSwaps(config);
- bundledOrders = await prepare(
+ bundledOrders = prepare(
bundledOrders,
availableSwaps,
config,
- signer,
- prioritization
+ signer
);
}
else {
@@ -289,16 +281,14 @@ const curveClear = async(
return;
}
- console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
-
const report = [];
- const dataFetcher = getDataFetcher(config, processLps(config.lps), !!config.usePublicRpc);
+ const dataFetcher = getDataFetcher(
+ config,
+ processLps(config.lps, config.chainId),
+ !!config.usePublicRpc
+ );
for (let i = 0; i < bundledOrders.length; i++) {
try {
- gasPrice = await signer.provider.getGasPrice();
console.log(
`------------------------- Trying To Clear ${
bundledOrders[i].buyTokenSymbol
@@ -448,14 +438,16 @@ const curveClear = async(
}
if (arbType === "order-taker") takeOrdersConfigStruct.data = exchangeData;
- // console.log(">>> Estimating the profit for this token pair...", "\n");
- const ethPrice = await getEthPrice(
- config,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- gasPrice,
- dataFetcher
- );
+ const gasPrice = await signer.provider.getGasPrice();
+ const ethPrice = gasCoveragePercentage !== "0"
+ ? "0"
+ : await getEthPrice(
+ config,
+ bundledOrders[i].buyToken,
+ bundledOrders[i].buyTokenDecimals,
+ gasPrice,
+ dataFetcher
+ );
if (ethPrice === undefined) console.log("can not get ETH price, skipping...", "\n");
else {
const rawtx = {
@@ -477,48 +469,9 @@ const curveClear = async(
};
console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
let gasLimit = await signer.estimateGas(rawtx);
- gasLimit = gasLimit.mul("11").div("10");
+ gasLimit = gasLimit.mul("112").div("100");
rawtx.gasLimit = gasLimit;
const gasCost = gasLimit.mul(gasPrice);
- // let gasLimit;
- // console.log("Block Number: " + await signer.provider.getBlockNumber());
- // if (arbType === "order-taker") gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // // set to zero for estimation
- // ethers.constants.Zero,
- // { gasPrice }
- // );
- // else gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // // set to zero for estimation
- // ethers.constants.Zero,
- // data,
- // { gasPrice }
- // );
- // gasLimit = gasLimit.mul("11").div("10");
- // const gasCost = gasLimit.mul(gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // ethers.utils.formatEther(bundledOrders[i].initPrice),
- // ethPrice,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
-
- // if (maxEstimatedProfit.isNegative()) console.log(
- // ">>> Skipping because estimated negative profit for this token pair",
- // "\n"
- // );
- // else {
- console.log(">>> Trying to submit the transaction for this token pair...", "\n");
const gasCostInToken = ethers.utils.parseUnits(
ethPrice
).mul(
@@ -528,43 +481,50 @@ const curveClear = async(
36 - bundledOrders[i].buyTokenDecimals
)
);
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- arbType === "order-taker"
- ? [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- : [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100),
- exchangeData
- ]
- );
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- const tx = await signer.sendTransaction(rawtx);
- // if (arbType === "order-taker") tx = await arb.arb(
- // takeOrdersConfigStruct,
- // // set to zero because only profitable transactions are submitted
- // gasCostInToken.mul(gasCoveragePercentage).div(100),
- // { gasPrice, gasLimit }
- // );
- // else tx = await arb.arb(
- // takeOrdersConfigStruct,
- // // set to zero because only profitable transactions are submitted
- // gasCostInToken.mul(gasCoveragePercentage).div(100),
- // data,
- // { gasPrice, gasLimit }
- // );
- 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"
- );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.15
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100"),
+ exchangeData
+ ]
+ );
+ await signer.estimateGas(rawtx);
+ }
try {
+ console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100"),
+ exchangeData
+ ]
+ );
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = 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();
- // console.log(receipt);
const income = getIncome(signer, receipt);
const clearActualPrice = getActualPrice(
receipt,
@@ -593,10 +553,10 @@ const curveClear = async(
`${bundledOrders[i].takeOrders.length} orders cleared successfully of this token pair!`,
"\n"
);
- console.log(
- "\x1b[36m%s\x1b[0m",
- `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
- );
+ // console.log(
+ // "\x1b[36m%s\x1b[0m",
+ // `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
+ // );
console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
ethers.utils.formatUnits(
@@ -631,15 +591,10 @@ const curveClear = async(
sellToken: bundledOrders[i].sellToken,
sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
clearedAmount: bundledQuoteAmount.toString(),
- clearPrice: ethers.utils.formatEther(
- bundledOrders[i].initPrice
- ),
- // clearGuaranteedPrice: ethers.utils.formatUnits(
- // guaranteedAmount,
- // bundledOrders[i].buyTokenDecimals
+ // clearPrice: ethers.utils.formatEther(
+ // bundledOrders[i].initPrice
// ),
clearActualPrice,
- // maxEstimatedProfit,
gasUsed: receipt.gasUsed,
gasCost: actualGasCost,
income,
@@ -651,12 +606,16 @@ const curveClear = async(
console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
console.log(error, "\n");
}
- // }
}
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
+ if (error === "dryrun" || error === "nomatch") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction dry run failed, skipping...");
+ }
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ }
}
}
}
diff --git a/src/index.js b/src/index.js
index c7e2ee01..a26af1d9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -59,10 +59,10 @@ const clearOptions = {
* for it to be considered profitable and get submitted
*/
gasCoveragePercentage: "100",
- /**
- * Prioritize better deals to get cleared first, default is true
- */
- prioritization: true
+ // /**
+ // * Prioritize better deals to get cleared first, default is true
+ // */
+ // prioritization: true
};
/**
@@ -101,7 +101,8 @@ const getOrderDetails = async(sgs, json, signer, sgFilters) => {
query: getQuery(
sgFilters?.orderHash,
sgFilters?.orderOwner,
- sgFilters?.orderInterpreter
+ sgFilters?.orderInterpreter,
+ sgFilters?.shuffle
)
},
{ headers: { "Content-Type": "application/json" } }
@@ -195,7 +196,7 @@ const getConfig = async(
* @param {string} mode - The mode for clearing, either "0x" or "curve" or "router"
* @param {object} config - The configuration object
* @param {any[]} ordersDetails - The order details queried from subgraph
- * @param {clearOptions} options - The options for clear, 'slippage',' gasCoveragePercentage' and 'prioritization'
+ * @param {clearOptions} options - The options for clear, such as 'gasCoveragePercentage''
* @returns The report of details of cleared orders
*/
const clear = async(
@@ -207,9 +208,9 @@ const clear = async(
const _mode = mode.toLowerCase();
const version = versions.node;
const majorVersion = Number(version.slice(0, version.indexOf(".")));
- const prioritization = options.prioritization !== undefined
- ? options.prioritization
- : clearOptions.prioritization;
+ // const prioritization = options.prioritization !== undefined
+ // ? !!options.prioritization
+ // : clearOptions.prioritization;
const gasCoveragePercentage = options.gasCoveragePercentage !== undefined
? options.gasCoveragePercentage
: clearOptions.gasCoveragePercentage;
@@ -225,14 +226,14 @@ const clear = async(
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else if (_mode === "curve") {
if (majorVersion >= 18) return await curveClear(
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else throw `NodeJS v18 or higher is required for running the app in "curve" mode, current version: ${version}`;
}
@@ -241,7 +242,7 @@ const clear = async(
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else throw `NodeJS v18 or higher is required for running the app in "router" mode, current version: ${version}`;
}
@@ -250,7 +251,7 @@ const clear = async(
config,
ordersDetails,
gasCoveragePercentage,
- prioritization
+ // prioritization
);
else throw `NodeJS v18 or higher is required for running the app in "router" mode, current version: ${version}`;
}
diff --git a/src/query.js b/src/query.js
index b39e28aa..e35d3170 100644
--- a/src/query.js
+++ b/src/query.js
@@ -49,15 +49,36 @@ const DefaultQuery = `{
* @param {string} orderHash - The order hash to apply as filter
* @param {string} owner - The order owner to apply as filter
* @param {string} interpreter - The interpreter to apply as filter
+ * @param {number} shuffle - (optional) A number in range of 0 - 3 that
+ * will change the order of getting order details from subgraph, mostly
+ * used for continuous bot running
* @returns the query string
*/
-const getQuery = (orderHash, owner, interpreter) => {
+const getQuery = (orderHash, owner, interpreter, shuffle = 0) => {
const orderHashFilter = orderHash ? `, id :"${orderHash.toLowerCase()}"` : "";
const ownerFilter = owner ? `, owner :"${owner.toLowerCase()}"` : "";
const interpreterFilter = interpreter ? `, interpreter :"${interpreter.toLowerCase()}"` : "";
+ let orderingProp, orderingDir;
+ const _turn = shuffle % 4;
+ if (_turn === 0) {
+ orderingProp = "id";
+ orderingDir = "asc";
+ }
+ if (_turn === 1) {
+ orderingProp = "id";
+ orderingDir = "desc";
+ }
+ if (_turn === 2) {
+ orderingProp = "timestamp";
+ orderingDir = "asc";
+ }
+ if (_turn === 3) {
+ orderingProp = "timestamp";
+ orderingDir = "desc";
+ }
return `{
orders(
- where: {orderActive: true${orderHashFilter}${ownerFilter}${interpreterFilter}}
+ orderBy: ${orderingProp}, orderDirection: ${orderingDir}, where: {orderActive: true${orderHashFilter}${ownerFilter}${interpreterFilter}}
) {
id
handleIO
diff --git a/src/router.js b/src/router.js
index c68e21ef..6c9b3dcb 100644
--- a/src/router.js
+++ b/src/router.js
@@ -14,82 +14,6 @@ const {
} = require("./utils");
-/**
- * Prepares the bundled orders by getting the best deals from Router and sorting the
- * bundled orders based on the best deals
- *
- * @param {any[]} bundledOrders - The bundled orders array
- * @param {any} dataFetcher - The DataFetcher instance
- * @param {any} config - The network config data
- * @param {ethers.BigNumber} gasPrice - The network gas price
- * @param {boolean} sort - (optional) Sort based on best deals or not
- */
-const prepare = async(bundledOrders, dataFetcher, config, gasPrice, sort = true) => {
- for (let i = 0; i < bundledOrders.length; i++) {
- const bOrder = bundledOrders[i];
- const pair = bOrder.buyTokenSymbol + "/" + bOrder.sellTokenSymbol;
- try {
- const fromToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.sellTokenDecimals,
- address: bOrder.sellToken,
- symbol: bOrder.sellTokenSymbol
- });
- const toToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.buyTokenDecimals,
- address: bOrder.buyToken,
- symbol: bOrder.buyTokenSymbol
- });
- await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
- const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken, toToken);
- const route = Router.findBestRoute(
- pcMap,
- config.chainId,
- fromToken,
- // cumulativeAmount,
- "1" + "0".repeat(bOrder.sellTokenDecimals),
- toToken,
- gasPrice.toNumber(),
- // providers,
- // poolFilter
- );
- if (route.status == "NoWay") throw "could not find any route for this token pair";
-
- // const rateFixed = route.amountOutBN.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- // const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
- const price = route.amountOutBN.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- bOrder.initPrice = price;
-
- console.log(`Current market price for ${pair} for: ${ethers.utils.formatEther(price)}`);
- console.log("Current ratio of the orders in this token pair:");
- bOrder.takeOrders.forEach(v => {
- console.log(ethers.utils.formatEther(v.ratio));
- });
- bOrder.takeOrders = bOrder.takeOrders.filter(
- v => price.gte(v.ratio)
- );
- console.log("\n");
- }
- catch(error) {
- console.log(`>>> could not get price for this ${pair} due to:`);
- console.log(error, "\n");
- }
- }
- console.log(
- ">>> Filtering bundled orders with lower ratio than current market price...",
- "\n"
- );
- bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
- return bundledOrders;
-};
-
/**
* Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with router contract
*
@@ -97,22 +21,19 @@ const prepare = async(bundledOrders, dataFetcher, config, gasPrice, sort = true)
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction
* for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const routerClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
- const lps = processLps(config.lps);
+ const lps = processLps(config.lps, config.chainId);
const dataFetcher = getDataFetcher(config, lps, !!config.usePublicRpc);
const signer = config.signer;
const arbAddress = config.arbAddress;
@@ -125,10 +46,10 @@ const routerClear = async(
// instantiating orderbook contract
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
- let gasPrice = await signer.provider.getGasPrice();
-
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32mROUTER\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
@@ -141,11 +62,6 @@ const routerClear = async(
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb);
- console.log(
- "------------------------- Getting Best Deals From RouteProcessor3 -------------------------",
- "\n"
- );
- bundledOrders = await prepare(bundledOrders, dataFetcher, config, gasPrice, prioritization);
}
else {
console.log("No orders found, exiting...", "\n");
@@ -157,15 +73,9 @@ const routerClear = async(
return;
}
- console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
-
const report = [];
for (let i = 0; i < bundledOrders.length; i++) {
try {
- gasPrice = await signer.provider.getGasPrice();
console.log(
`------------------------- Trying To Clear ${
bundledOrders[i].buyTokenSymbol
@@ -247,6 +157,7 @@ const routerClear = async(
await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken,toToken);
+ const gasPrice = await signer.provider.getGasPrice();
const route = Router.findBestRoute(
pcMap,
config.chainId,
@@ -264,7 +175,11 @@ const routerClear = async(
"1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
);
const price = rateFixed.mul("1" + "0".repeat(18)).div(cumulativeAmountFixed);
- console.log(`Current best route price for this token pair: ${ethers.utils.formatEther(price)}`, "\n");
+ console.log(
+ "Current best route price for this token pair:",
+ `\x1b[33m${ethers.utils.formatEther(price)}\x1b[0m`,
+ "\n"
+ );
// filter take orders based on curent price and calculate final bundle quote amount
bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter(
@@ -301,11 +216,6 @@ const routerClear = async(
v => console.log("\x1b[36m%s\x1b[0m", v)
);
console.log("");
- // console.log(
- // "\x1b[36m%s\x1b[0m",
- // visualizeRoute(fromToken.address, toToken.address, route.legs),
- // "\n"
- // );
const rpParams = Router.routeProcessor2Params(
pcMap,
@@ -317,7 +227,6 @@ const routerClear = async(
// permits
// "0.005"
);
-
const takeOrdersConfigStruct = {
output: bundledOrders[i].buyToken,
input: bundledOrders[i].sellToken,
@@ -362,14 +271,15 @@ const routerClear = async(
);
if (arbType === "order-taker") takeOrdersConfigStruct.data = exchangeData;
- // console.log(">>> Estimating the profit for this token pair...", "\n");
- const ethPrice = await getEthPrice(
- config,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- gasPrice,
- dataFetcher
- );
+ const ethPrice = gasCoveragePercentage !== "0"
+ ? "0"
+ : await getEthPrice(
+ config,
+ bundledOrders[i].buyToken,
+ bundledOrders[i].buyTokenDecimals,
+ gasPrice,
+ dataFetcher
+ );
if (ethPrice === undefined) console.log("can not get ETH price, skipping...", "\n");
else {
const rawtx = {
@@ -391,31 +301,9 @@ const routerClear = async(
};
console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
let gasLimit = await signer.estimateGas(rawtx);
- gasLimit = gasLimit.mul("11").div("10");
+ gasLimit = gasLimit.mul("112").div("100");
rawtx.gasLimit = gasLimit;
const gasCost = gasLimit.mul(gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // ethers.utils.formatEther(bundledOrders[i].initPrice),
- // ethPrice,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
-
- // if (maxEstimatedProfit.isNegative()) console.log(
- // ">>> Skipping because estimated negative profit for this token pair",
- // "\n"
- // );
- // else {
- console.log(">>> Trying to submit the transaction for this token pair...", "\n");
const gasCostInToken = ethers.utils.parseUnits(
ethPrice
).mul(
@@ -425,28 +313,48 @@ const routerClear = async(
36 - bundledOrders[i].buyTokenDecimals
)
);
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- arbType === "order-taker"
- ? [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- : [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100),
- exchangeData
- ]
- );
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- const tx = 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"
- );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.15
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100"),
+ exchangeData
+ ]
+ );
+ await signer.estimateGas(rawtx);
+ }
try {
+ console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100"),
+ exchangeData
+ ]
+ );
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = 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 income = getIncome(signer, receipt);
const clearActualPrice = getActualPrice(
@@ -478,7 +386,7 @@ const routerClear = async(
);
console.log(
"\x1b[36m%s\x1b[0m",
- `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
+ `Clear Initial Price: ${ethers.utils.formatEther(price)}`
);
console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
@@ -515,14 +423,9 @@ const routerClear = async(
sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
clearedAmount: bundledQuoteAmount.toString(),
clearPrice: ethers.utils.formatEther(
- bundledOrders[i].initPrice
+ price
),
- // clearGuaranteedPrice: ethers.utils.formatUnits(
- // guaranteedAmount,
- // bundledOrders[i].buyTokenDecimals
- // ),
clearActualPrice,
- // maxEstimatedProfit,
gasUsed: receipt.gasUsed,
gasCost: actualGasCost,
income,
@@ -534,13 +437,17 @@ const routerClear = async(
console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
console.log(error, "\n");
}
- // }
}
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
- // reason, code, method, transaction, error, stack, message
+ if (error === "dryrun" || error === "nomatch") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction dry run failed, skipping...");
+ }
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ // reason, code, method, transaction, error, stack, message
+ }
}
}
}
diff --git a/src/srouter.js b/src/srouter.js
index f046ae8d..6b45dbc8 100644
--- a/src/srouter.js
+++ b/src/srouter.js
@@ -15,80 +15,6 @@ const {
} = require("./utils");
-/**
- * Prepares the bundled orders by getting the best deals from Router and sorting the
- * bundled orders based on the best deals
- *
- * @param {any[]} bundledOrders - The bundled orders array
- * @param {any} dataFetcher - The DataFetcher instance
- * @param {any} config - The network config data
- * @param {ethers.BigNumber} gasPrice - The network gas price
- * @param {boolean} sort - (optional) Sort based on best deals or not
- */
-const prepare = async(bundledOrders, dataFetcher, config, gasPrice, sort = true) => {
- for (let i = 0; i < bundledOrders.length; i++) {
- const bOrder = bundledOrders[i];
- const pair = bOrder.buyTokenSymbol + "/" + bOrder.sellTokenSymbol;
- try {
- const fromToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.sellTokenDecimals,
- address: bOrder.sellToken,
- symbol: bOrder.sellTokenSymbol
- });
- const toToken = new Token({
- chainId: config.chainId,
- decimals: bOrder.buyTokenDecimals,
- address: bOrder.buyToken,
- symbol: bOrder.buyTokenSymbol
- });
- await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
- const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken, toToken);
- const route = Router.findBestRoute(
- pcMap,
- config.chainId,
- fromToken,
- // cumulativeAmount,
- "1" + "0".repeat(bOrder.sellTokenDecimals),
- toToken,
- gasPrice.toNumber(),
- // providers,
- // poolFilter
- );
- if (route.status == "NoWay") throw "could not find any route for this token pair";
-
- const price = route.amountOutBN.mul("1" + "0".repeat(18 - bOrder.buyTokenDecimals));
- bOrder.initPrice = price;
-
- console.log(`Current market price for ${pair} for: ${ethers.utils.formatEther(price)}`);
- console.log("Current ratio of the orders in this token pair:");
- bOrder.takeOrders.forEach(v => {
- if (v.ratio) console.log(ethers.utils.formatEther(v.ratio));
- });
- bOrder.takeOrders = bOrder.takeOrders.filter(
- v => v.ratio !== undefined ? price.gte(v.ratio) : true
- );
- console.log("\n");
- }
- catch(error) {
- console.log(`>>> could not get price for this ${pair} due to:`);
- console.log(error, "\n");
- }
- }
- console.log(
- ">>> Filtering bundled orders with lower ratio than current market price...",
- "\n"
- );
- bundledOrders = bundledOrders.filter(v => v.initPrice && v.takeOrders.length > 0);
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
- return bundledOrders;
-};
-
/**
* Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with specialized router contract
*
@@ -96,22 +22,19 @@ const prepare = async(bundledOrders, dataFetcher, config, gasPrice, sort = true)
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction
* for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const srouterClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
- const lps = processLps(config.lps);
+ const lps = processLps(config.lps, config.chainId);
const dataFetcher = getDataFetcher(config, lps, !!config.usePublicRpc);
const signer = config.signer;
const arbAddress = config.arbAddress;
@@ -125,10 +48,10 @@ const srouterClear = async(
// instantiating orderbook contract
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
- let gasPrice = await signer.provider.getGasPrice();
-
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32mS-ROUTER\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
@@ -141,11 +64,6 @@ const srouterClear = async(
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb, maxProfit);
- console.log(
- "------------------------- Getting Best Deals From RouteProcessor3 -------------------------",
- "\n"
- );
- bundledOrders = await prepare(bundledOrders, dataFetcher, config, gasPrice, prioritization);
}
else {
console.log("No orders found, exiting...", "\n");
@@ -157,15 +75,9 @@ const srouterClear = async(
return;
}
- console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
-
const report = [];
for (let i = 0; i < bundledOrders.length; i++) {
try {
- gasPrice = await signer.provider.getGasPrice();
console.log(
`------------------------- Trying To Clear ${
bundledOrders[i].buyTokenSymbol
@@ -177,305 +89,322 @@ const srouterClear = async(
console.log(`Buy Token Address: ${bundledOrders[i].buyToken}`);
console.log(`Sell Token Address: ${bundledOrders[i].sellToken}`, "\n");
- if (!bundledOrders[i].takeOrders.length) console.log(
- "All orders of this token pair have empty vault balance, skipping...",
- "\n"
- );
- else {
- const fromToken = new Token({
- chainId: config.chainId,
- decimals: bundledOrders[i].sellTokenDecimals,
- address: bundledOrders[i].sellToken,
- symbol: bundledOrders[i].sellTokenSymbol
- });
- const toToken = new Token({
- chainId: config.chainId,
- decimals: bundledOrders[i].buyTokenDecimals,
- address: bundledOrders[i].buyToken,
- symbol: bundledOrders[i].buyTokenSymbol
- });
+ if (!bundledOrders[i].takeOrders.length) throw "All orders of this token pair have empty vault balance, skipping...";
- const obSellTokenBalance = ethers.BigNumber.from(await signer.call({
- data: "0x70a08231000000000000000000000000" + orderbookAddress.slice(2),
- to: bundledOrders[i].sellToken
- }));
- const quoteChunks = obSellTokenBalance.div("5");
- let ethPrice;
+ const fromToken = new Token({
+ chainId: config.chainId,
+ decimals: bundledOrders[i].sellTokenDecimals,
+ address: bundledOrders[i].sellToken,
+ symbol: bundledOrders[i].sellTokenSymbol
+ });
+ const toToken = new Token({
+ chainId: config.chainId,
+ decimals: bundledOrders[i].buyTokenDecimals,
+ address: bundledOrders[i].buyToken,
+ symbol: bundledOrders[i].buyTokenSymbol
+ });
- for (let j = 5; j > 0; j--) {
- const maximumInput = j === 5 ? obSellTokenBalance : quoteChunks.mul(j);
- const maximumInputFixed = maximumInput.mul(
- "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals)
- );
+ const obSellTokenBalance = ethers.BigNumber.from(await signer.call({
+ data: "0x70a08231000000000000000000000000" + orderbookAddress.slice(2),
+ to: bundledOrders[i].sellToken
+ }));
+ const quoteChunks = obSellTokenBalance.div("5");
+
+ if (obSellTokenBalance.isZero()) throw `Orderbook has no ${
+ bundledOrders[i].sellTokenSymbol
+ } balance, skipping...`;
+
+ let ethPrice;
+ const gasPrice = await signer.provider.getGasPrice();
+ try {
+ if (gasCoveragePercentage !== "0") ethPrice = await getEthPrice(
+ config,
+ bundledOrders[i].buyToken,
+ bundledOrders[i].buyTokenDecimals,
+ gasPrice,
+ dataFetcher
+ );
+ else ethPrice = "0";
+ if (ethPrice === undefined) throw "could not find a route for ETH price, skipping...";
+ }
+ catch {
+ throw "could not get ETH price, skipping...";
+ }
+ for (let j = 5; j > 0; j--) {
+ const maximumInput = j === 5 ? obSellTokenBalance : quoteChunks.mul(j);
+ const maximumInputFixed = maximumInput.mul(
+ "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals)
+ );
- console.log(`>>> Trying to arb with ${
+ console.log(`>>> Trying to arb with ${
+ ethers.utils.formatEther(maximumInputFixed)
+ } ${
+ bundledOrders[i].sellTokenSymbol
+ } as maximum input`);
+ console.log(">>> Getting best route", "\n");
+
+ await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
+ const pcMap = dataFetcher.getCurrentPoolCodeMap(
+ fromToken,
+ toToken
+ );
+ const route = Router.findBestRoute(
+ pcMap,
+ config.chainId,
+ fromToken,
+ maximumInput,
+ toToken,
+ gasPrice.toNumber(),
+ // 30e9,
+ // providers,
+ // poolFilter
+ );
+ if (route.status == "NoWay") console.log(
+ "\x1b[31m%s\x1b[0m",
+ `could not find any route for this token pair for ${
ethers.utils.formatEther(maximumInputFixed)
} ${
bundledOrders[i].sellTokenSymbol
- } as maximum input`);
- console.log(">>> Getting best route", "\n");
- await fetchPoolsForTokenWrapper(dataFetcher, fromToken, toToken);
- const pcMap = dataFetcher.getCurrentPoolCodeMap(
- fromToken,
- toToken
+ }, trying with a lower amount...`
+ );
+ else {
+ const rateFixed = route.amountOutBN.mul(
+ "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
+ );
+ const price = rateFixed.mul("1" + "0".repeat(18)).div(maximumInputFixed);
+ if (maxProfit) bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter(
+ v => v.ratio !== undefined ? price.mul("102").div("100").gte(v.ratio) : true
+ );
+ console.log(
+ "Current best route price for this token pair:",
+ `\x1b[33m${ethers.utils.formatEther(price)}\x1b[0m`,
+ "\n"
+ );
+ console.log(">>> Route portions: ", "\n");
+ visualizeRoute(fromToken, toToken, route.legs).forEach(
+ v => console.log("\x1b[36m%s\x1b[0m", v)
);
- const route = Router.findBestRoute(
+ console.log("");
+
+ const rpParams = Router.routeProcessor2Params(
pcMap,
- config.chainId,
+ route,
fromToken,
- maximumInput,
toToken,
- gasPrice.toNumber(),
- // 30e9,
- // providers,
- // poolFilter
- );
- if (route.status == "NoWay") console.log(
- "could not find any route for this token pair with this certain amount"
+ arb.address,
+ config.routeProcessor3Address,
+ // permits
+ // "0.005"
);
- else {
- const rateFixed = route.amountOutBN.mul(
- "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- );
- const price = rateFixed.mul("1" + "0".repeat(18)).div(maximumInputFixed);
- console.log(`Current best route price for this token pair: ${ethers.utils.formatEther(price)}`, "\n");
- console.log(">>> Route portions: ", "\n");
- visualizeRoute(fromToken, toToken, route.legs).forEach(
- v => console.log("\x1b[36m%s\x1b[0m", v)
- );
- console.log("");
+ const takeOrdersConfigStruct = {
+ minimumInput: ethers.constants.One,
+ maximumInput,
+ maximumIORatio: maxRatio ? ethers.constants.MaxUint256 : price,
+ orders: bundledOrders[i].takeOrders.map(v => v.takeOrder),
+ data: ethers.utils.defaultAbiCoder.encode(
+ ["bytes"],
+ [rpParams.routeCode]
+ )
+ };
- const rpParams = Router.routeProcessor2Params(
- pcMap,
- route,
- fromToken,
- toToken,
- arb.address,
- config.routeProcessor3Address,
- // permits
- // "0.005"
- );
- const takeOrdersConfigStruct = {
- minimumInput: ethers.constants.One,
- maximumInput,
- maximumIORatio: maxRatio ? ethers.constants.MaxUint256 : price,
- orders: bundledOrders[i].takeOrders.map(v => v.takeOrder),
- data: ethers.utils.defaultAbiCoder.encode(
- ["bytes"],
- [rpParams.routeCode]
- )
+ // building and submit the transaction
+ try {
+ const rawtx = {
+ data: arb.interface.encodeFunctionData("arb", [takeOrdersConfigStruct, "0"]),
+ to: arb.address,
+ gasPrice
};
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ let gasLimit;
+ try {
+ gasLimit = await signer.estimateGas(rawtx);
+ }
+ catch {
+ throw "nomatch";
+ }
+ gasLimit = gasLimit.mul("112").div("100");
+ rawtx.gasLimit = gasLimit;
+ const gasCost = gasLimit.mul(gasPrice);
+ const gasCostInToken = ethers.utils.parseUnits(
+ ethPrice
+ ).mul(
+ gasCost
+ ).div(
+ "1" + "0".repeat(
+ 36 - bundledOrders[i].buyTokenDecimals
+ )
+ );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.2
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ );
+ try {
+ await signer.estimateGas(rawtx);
+ }
+ catch {
+ throw "dryrun";
+ }
+ }
- // building and submit the transaction
+ // submit the tx only if dry runs with headroom is passed
try {
- if (ethPrice === undefined) ethPrice = await getEthPrice(
- config,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- gasPrice,
- dataFetcher
+ console.log(">>> Trying to submit the transaction...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
);
- if (ethPrice === undefined) console.log("can not get ETH price, skipping...", "\n");
- else {
- const rawtx = {
- data: arb.interface.encodeFunctionData("arb", [takeOrdersConfigStruct, "0"]),
- to: arb.address,
- gasPrice
- };
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- let gasLimit;
- try {
- gasLimit = await signer.estimateGas(rawtx);
- }
- catch {
- // console.log(err);
- throw "nomatch";
- }
- gasLimit = gasLimit.mul("11").div("10");
- rawtx.gasLimit = gasLimit;
- const gasCost = gasLimit.mul(gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // ethers.utils.formatEther(bundledOrders[i].initPrice),
- // ethPrice,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = await signer.sendTransaction(rawtx);
- // if (maxEstimatedProfit.isNegative()) console.log(
- // ">>> Skipping because estimated negative profit for this token pair",
- // "\n"
- // );
- // else {
- console.log(">>> Trying to submit the transaction...", "\n");
- const gasCostInToken = ethers.utils.parseUnits(
+ 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();
+ if (receipt.status === 1) {
+ const clearActualAmount = getActualClearAmount(
+ arbAddress,
+ orderbookAddress,
+ receipt
+ );
+ const income = getIncome(signer, receipt);
+ const clearActualPrice = getActualPrice(
+ receipt,
+ orderbookAddress,
+ arbAddress,
+ clearActualAmount.mul("1" + "0".repeat(
+ 18 - bundledOrders[i].sellTokenDecimals
+ )),
+ bundledOrders[i].buyTokenDecimals
+ );
+ const actualGasCost = ethers.BigNumber.from(
+ receipt.effectiveGasPrice
+ ).mul(receipt.gasUsed);
+ const actualGasCostInToken = ethers.utils.parseUnits(
ethPrice
).mul(
- gasCost
+ actualGasCost
).div(
"1" + "0".repeat(
36 - bundledOrders[i].buyTokenDecimals
)
);
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- );
- const tx = await signer.sendTransaction(rawtx);
+ const netProfit = income
+ ? income.sub(actualGasCostInToken)
+ : undefined;
- 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"
+ "\x1b[36m%s\x1b[0m",
+ `Clear Initial Price: ${ethers.utils.formatEther(price)}`
);
-
- try {
- const receipt = await tx.wait();
- // console.log(receipt);
- if (receipt.status === 1) {
- const clearActualAmount = getActualClearAmount(
- arbAddress,
- orderbookAddress,
- receipt
- );
- const income = getIncome(signer, receipt);
- const clearActualPrice = getActualPrice(
- receipt,
- orderbookAddress,
- arbAddress,
- clearActualAmount.mul("1" + "0".repeat(
- 18 - bundledOrders[i].sellTokenDecimals
- )),
- bundledOrders[i].buyTokenDecimals
- );
- const actualGasCost = ethers.BigNumber.from(
- receipt.effectiveGasPrice
- ).mul(receipt.gasUsed);
- const actualGasCostInToken = ethers.utils.parseUnits(
- ethPrice
- ).mul(
- actualGasCost
- ).div(
- "1" + "0".repeat(
- 36 - bundledOrders[i].buyTokenDecimals
- )
- );
- const netProfit = income
- ? income.sub(actualGasCostInToken)
- : undefined;
-
- console.log(
- "\x1b[36m%s\x1b[0m",
- `Clear Initial Price: ${ethers.utils.formatEther(bundledOrders[i].initPrice)}`
- );
- console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
- console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
- ethers.utils.formatUnits(
- clearActualAmount,
- bundledOrders[i].sellTokenDecimals
- )
- } ${bundledOrders[i].sellTokenSymbol}`);
- console.log("\x1b[36m%s\x1b[0m", `Consumed Gas: ${
- ethers.utils.formatEther(actualGasCost)
- } ${
- config.nativeToken.symbol
- }`, "\n");
- if (income) {
- console.log("\x1b[35m%s\x1b[0m", `Gross Income: ${ethers.utils.formatUnits(
- income,
- bundledOrders[i].buyTokenDecimals
- )} ${bundledOrders[i].buyTokenSymbol}`);
- console.log("\x1b[35m%s\x1b[0m", `Net Profit: ${ethers.utils.formatUnits(
- netProfit,
- bundledOrders[i].buyTokenDecimals
- )} ${bundledOrders[i].buyTokenSymbol}`, "\n");
- }
-
- report.push({
- transactionHash: receipt.transactionHash,
- tokenPair:
- bundledOrders[i].buyTokenSymbol +
- "/" +
- bundledOrders[i].sellTokenSymbol,
- buyToken: bundledOrders[i].buyToken,
- buyTokenDecimals: bundledOrders[i].buyTokenDecimals,
- sellToken: bundledOrders[i].sellToken,
- sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
- clearedAmount: clearActualAmount.toString(),
- clearPrice: ethers.utils.formatEther(
- bundledOrders[i].initPrice
- ),
- clearActualPrice,
- // maxEstimatedProfit,
- gasUsed: receipt.gasUsed,
- gasCost: actualGasCost,
- income,
- netProfit,
- clearedOrders: bundledOrders[i].takeOrders.map(
- v => v.id
- ),
- });
- j = 0;
- }
- else if (j > 1) console.log(
- `could not clear with ${ethers.utils.formatEther(
- maximumInputFixed
- )} ${
- bundledOrders[i].sellTokenSymbol
- } as max input, trying with lower amount...`
- );
- else console.log("could not arb this pair");
- }
- catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
- console.log(error, "\n");
- if (j > 1) console.log(
- "\x1b[34m%s\x1b[0m",
- `could not clear with ${ethers.utils.formatEther(
- maximumInputFixed
- )} ${
- bundledOrders[i].sellTokenSymbol
- } as max input, trying with lower amount...`, "\n"
- );
- else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
+ console.log("\x1b[36m%s\x1b[0m", `Clear Actual Price: ${clearActualPrice}`);
+ console.log("\x1b[36m%s\x1b[0m", `Clear Amount: ${
+ ethers.utils.formatUnits(
+ clearActualAmount,
+ bundledOrders[i].sellTokenDecimals
+ )
+ } ${bundledOrders[i].sellTokenSymbol}`);
+ console.log("\x1b[36m%s\x1b[0m", `Consumed Gas: ${
+ ethers.utils.formatEther(actualGasCost)
+ } ${
+ config.nativeToken.symbol
+ }`, "\n");
+ if (income) {
+ console.log("\x1b[35m%s\x1b[0m", `Gross Income: ${ethers.utils.formatUnits(
+ income,
+ bundledOrders[i].buyTokenDecimals
+ )} ${bundledOrders[i].buyTokenSymbol}`);
+ console.log("\x1b[35m%s\x1b[0m", `Net Profit: ${ethers.utils.formatUnits(
+ netProfit,
+ bundledOrders[i].buyTokenDecimals
+ )} ${bundledOrders[i].buyTokenSymbol}`, "\n");
}
+
+ report.push({
+ transactionHash: receipt.transactionHash,
+ tokenPair:
+ bundledOrders[i].buyTokenSymbol +
+ "/" +
+ bundledOrders[i].sellTokenSymbol,
+ buyToken: bundledOrders[i].buyToken,
+ buyTokenDecimals: bundledOrders[i].buyTokenDecimals,
+ sellToken: bundledOrders[i].sellToken,
+ sellTokenDecimals: bundledOrders[i].sellTokenDecimals,
+ clearedAmount: clearActualAmount.toString(),
+ clearPrice: ethers.utils.formatEther(price),
+ clearActualPrice,
+ gasUsed: receipt.gasUsed,
+ gasCost: actualGasCost,
+ income,
+ netProfit,
+ clearedOrders: bundledOrders[i].takeOrders.map(
+ v => v.id
+ ),
+ });
+ j = 0;
}
- }
- catch (error) {
- if (error !== "nomatch") {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
- // reason, code, method, transaction, error, stack, message
- }
- if (j > 1) console.log(
- "\x1b[34m%s\x1b[0m",
+ else if (j > 1) console.log(
`could not clear with ${ethers.utils.formatEther(
maximumInputFixed
)} ${
bundledOrders[i].sellTokenSymbol
- } as max input, trying with lower amount...`, "\n"
+ } as max input, trying with lower amount...`
);
- else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
+ else console.log("could not arb this pair");
}
+ catch (error) {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
+ console.log(error, "\n");
+ throw "failed-exec";
+ // if (j > 1) console.log(
+ // "\x1b[34m%s\x1b[0m",
+ // `could not clear with ${ethers.utils.formatEther(
+ // maximumInputFixed
+ // )} ${
+ // bundledOrders[i].sellTokenSymbol
+ // } as max input, trying with lower amount...`, "\n"
+ // );
+ // else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
+ }
+
+ }
+ catch (error) {
+ if (error !== "nomatch" && error !== "dryrun" && error !== "failed-exec") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ // reason, code, method, transaction, error, stack, message
+ }
+ if (error === "failed-exec") throw "Transaction execution failed, skipping this pair...";
+ if (j > 1) console.log(
+ "\x1b[34m%s\x1b[0m",
+ `could not clear with ${ethers.utils.formatEther(
+ maximumInputFixed
+ )} ${
+ bundledOrders[i].sellTokenSymbol
+ } as max input, trying with lower amount...`, "\n"
+ );
+ else console.log("\x1b[34m%s\x1b[0m", "could not arb this pair", "\n");
}
}
}
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Something went wrong, reason:", "\n");
- console.log(error);
+ if (typeof error === "string") console.log("\x1b[31m%s\x1b[0m", error, "\n");
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Something went wrong, reason:", "\n");
+ console.log(error);
+ }
}
}
return report;
diff --git a/src/utils.js b/src/utils.js
index 40375779..90dca772 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -8,11 +8,15 @@ const { DataFetcher, Router, LiquidityProviders } = require("@sushiswap/router")
/**
- * Fallback transports for viem client
+ * Chain specific fallback data
*/
-const fallbackTransports = {
+const fallbacks = {
[ChainId.ARBITRUM_NOVA]: {
transport: http("https://nova.arbitrum.io/rpc"),
+ liquidityProviders: [
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.ARBITRUM]: {
transport: [
@@ -25,30 +29,53 @@ const fallbackTransports = {
http("https://arbitrum.blockpi.network/v1/rpc/public"),
http("https://arb-mainnet-public.unifra.io"),
],
+ liquidityProviders: [
+ "dfyn",
+ "elk",
+ "sushiswapv3",
+ "uniswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.AVALANCHE]: {
transport: [
http("https://api.avax.network/ext/bc/C/rpc"),
http("https://rpc.ankr.com/avalanche")
],
+ liquidityProviders: [
+ "elk",
+ "traderjoe",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.BOBA]: {
transport: [
http("https://mainnet.boba.network"),
http("https://lightning-replica.boba.network")
],
+ liquidityProviders: [
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.BOBA_AVAX]: {
transport: [
http("https://avax.boba.network"),
http("https://replica.avax.boba.network")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.BOBA_BNB]: {
transport: [
http("https://bnb.boba.network"),
http("https://replica.bnb.boba.network")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.BSC]: {
transport: [
@@ -58,12 +85,26 @@ const fallbackTransports = {
http("https://bsc-dataseed1.binance.org"),
http("https://bsc-dataseed2.binance.org"),
],
+ liquidityProviders: [
+ "apeswap",
+ "biswap",
+ "elk",
+ "jetswap",
+ "pancakeswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv3"
+ ]
},
[ChainId.BTTC]: {
transport: http("https://rpc.bittorrentchain.io"),
},
[ChainId.CELO]: {
- transport: http("https://forno.celo.org")
+ transport: http("https://forno.celo.org"),
+ liquidityProviders: [
+ "ubeswap",
+ "sushiswapv2"
+ ]
},
[ChainId.ETHEREUM]: {
transport: [
@@ -76,6 +117,16 @@ const fallbackTransports = {
http("https://1rpc.io/eth"),
http("https://ethereum.publicnode.com"),
http("https://cloudflare-eth.com"),
+ ],
+ liquidityProviders: [
+ "apeswap",
+ "curveswap",
+ "elk",
+ "pancakeswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv2",
+ "uniswapv3"
]
},
[ChainId.FANTOM]: {
@@ -84,33 +135,66 @@ const fallbackTransports = {
http("https://rpc.fantom.network"),
http("https://rpc2.fantom.network"),
],
+ liquidityProviders: [
+ "dfyn",
+ "elk",
+ "jetswap",
+ "spookyswap",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.FUSE]: {
transport: http("https://rpc.fuse.io"),
+ liquidityProviders: [
+ "elk",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.GNOSIS]: {
transport: http("https://rpc.ankr.com/gnosis"),
+ liquidityProviders: [
+ "elk",
+ "honeyswap",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.HARMONY]: {
transport: [
http("https://api.harmony.one"),
http("https://rpc.ankr.com/harmony")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.KAVA]: {
transport: [
http("https://evm.kava.io"),
- http("https://evm2.kava.io")
+ http("https://evm2.kava.io"),
],
+ liquidityProviders: [
+ "elk"
+ ]
},
[ChainId.MOONBEAM]: {
transport: [
http("https://rpc.api.moonbeam.network"),
http("https://rpc.ankr.com/moonbeam")
],
+ liquidityProviders: [
+ "sushiswapv2"
+ ]
},
[ChainId.MOONRIVER]: {
transport: http("https://rpc.api.moonriver.moonbeam.network"),
+ liquidityProviders: [
+ "elk",
+ "sushiswapv3",
+ "sushiswapv2"
+ ]
},
[ChainId.OPTIMISM]: {
transport: [
@@ -121,6 +205,11 @@ const fallbackTransports = {
http("https://optimism.blockpi.network/v1/rpc/public"),
http("https://mainnet.optimism.io"),
],
+ liquidityProviders: [
+ "elk",
+ "sushiswapv3",
+ "uniswapv3"
+ ]
},
[ChainId.POLYGON]: {
transport: [
@@ -136,6 +225,16 @@ const fallbackTransports = {
http("https://rpc-mainnet.maticvigil.com"),
// ...polygon.rpcUrls.default.http.map((url) => http(url)),
],
+ liquidityProviders: [
+ "apeswap",
+ "dfyn",
+ "elk",
+ "jetswap",
+ "quickswap",
+ "sushiswapv3",
+ "sushiswapv2",
+ "uniswapv3"
+ ]
},
[ChainId.POLYGON_ZKEVM]: {
transport: [
@@ -143,6 +242,10 @@ const fallbackTransports = {
http("https://rpc.ankr.com/polygon_zkevm"),
http("https://rpc.polygon-zkevm.gateway.fm"),
],
+ liquidityProviders: [
+ "dovishv3",
+ "sushiswapv3"
+ ]
},
[ChainId.THUNDERCORE]: {
transport: [
@@ -150,6 +253,10 @@ const fallbackTransports = {
http("https://mainnet-rpc.thundercore.io"),
http("https://mainnet-rpc.thundertoken.net"),
],
+ liquidityProviders: [
+ "laserswap",
+ "sushiswapv3"
+ ]
},
};
@@ -681,11 +788,11 @@ const getDataFetcher = (config, liquidityProviders = [], useFallbacks = false) =
transport: config.rpc && config.rpc !== "test"
? useFallbacks
? fallback(
- [config.rpc, ...fallbackTransports[config.chainId].transport],
+ [config.rpc, ...fallbacks[config.chainId].transport],
{rank: true}
)
: http(config.rpc)
- : fallback(fallbackTransports[config.chainId].transport, {rank: true}),
+ : fallback(fallbacks[config.chainId].transport, {rank: true}),
// batch: {
// multicall: {
// batchSize: 512
@@ -804,8 +911,9 @@ const fetchPoolsForTokenWrapper = async(dataFetcher, fromToken, toToken, exclude
* Resolves an array of case-insensitive names to LiquidityProviders, ignores the ones that are not valid
*
* @param {string[]} liquidityProviders - List of liquidity providers
+ * @param {number} chainId - The chain id
*/
-const processLps = (liquidityProviders) => {
+const processLps = (liquidityProviders, chainId) => {
if (
!liquidityProviders ||
!Array.isArray(liquidityProviders) ||
@@ -815,7 +923,12 @@ const processLps = (liquidityProviders) => {
const _lps = [];
const LP = Object.values(LiquidityProviders);
for (let i = 0; i < liquidityProviders.length; i++) {
- const index = LP.findIndex(v => v.toLowerCase() === liquidityProviders[i].toLowerCase());
+ const index = LP.findIndex(
+ v => v.toLowerCase() === liquidityProviders[i].toLowerCase()
+ && !!fallbacks[chainId]?.liquidityProviders.includes(
+ liquidityProviders[i].toLowerCase()
+ )
+ );
if (index > -1 && !_lps.includes(LP[index])) _lps.push(LP[index]);
}
return _lps.length ? _lps : undefined;
@@ -1254,8 +1367,66 @@ const visualizeRoute = (fromToken, toToken, legs) => {
);
};
+/**
+ * Builds initial 0x requests bodies from token addresses that is required
+ * for getting token prices with least amount of hits possible and that is
+ * to pair up tokens in a way that each show up only once in a request body
+ * so that the number of requests will be: "number-of-tokens / 2" at best or
+ * "(number-of-tokens / 2) + 1" at worst if the number of tokens is an odd digit.
+ * This way the responses will include the "rate" for sell/buy tokens to native
+ * network token which will be used to estimate the initial price of all possible
+ * token pair combinations.
+ *
+ * @param {string} api - The 0x API endpoint URL
+ * @param {any[]} queries - The array that keeps the 0x query text
+ * @param {string} tokenAddress - The token address
+ * @param {number} tokenDecimals - The token decimals
+ * @param {string} tokenSymbol - The token symbol
+ */
+const build0xQueries = (api, queries, tokenAddress, tokenDecimals, tokenSymbol) => {
+ tokenAddress = tokenAddress.toLowerCase();
+ if (queries.length === 0) queries.push([
+ tokenAddress,
+ tokenDecimals,
+ tokenSymbol
+ ]);
+ else if (!Array.isArray(queries[queries.length - 1])) {
+ if(!queries.find(v => v.quote.includes(tokenAddress))) queries.push([
+ tokenAddress,
+ tokenDecimals,
+ tokenSymbol
+ ]);
+ }
+ else {
+ if(
+ queries[queries.length - 1][0] !== tokenAddress &&
+ !queries.slice(0, -1).find(v => v.quote.includes(tokenAddress))
+ ) {
+ queries[queries.length - 1] = {
+ quote: `${
+ api
+ }swap/v1/price?buyToken=${
+ queries[queries.length - 1][0]
+ }&sellToken=${
+ tokenAddress
+ }&sellAmount=${
+ "1" + "0".repeat(tokenDecimals)
+ }`,
+ tokens: [
+ queries[queries.length - 1][2],
+ tokenSymbol,
+ queries[queries.length - 1][0],
+ tokenAddress,
+ queries[queries.length - 1][1],
+ tokenDecimals
+ ]
+ };
+ }
+ }
+};
+
module.exports = {
- fallbackTransports,
+ fallbacks,
bnFromFloat,
toFixed18,
fromFixed18,
@@ -1277,5 +1448,6 @@ module.exports = {
promiseTimeout,
getActualClearAmount,
getRouteForTokens,
- visualizeRoute
+ visualizeRoute,
+ build0xQueries
};
diff --git a/src/zeroex.js b/src/zeroex.js
index 215b0f04..6938043d 100644
--- a/src/zeroex.js
+++ b/src/zeroex.js
@@ -7,146 +7,23 @@ const { sleep, getIncome, getActualPrice } = require("./utils");
const HEADERS = { headers: { "accept-encoding": "null" } };
-/**
- * Builds initial 0x requests bodies from token addresses that is required
- * for getting token prices with least amount of hits possible and that is
- * to pair up tokens in a way that each show up only once in a request body
- * so that the number of requests will be: "number-of-tokens / 2" at best or
- * "(number-of-tokens / 2) + 1" at worst if the number of tokens is an odd digit.
- * This way the responses will include the "rate" for sell/buy tokens to native
- * network token which will be used to estimate the initial price of all possible
- * token pair combinations.
- *
- * @param {string} api - The 0x API endpoint URL
- * @param {any[]} quotes - The array that keeps the quotes
- * @param {string} tokenAddress - The token address
- * @param {number} tokenDecimals - The token decimals
- * @param {string} tokenSymbol - The token symbol
- */
-const initRequests = (api, quotes, tokenAddress, tokenDecimals, tokenSymbol) => {
- if (quotes.length === 0) quotes.push([
- tokenAddress,
- tokenDecimals,
- tokenSymbol
- ]);
- else if (!Array.isArray(quotes[quotes.length - 1])) {
- if(!quotes.find(v => v.quote.includes(tokenAddress))) quotes.push([
- tokenAddress,
- tokenDecimals,
- tokenSymbol
- ]);
- }
- else {
- if(
- quotes[quotes.length - 1][0] !== tokenAddress &&
- !quotes.slice(0, -1).find(v => v.quote.includes(tokenAddress))
- ) {
- quotes[quotes.length - 1] = {
- quote: `${
- api
- }swap/v1/price?buyToken=${
- quotes[quotes.length - 1][0]
- }&sellToken=${
- tokenAddress
- }&sellAmount=${
- "1" + "0".repeat(tokenDecimals)
- }`,
- tokens: [quotes[quotes.length - 1][2], tokenSymbol]
- };
- }
- }
-};
-
-/**
- * Prepares the bundled orders by getting the best deals from 0x and sorting the
- * bundled orders based on the best deals
- *
- * @param {string[]} quotes - The 0x request quote bodies
- * @param {any[]} bundledOrders - The bundled orders array
- * @param {boolean} sort - (optional) Sort based on best deals or not
- */
-const prepare = async(quotes, bundledOrders, sort = true) => {
- try {
- console.log(">>> Getting initial prices from 0x");
- const promises = [];
- for (let i = 0; i < quotes.length; i++) {
- promises.push(axios.get(quotes[i].quote, HEADERS));
- await sleep(1000);
- }
- const responses = await Promise.allSettled(promises);
-
- let prices = [];
- responses.forEach((v, i) => {
- if (v.status == "fulfilled") prices.push([
- {
- token: v.value.data.buyTokenAddress,
- rate: v.value.data.buyTokenToEthRate
- },
- {
- token: v.value.data.sellTokenAddress,
- rate: v.value.data.sellTokenToEthRate
- }
- ]);
- else {
- console.log(`Could not get prices for ${quotes[i].tokens[0]} and ${quotes[i].tokens[1]}, reason:`);
- console.log(v.reason.message);
- }
- });
- prices = prices.flat();
-
- bundledOrders.forEach(v => {
- console.log(`\nCalculating initial price for ${v.buyTokenSymbol}/${v.sellTokenSymbol} ...`);
- const sellTokenPrice = prices.find(
- e => e.token.toLowerCase() === v.sellToken.toLowerCase()
- )?.rate;
- const buyTokenPrice = prices.find(
- e => e.token.toLowerCase() === v.buyToken.toLowerCase()
- )?.rate;
- if (sellTokenPrice && buyTokenPrice) {
- v.initPrice = ethers.utils.parseUnits(buyTokenPrice)
- .mul(ethers.utils.parseUnits("1"))
- .div(ethers.utils.parseUnits(sellTokenPrice));
- console.log(`result: ${ethers.utils.formatEther(v.initPrice)}`);
- }
- else console.log("Could not calculate initial price for this token pair due to lack of required data!");
- });
- bundledOrders = bundledOrders.filter(v => v.initPrice !== undefined);
-
- if (sort) {
- console.log("\n", ">>> Sorting the bundled orders based on initial prices...");
- bundledOrders.sort(
- (a, b) => a.initPrice.gt(b.initPrice) ? -1 : a.initPrice.lt(b.initPrice) ? 1 : 0
- );
- }
- return bundledOrders;
- }
- catch (error) {
- console.log("something went wrong during the process of getting initial prices!");
- console.log(error);
- return [];
- }
-};
-
/**
* Main function that gets order details from subgraph, bundles the ones that have balance and tries clearing them with 0x
*
* @param {object} config - The configuration object
* @param {any[]} ordersDetails - The order details queried from subgraph
* @param {string} gasCoveragePercentage - (optional) The percentage of the gas cost to cover on each transaction for it to be considered profitable and get submitted
- * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true
* @returns The report of details of cleared orders
*/
const zeroExClear = async(
config,
ordersDetails,
- gasCoveragePercentage = "100",
- prioritization = true
+ gasCoveragePercentage = "100"
) => {
if (
gasCoveragePercentage < 0 ||
!Number.isInteger(Number(gasCoveragePercentage))
) throw "invalid gas coverage percentage, must be an integer greater than equal 0";
- if (typeof prioritization !== "boolean") throw "invalid value for 'prioritization'";
let rateLimit;
if (config.monthlyRatelimit !== undefined) {
@@ -162,8 +39,8 @@ const zeroExClear = async(
const proxyAddress = config.zeroEx.proxyAddress;
const arbAddress = config.arbAddress;
const orderbookAddress = config.orderbookAddress;
- const nativeToken = config.nativeToken;
const arbType = config.arbType;
+ // const nativeToken = config.nativeWrappedToken;
// set the api key in headers
if (config.apiKey) HEADERS.headers["0x-api-key"] = config.apiKey;
@@ -176,14 +53,16 @@ const zeroExClear = async(
const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer);
console.log(
- "------------------------- Starting Clearing Process -------------------------",
+ "------------------------- Starting The",
+ "\x1b[32m0x\x1b[0m",
+ "Mode -------------------------",
"\n"
);
console.log("\x1b[33m%s\x1b[0m", Date());
console.log("Arb Contract Address: " , arbAddress);
console.log("OrderBook Contract Address: " , orderbookAddress, "\n");
- const initQuotes = [];
+ // const initPriceQueries = [];
let bundledOrders = [];
if (ordersDetails.length) {
@@ -191,22 +70,22 @@ const zeroExClear = async(
"------------------------- Bundling Orders -------------------------", "\n"
);
bundledOrders = await bundleTakeOrders(ordersDetails, orderbook, arb);
- for (let i = 0; i < bundledOrders.length; i++) {
- initRequests(
- api,
- initQuotes,
- bundledOrders[i].sellToken,
- bundledOrders[i].sellTokenDecimals,
- bundledOrders[i].sellTokenSymbol
- );
- initRequests(
- api,
- initQuotes,
- bundledOrders[i].buyToken,
- bundledOrders[i].buyTokenDecimals,
- bundledOrders[i].buyTokenSymbol
- );
- }
+ // for (let i = 0; i < bundledOrders.length; i++) {
+ // build0xQueries(
+ // api,
+ // initPriceQueries,
+ // bundledOrders[i].sellToken,
+ // bundledOrders[i].sellTokenDecimals,
+ // bundledOrders[i].sellTokenSymbol
+ // );
+ // build0xQueries(
+ // api,
+ // initPriceQueries,
+ // bundledOrders[i].buyToken,
+ // bundledOrders[i].buyTokenDecimals,
+ // bundledOrders[i].buyTokenSymbol
+ // );
+ // }
}
else {
console.log("No orders found, exiting...", "\n");
@@ -218,36 +97,38 @@ const zeroExClear = async(
return;
}
- console.log(
- "------------------------- Getting Best Deals From 0x -------------------------",
- "\n"
- );
- if (Array.isArray(initQuotes[initQuotes.length - 1])) {
- initQuotes[initQuotes.length - 1] = {
- quote: `${
- api
- }swap/v1/price?buyToken=${
- nativeToken.address.toLowerCase()
- }&sellToken=${
- initQuotes[initQuotes.length - 1][0]
- }&sellAmount=${
- "1" + "0".repeat(initQuotes[initQuotes.length - 1][1])
- }`,
- tokens: ["ETH", initQuotes[initQuotes.length - 1][2]]
- };
- }
- hits += initQuotes.length;
- bundledOrders = await prepare(
- initQuotes,
- bundledOrders,
- prioritization
- );
-
- if (bundledOrders.length) console.log(
- "------------------------- Trying To Clear Bundled Orders -------------------------",
- "\n"
- );
- else {
+ // console.log(
+ // "------------------------- Getting Best Deals From 0x -------------------------",
+ // "\n"
+ // );
+ // if (Array.isArray(initPriceQueries[initPriceQueries.length - 1])) {
+ // initPriceQueries[initPriceQueries.length - 1] = {
+ // quote: `${
+ // api
+ // }swap/v1/price?buyToken=${
+ // nativeToken.address.toLowerCase()
+ // }&sellToken=${
+ // initPriceQueries[initPriceQueries.length - 1][0]
+ // }&sellAmount=${
+ // "1" + "0".repeat(initPriceQueries[initPriceQueries.length - 1][1])
+ // }`,
+ // tokens: [
+ // nativeToken.symbol,
+ // initPriceQueries[initPriceQueries.length - 1][2],
+ // nativeToken.address.toLowerCase(),
+ // initPriceQueries[initPriceQueries.length - 1][0],
+ // nativeToken.decimals,
+ // initPriceQueries[initPriceQueries.length - 1][1],
+ // ]
+ // };
+ // }
+ // hits += initPriceQueries.length;
+ // bundledOrders = await prepare(
+ // initPriceQueries,
+ // bundledOrders
+ // );
+
+ if (bundledOrders.length === 0) {
console.log("Could not find any order to clear for current market price, exiting...", "\n");
return;
}
@@ -355,7 +236,6 @@ const zeroExClear = async(
);
if (bundledOrders[i].takeOrders.length) {
-
cumulativeAmount = ethers.constants.Zero;
bundledOrders[i].takeOrders.forEach(v => {
cumulativeAmount = cumulativeAmount.add(v.quoteAmount);
@@ -394,10 +274,9 @@ const zeroExClear = async(
orders: bundledOrders[i].takeOrders.map(v => v.takeOrder),
};
if (/^flash-loan-v3$|^order-taker$/.test(arbType)) {
- takeOrdersConfigStruct.data = "0x00";
+ takeOrdersConfigStruct.data = "0x";
delete takeOrdersConfigStruct.output;
delete takeOrdersConfigStruct.input;
- if (arbType === "flash-loan-v3") takeOrdersConfigStruct.data = "0x";
}
// submit the transaction
@@ -407,8 +286,6 @@ const zeroExClear = async(
[txQuote.allowanceTarget, proxyAddress, txQuote.data]
);
if (arbType === "order-taker") takeOrdersConfigStruct.data = exchangeData;
-
- // console.log(">>> Estimating the profit for this token pair...", "\n");
const rawtx = {
data: arb.interface.encodeFunctionData(
"arb",
@@ -428,43 +305,9 @@ const zeroExClear = async(
};
console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
let gasLimit = await signer.estimateGas(rawtx);
- gasLimit = gasLimit.mul("11").div("10");
+ gasLimit = gasLimit.mul("112").div("100");
rawtx.gasLimit = gasLimit;
- const gasCost = gasLimit.mul(gasPrice);
-
- // let gasLimit;
- // console.log("Block Number: " + await signer.provider.getBlockNumber());
- // if (arbType === "order-taker") gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // ethers.constants.Zero,
- // { gasPrice: txQuote.gasPrice }
- // );
- // else gasLimit = await arb.estimateGas.arb(
- // takeOrdersConfigStruct,
- // ethers.constants.Zero,
- // exchangeData,
- // { gasPrice: txQuote.gasPrice }
- // );
-
- // gasLimit = gasLimit.mul("11").div("10");
- // const gasCost = gasLimit.mul(txQuote.gasPrice);
- // const maxEstimatedProfit = estimateProfit(
- // txQuote.price,
- // txQuote.buyTokenToEthRate,
- // bundledOrders[i],
- // gasCost,
- // gasCoveragePercentage
- // ).div(
- // "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals)
- // );
- // console.log(`Max Estimated Profit: ${
- // ethers.utils.formatUnits(
- // maxEstimatedProfit,
- // bundledOrders[i].buyTokenDecimals
- // )
- // } ${bundledOrders[i].buyTokenSymbol}`, "\n");
- // if (!maxEstimatedProfit.isNegative()) {
- console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ const gasCost = gasLimit.mul(txQuote.gasPrice);
const gasCostInToken = ethers.utils.parseUnits(
txQuote.buyTokenToEthRate
).mul(
@@ -474,31 +317,50 @@ const zeroExClear = async(
36 - bundledOrders[i].buyTokenDecimals
)
);
- rawtx.data = arb.interface.encodeFunctionData(
- "arb",
- arbType === "order-taker"
- ? [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100)
- ]
- : [
- takeOrdersConfigStruct,
- gasCostInToken.mul(gasCoveragePercentage).div(100),
- exchangeData
- ]
- );
- console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
- const tx = 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"
- );
+ if (gasCoveragePercentage !== "0") {
+ const headroom = (
+ Number(gasCoveragePercentage) * 1.2
+ ).toFixed();
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(headroom).div("100"),
+ exchangeData
+ ]
+ );
+ await signer.estimateGas(rawtx);
+ }
try {
+ console.log(">>> Trying to submit the transaction for this token pair...", "\n");
+ rawtx.data = arb.interface.encodeFunctionData(
+ "arb",
+ arbType === "order-taker"
+ ? [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100")
+ ]
+ : [
+ takeOrdersConfigStruct,
+ gasCostInToken.mul(gasCoveragePercentage).div("100"),
+ exchangeData
+ ]
+ );
+ console.log("Block Number: " + await signer.provider.getBlockNumber(), "\n");
+ const tx = 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();
- // console.log(receipt);
const income = getIncome(signer, receipt);
const clearActualPrice = getActualPrice(
receipt,
@@ -565,7 +427,6 @@ const zeroExClear = async(
clearPrice: txQuote.price,
clearGuaranteedPrice: txQuote.guaranteedPrice,
clearActualPrice,
- // maxEstimatedProfit,
gasUsed: receipt.gasUsed,
gasCost: actualGasCost,
income,
@@ -579,12 +440,15 @@ const zeroExClear = async(
console.log("\x1b[31m%s\x1b[0m", ">>> Transaction execution failed due to:");
console.log(error, "\n");
}
- // }
- // else console.log(">>> Skipping because estimated negative profit for this token pair", "\n");
}
catch (error) {
- console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
- console.log(error, "\n");
+ if (error === "dryrun" || error === "nomatch") {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction dry run failed, skipping...");
+ }
+ else {
+ console.log("\x1b[31m%s\x1b[0m", ">>> Transaction failed due to:");
+ console.log(error, "\n");
+ }
}
}
else console.log("\x1b[31m%s\x1b[0m", "Failed to get quote from 0x", "\n");
diff --git a/test/0x.test.js b/test/0x.test.js
index da4b022e..32711866 100644
--- a/test/0x.test.js
+++ b/test/0x.test.js
@@ -178,7 +178,7 @@
// config.apiKey = process?.env?.API_KEY;
// config.signer = bot;
// config.arbType = "flash-loan-v2";
-// const reports = await clear("0x", config, sgOrders, {prioritization: false});
+// const reports = await clear("0x", config, sgOrders);
// // should have cleared 2 toke pairs bundled orders
// assert.ok(reports.length == 2);
@@ -298,7 +298,7 @@
// config.apiKey = process?.env?.API_KEY;
// config.signer = bot;
// config.arbType = "flash-loan-v3";
-// const reports = await clear("0x", config, sgOrders, {prioritization: false});
+// const reports = await clear("0x", config, sgOrders);
// // should have cleared 2 toke pairs bundled orders
// assert.ok(reports.length == 2);
@@ -418,7 +418,7 @@
// config.apiKey = process?.env?.API_KEY;
// config.signer = bot;
// config.arbType = "order-taker";
-// const reports = await clear("0x", config, sgOrders, {prioritization: false});
+// const reports = await clear("0x", config, sgOrders);
// // should have cleared 2 toke pairs bundled orders
// assert.ok(reports.length == 2);
diff --git a/test/curve.test.js b/test/curve.test.js
index 73b29e5e..98e1fc48 100644
--- a/test/curve.test.js
+++ b/test/curve.test.js
@@ -175,7 +175,7 @@ describe("Rain Arb Bot 'curve' Mode Tests", async function () {
config.signer = bot;
config.lps = ["SushiSwapV2"];
config.arbType = "flash-loan-v2";
- const reports = await clear("curve", config, sgOrders, {prioritization: false});
+ const reports = await clear("curve", config, sgOrders);
// should have cleared 2 toke pairs bundled orders
assert.ok(reports.length == 2);
@@ -296,7 +296,7 @@ describe("Rain Arb Bot 'curve' Mode Tests", async function () {
config.signer = bot;
config.lps = ["SushiSwapV2"];
config.arbType = "flash-loan-v3";
- const reports = await clear("curve", config, sgOrders, {prioritization: false});
+ const reports = await clear("curve", config, sgOrders);
// should have cleared 2 toke pairs bundled orders
assert.ok(reports.length == 2);
@@ -416,7 +416,7 @@ describe("Rain Arb Bot 'curve' Mode Tests", async function () {
config.signer = bot;
config.lps = ["SushiSwapV2"];
config.arbType = "order-taker";
- const reports = await clear("curve", config, sgOrders, {prioritization: false});
+ const reports = await clear("curve", config, sgOrders);
// should have cleared 2 toke pairs bundled orders
assert.ok(reports.length == 2);
diff --git a/test/router.test.js b/test/router.test.js
index a216debc..c16502ca 100644
--- a/test/router.test.js
+++ b/test/router.test.js
@@ -175,7 +175,8 @@ describe("Rain Arb Bot 'router' Mode Tests", async function () {
config.signer = bot;
config.lps = ["SushiSwapV2"];
config.arbType = "flash-loan-v2";
- const reports = await clear("router", config, sgOrders, {prioritization: false});
+ config.apiKey = process?.env?.API_KEY;
+ const reports = await clear("router", config, sgOrders);
// should have cleared 2 toke pairs bundled orders
assert.ok(reports.length == 2);
@@ -296,7 +297,8 @@ describe("Rain Arb Bot 'router' Mode Tests", async function () {
config.signer = bot;
config.lps = ["SushiSwapV2"];
config.arbType = "flash-loan-v3";
- const reports = await clear("router", config, sgOrders, {prioritization: false});
+ config.apiKey = process?.env?.API_KEY;
+ const reports = await clear("router", config, sgOrders);
// should have cleared 2 toke pairs bundled orders
assert.ok(reports.length == 2);
@@ -416,7 +418,8 @@ describe("Rain Arb Bot 'router' Mode Tests", async function () {
config.signer = bot;
config.lps = ["SushiSwapV2"];
config.arbType = "order-taker";
- const reports = await clear("router", config, sgOrders, {prioritization: false});
+ config.apiKey = process?.env?.API_KEY;
+ const reports = await clear("router", config, sgOrders);
// should have cleared 2 toke pairs bundled orders
assert.ok(reports.length == 2);
diff --git a/test/srouter.test.js b/test/srouter.test.js
index 688eb371..85f88f6b 100644
--- a/test/srouter.test.js
+++ b/test/srouter.test.js
@@ -167,7 +167,7 @@ describe("Rain Arb Bot 'srouter' Mode Tests", async function () {
config.rpc = "test";
config.signer = bot;
config.lps = ["SushiSwapV2"];
- const reports = await clear("srouter", config, sgOrders, {prioritization: false});
+ const reports = await clear("srouter", config, sgOrders);
// should have cleared 2 toke pairs bundled orders
assert.ok(reports.length == 2);