Skip to content

Commit

Permalink
Merge branch 'master' into 2024-10-25-owner-limit
Browse files Browse the repository at this point in the history
  • Loading branch information
rouzwelt authored Nov 23, 2024
2 parents b855d57 + 30b4859 commit 8fcf64f
Show file tree
Hide file tree
Showing 25 changed files with 919 additions and 288 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ Other optional arguments are:
- `--owner-profile`, Specifies the owner limit, example: --owner-profile 0x123456=12 . Will override the 'OWNER_PROFILE' in env variables
- `--public-rpc`, Allows to use public RPCs as fallbacks, default is false. Will override the 'PUBLIC_RPC' in env variables
- `--gas-price-multiplier`, Option to multiply the gas price fetched from the rpc as percentage, default is 107, ie +7%. Will override the 'GAS_PRICE_MULTIPLIER' in env variables
- `--gas-limit-multiplier`, Option to multiply the gas limit estimation from the rpc as percentage, default is 105, ie +5%. Will override the 'GAS_LIMIT_MULTIPLIER' in env variables
- `--tx-gas`, Option to set a static gas limit for all submitting txs. Will override the 'TX_GAS' in env variables
- `-V` or `--version`, output the version number
- `-h` or `--help`, output usage information

Expand Down Expand Up @@ -259,6 +261,12 @@ ROUTE="single"

# Option to multiply the gas price fetched from the rpc as percentage, default is 107, ie +7%
GAS_PRICE_MULTIPLIER=

# Option to multiply the gas limit estimation from the rpc as percentage, default is 105, ie +5%
GAS_LIMIT_MULTIPLIER=

# Option to set a static gas limit for all submitting txs
TX_GAS=
```
If both env variables and CLI argument are set, the CLI arguments will be prioritized and override the env variables.

Expand Down
6 changes: 6 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ ROUTE="single"
# Option to multiply the gas price fetched from the rpc as percentage, default is 107, ie +7%
GAS_PRICE_MULTIPLIER=

# Option to multiply the gas limit estimation from the rpc as percentage, default is 105, ie +5%
GAS_LIMIT_MULTIPLIER=

# Option to set a static gas limit for all submitting txs
TX_GAS=


# test rpcs vars
TEST_POLYGON_RPC=
Expand Down
21 changes: 13 additions & 8 deletions src/account.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ChainId, RPParams } from "sushi";
import { BigNumber, ethers } from "ethers";
import { ErrorSeverity, errorSnapshot } from "./error";
import { Native, Token, WNATIVE } from "sushi/currency";
import { ROUTE_PROCESSOR_4_ADDRESS } from "sushi/config";
import { getRpSwap, PoolBlackList, sleep } from "./utils";
import { createViemClient, getDataFetcher } from "./config";
import { ChainId, LiquidityProviders, RPParams } from "sushi";
import { mnemonicToAccount, privateKeyToAccount } from "viem/accounts";
import { erc20Abi, multicall3Abi, orderbookAbi, routeProcessor3Abi } from "./abis";
import { context, Context, SpanStatusCode, trace, Tracer } from "@opentelemetry/api";
Expand Down Expand Up @@ -157,10 +157,10 @@ export async function manageAccounts(
) {
const removedWallets: ViemClient[] = [];
let accountsToAdd = 0;
const gasPrice = await config.viemClient.getGasPrice();
for (let i = config.accounts.length - 1; i >= 0; i--) {
if (config.accounts[i].BALANCE.lt(avgGasCost.mul(4))) {
try {
const gasPrice = await config.viemClient.getGasPrice();
await sweepToMainWallet(
config.accounts[i],
config.mainAccount,
Expand Down Expand Up @@ -533,16 +533,20 @@ export async function sweepToMainWallet(
}
}

if (cumulativeGasLimit.mul(gasPrice).gt(fromWallet.BALANCE)) {
if (cumulativeGasLimit.mul(gasPrice).mul(125).div(100).gt(fromWallet.BALANCE)) {
const span = tracer?.startSpan("fund-wallet-to-sweep", undefined, mainCtx);
span?.setAttribute("details.wallet", fromWallet.account.address);
try {
const transferAmount = cumulativeGasLimit.mul(gasPrice).sub(fromWallet.BALANCE);
const transferAmount = cumulativeGasLimit
.mul(gasPrice)
.mul(125)
.div(100)
.sub(fromWallet.BALANCE);
span?.setAttribute("details.amount", ethers.utils.formatUnits(transferAmount));
const hash = await toWallet.sendTransaction({
to: fromWallet.account.address,
value: transferAmount.toBigInt(),
nonce: await getNonce(fromWallet),
nonce: await getNonce(toWallet),
});
const receipt = await toWallet.waitForTransactionReceipt({
hash,
Expand Down Expand Up @@ -640,6 +644,7 @@ export async function sweepToMainWallet(
if (transferAmount.gt(0)) {
span?.setAttribute("details.amount", ethers.utils.formatUnits(transferAmount));
const hash = await fromWallet.sendTransaction({
gasPrice,
to: toWallet.account.address,
value: transferAmount.toBigInt(),
gas: gasLimit.toBigInt(),
Expand Down Expand Up @@ -746,7 +751,7 @@ export async function sweepToEth(config: BotConfig, tracer?: Tracer, ctx?: Conte
rp4Address,
config.dataFetcher,
gasPrice,
Object.values(LiquidityProviders).filter((v) => v !== LiquidityProviders.CurveSwap),
config.lps,
);
let routeText = "";
route.legs.forEach((v, i) => {
Expand Down Expand Up @@ -803,7 +808,7 @@ export async function sweepToEth(config: BotConfig, tracer?: Tracer, ctx?: Conte
const rawtx = { to: rp4Address, data: "0x" as `0x${string}` };
let gas = 0n;
let amountOutMin = ethers.constants.Zero;
for (let j = 50; j > 0; j--) {
for (let j = 50; j > 39; j--) {
amountOutMin = ethers.BigNumber.from(rpParams.amountOutMin)
.mul(2 * j)
.div(100);
Expand All @@ -819,7 +824,7 @@ export async function sweepToEth(config: BotConfig, tracer?: Tracer, ctx?: Conte
gas = await config.mainAccount.estimateGas(rawtx);
break;
} catch (error) {
if (j === 1) throw error;
if (j === 40) throw error;
}
}
const gasCost = gasPrice.mul(gas).mul(15).div(10);
Expand Down
97 changes: 86 additions & 11 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
import { BotConfig, BundledOrders, CliOptions, ViemClient } from "./types";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { manageAccounts, rotateProviders, sweepToMainWallet, sweepToEth } from "./account";
import {
sweepToEth,
manageAccounts,
rotateProviders,
sweepToMainWallet,
getBatchEthBalance,
} from "./account";
import {
prepareOrdersForRound,
getOrderbookOwnersProfileMapFromSg,
Expand Down Expand Up @@ -63,6 +69,8 @@ const ENV_OPTIONS = {
botMinBalance: process?.env?.BOT_MIN_BALANCE,
selfFundOrders: process?.env?.SELF_FUND_ORDERS,
gasPriceMultiplier: process?.env?.GAS_PRICE_MULTIPLIER,
gasLimitMultiplier: process?.env?.GAS_LIMIT_MULTIPLIER,
txGas: process?.env?.TX_GAS,
route: process?.env?.ROUTE,
ownerProfile: process?.env?.OWNER_PROFILE
? Array.from(process?.env?.OWNER_PROFILE.matchAll(/[^,\s]+/g)).map((v) => v[0])
Expand Down Expand Up @@ -184,6 +192,14 @@ const getOptions = async (argv: any, version?: string) => {
"--gas-price-multiplier <integer>",
"Option to multiply the gas price fetched from the rpc as percentage, default is 107, ie +7%. Will override the 'GAS_PRICE_MULTIPLIER' in env variables",
)
.option(
"--gas-limit-multiplier <integer>",
"Option to multiply the gas limit estimation from the rpc as percentage, default is 105, ie +5%. Will override the 'GAS_LIMIT_MULTIPLIER' in env variables",
)
.option(
"--tx-gas <integer>",
"Option to set a static gas limit for all submitting txs. Will override the 'TX_GAS' in env variables",
)
.description(
[
"A NodeJS app to find and take arbitrage trades for Rain Orderbook orders against some DeFi liquidity providers, requires NodeJS v18 or higher.",
Expand Down Expand Up @@ -223,6 +239,9 @@ const getOptions = async (argv: any, version?: string) => {
cmdOptions.selfFundOrders = cmdOptions.selfFundOrders || getEnv(ENV_OPTIONS.selfFundOrders);
cmdOptions.gasPriceMultiplier =
cmdOptions.gasPriceMultiplier || getEnv(ENV_OPTIONS.gasPriceMultiplier);
cmdOptions.gasLimitMultiplier =
cmdOptions.gasLimitMultiplier || getEnv(ENV_OPTIONS.gasLimitMultiplier);
cmdOptions.txGas = cmdOptions.txGas || getEnv(ENV_OPTIONS.txGas);
cmdOptions.botMinBalance = cmdOptions.botMinBalance || getEnv(ENV_OPTIONS.botMinBalance);
cmdOptions.ownerProfile = cmdOptions.ownerProfile || getEnv(ENV_OPTIONS.ownerProfile);
cmdOptions.route = cmdOptions.route || getEnv(ENV_OPTIONS.route);
Expand Down Expand Up @@ -285,9 +304,10 @@ export const arbRound = async (
try {
let txs;
let foundOpp = false;
let didClear = false;
const { reports = [], avgGasCost = undefined } = await clear(
config,
bundledOrders,
ordersDetails,
tracer,
ctx,
);
Expand All @@ -296,23 +316,31 @@ export const arbRound = async (
if (txs.length) {
foundOpp = true;
span.setAttribute("txUrls", txs);
span.setAttribute("didClear", true);
span.setAttribute("foundOpp", true);
} else if (
reports.some((v) => v.status === ProcessPairReportStatus.FoundOpportunity)
) {
foundOpp = true;
span.setAttribute("foundOpp", true);
}
if (
reports.some(
(v) =>
v.status === ProcessPairReportStatus.FoundOpportunity && !v.reason,
)
) {
didClear = true;
span.setAttribute("didClear", true);
}
} else {
span.setAttribute("didClear", false);
}
if (avgGasCost) {
span.setAttribute("avgGasCost", avgGasCost.toString());
span.setAttribute("avgGasCost", ethers.utils.formatUnits(avgGasCost));
}
span.setStatus({ code: SpanStatusCode.OK });
span.end();
return { txs, foundOpp, avgGasCost };
return { txs, foundOpp, didClear, avgGasCost };
} catch (e: any) {
if (e?.startsWith?.("Failed to batch quote orders")) {
span.setAttribute("severity", ErrorSeverity.LOW);
Expand All @@ -326,7 +354,7 @@ export const arbRound = async (
span.setAttribute("didClear", false);
span.setAttribute("foundOpp", false);
span.end();
return { txs: [], foundOpp: false, avgGasCost: undefined };
return { txs: [], foundOpp: false, didClear: false, avgGasCost: undefined };
}
});
};
Expand Down Expand Up @@ -406,6 +434,32 @@ export async function startup(argv: any, version?: string, tracer?: Tracer, ctx?
} else {
options.gasPriceMultiplier = 107;
}
if (options.gasLimitMultiplier) {
if (typeof options.gasLimitMultiplier === "number") {
if (options.gasLimitMultiplier <= 0 || !Number.isInteger(options.gasLimitMultiplier))
throw "invalid gasLimitMultiplier value, must be an integer greater than zero";
} else if (
typeof options.gasLimitMultiplier === "string" &&
/^[0-9]+$/.test(options.gasLimitMultiplier)
) {
options.gasLimitMultiplier = Number(options.gasLimitMultiplier);
if (options.gasLimitMultiplier <= 0)
throw "invalid gasLimitMultiplier value, must be an integer greater than zero";
} else throw "invalid gasLimitMultiplier value, must be an integer greater than zero";
} else {
options.gasLimitMultiplier = 105;
}
if (options.txGas) {
if (typeof options.txGas === "number") {
if (options.txGas <= 0 || !Number.isInteger(options.txGas))
throw "invalid txGas value, must be an integer greater than zero";
else options.txGas = BigInt(options.txGas);
} else if (typeof options.txGas === "string" && /^[0-9]+$/.test(options.txGas)) {
options.txGas = BigInt(options.txGas);
if (options.txGas <= 0n)
throw "invalid txGas value, must be an integer greater than zero";
} else throw "invalid txGas value, must be an integer greater than zero";
}
const poolUpdateInterval = _poolUpdateInterval * 60 * 1000;
let ordersDetails: SgOrder[] = [];
if (!process?.env?.CLI_STARTUP_TEST) {
Expand Down Expand Up @@ -607,16 +661,19 @@ export const main = async (argv: any, version?: string) => {
config,
bundledOrders,
);
let txs, foundOpp, roundAvgGasCost;
let txs, foundOpp, didClear, roundAvgGasCost;
if (roundResult) {
txs = roundResult.txs;
foundOpp = roundResult.foundOpp;
didClear = roundResult.didClear;
roundAvgGasCost = roundResult.avgGasCost;
}
if (txs && txs.length) {
roundSpan.setAttribute("txUrls", txs);
roundSpan.setAttribute("didClear", true);
roundSpan.setAttribute("foundOpp", true);
} else if (didClear) {
roundSpan.setAttribute("foundOpp", true);
roundSpan.setAttribute("didClear", true);
} else if (foundOpp) {
roundSpan.setAttribute("foundOpp", true);
roundSpan.setAttribute("didClear", false);
Expand All @@ -625,6 +682,19 @@ export const main = async (argv: any, version?: string) => {
roundSpan.setAttribute("didClear", false);
}

// fecth account's balances
if (foundOpp && config.accounts.length) {
try {
const balances = await getBatchEthBalance(
config.accounts.map((v) => v.account.address),
config.viemClient as any as ViemClient,
);
config.accounts.forEach((v, i) => (v.BALANCE = balances[i]));
} catch {
/**/
}
}

// keep avg gas cost
if (roundAvgGasCost) {
const _now = Date.now();
Expand Down Expand Up @@ -711,10 +781,15 @@ export const main = async (argv: any, version?: string) => {
roundSpan.setStatus({ code: SpanStatusCode.ERROR, message: snapshot });
}
if (config.accounts.length) {
roundSpan.setAttribute(
"circulatingAccounts",
config.accounts.map((v) => v.account.address),
const accountsWithBalance: Record<string, string> = {};
config.accounts.forEach(
(v) =>
(accountsWithBalance[v.account.address] = ethers.utils.formatUnits(
v.BALANCE,
)),
);
roundSpan.setAttribute("circulatingAccounts", JSON.stringify(accountsWithBalance));
roundSpan.setAttribute("lastAccountIndex", lastUsedAccountIndex);
}
if (avgGasCost) {
roundSpan.setAttribute("avgGasCost", ethers.utils.formatUnits(avgGasCost));
Expand Down
Loading

0 comments on commit 8fcf64f

Please sign in to comment.