Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
rouzwelt committed Jan 20, 2025
1 parent 2af8ca4 commit 1ca78ee
Show file tree
Hide file tree
Showing 18 changed files with 447 additions and 1,145 deletions.
98 changes: 98 additions & 0 deletions scripts/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { PublicClient } from "viem";
import { Token } from "sushi/currency";
import { ChainId, Router } from "sushi";
import { BigNumber, ethers } from "ethers";
import { getDataFetcher } from "../src/config";

/**
* Gets the route for tokens
* @param chainId - The network chain id
* @param rpcs - The rpcs
* @param sellAmount - The sell amount, should be in onchain token value
* @param fromTokenAddress - The from token address
* @param fromTokenDecimals - The from token decimals
* @param toTokenAddress - The to token address
* @param toTokenDecimals - The to token decimals
* @param receiverAddress - The address of the receiver
* @param routeProcessorAddress - The address of the RouteProcessor contract
* @param abiencoded - If the result should be abi encoded or not
*/
export const getRouteForTokens = async (
chainId: number,
rpcs: string[],
sellAmount: BigNumber,
fromTokenAddress: string,
fromTokenDecimals: number,
toTokenAddress: string,
toTokenDecimals: number,
receiverAddress: string,
routeProcessorAddress: string,
abiEncoded = false,
) => {
const amountIn = sellAmount.toBigInt();
const fromToken = new Token({
chainId: chainId,
decimals: fromTokenDecimals,
address: fromTokenAddress,
});
const toToken = new Token({
chainId: chainId,
decimals: toTokenDecimals,
address: toTokenAddress,
});
const dataFetcher = await getDataFetcher({
chain: { id: chainId },
rpc: rpcs,
} as any as PublicClient);
await dataFetcher.fetchPoolsForToken(fromToken, toToken);
const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken, toToken);
const route = Router.findBestRoute(
pcMap,
chainId as ChainId,
fromToken,
amountIn,
toToken,
Number(await dataFetcher.web3Client.getGasPrice()),
// providers,
// poolFilter
);
if (route.status == "NoWay") throw "NoWay";
else {
let routeText = "";
route.legs.forEach((v, i) => {
if (i === 0)
routeText =
routeText +
v.tokenTo.symbol +
"/" +
v.tokenFrom.symbol +
"(" +
(v as any).poolName +
")";
else
routeText =
routeText +
" + " +
v.tokenTo.symbol +
"/" +
v.tokenFrom.symbol +
"(" +
(v as any).poolName +
")";
});
// eslint-disable-next-line no-console
console.log("Route portions: ", routeText, "\n");
const rpParams = Router.routeProcessor4Params(
pcMap,
route,
fromToken,
toToken,
receiverAddress as `0x${string}`,
routeProcessorAddress as `0x${string}`,
// permits
// "0.005"
);
if (abiEncoded) return ethers.utils.defaultAbiCoder.encode(["bytes"], [rpParams.routeCode]);
else return rpParams.routeCode;
}
};
4 changes: 2 additions & 2 deletions scripts/sweep.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* eslint-disable no-console */
import { ChainId } from "sushi";
import { ethers } from "ethers";
import { PublicClient } from "viem";
import { TokenDetails } from "../src/types";
import { errorSnapshot } from "../src/error";
import { PublicClient, erc20Abi } from "viem";
import { routeProcessor3Abi } from "../src/abis";
import { setWatchedTokens } from "../src/account";
import { Native, Token, WNATIVE } from "sushi/currency";
import { ROUTE_PROCESSOR_4_ADDRESS } from "sushi/config";
import { erc20Abi, routeProcessor3Abi } from "../src/abis";
import { createViemClient, getDataFetcher } from "../src/config";
import { getRpSwap, PoolBlackList, processLps, sleep } from "../src/utils";
import { HDAccount, mnemonicToAccount, PrivateKeyAccount } from "viem/accounts";
Expand Down
15 changes: 3 additions & 12 deletions src/abis.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
import { parseAbi } from "viem";

/**
* Minimal ABI for ERC20 contract only including Transfer event
*/
export const erc20Abi = [
"event Transfer(address indexed from, address indexed to, uint256 value)",
"function symbol() public view returns (string memory)",
"function transfer(address to, uint256 amount) external returns (bool)",
"function balanceOf(address account) external view returns (uint256)",
"function approve(address spender, uint256 amount) external returns (bool)",
"function allowance(address owner, address spender) external view returns (uint256)",
] as const;

// structs used in the orderbook and arb abis
export const IO = "(address token, uint8 decimals, uint256 vaultId)" as const;
export const EvaluableV3 = "(address interpreter, address store, bytes bytecode)" as const;
Expand Down Expand Up @@ -108,6 +96,9 @@ export const DefaultArbEvaluable = {

export const TakeOrderV2EventAbi = parseAbi([orderbookAbi[13]]);
export const OrderbookQuoteAbi = parseAbi([orderbookAbi[14]]);
export const VaultBalanceAbi = parseAbi([orderbookAbi[3]]);
export const DeployerAbi = parseAbi(deployerAbi);
export const MulticallAbi = parseAbi(multicall3Abi);

/**
* Arbitrum node interface address, used to get L1 gas limit.
Expand Down
144 changes: 94 additions & 50 deletions src/account.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import { ChainId, RPParams } from "sushi";
import { BigNumber, ethers } from "ethers";
import { erc20Abi, PublicClient } from "viem";
import { estimateGasCost, getTxFee } from "./gas";
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 { mnemonicToAccount, privateKeyToAccount } from "viem/accounts";
import { erc20Abi, multicall3Abi, orderbookAbi, routeProcessor3Abi } from "./abis";
import { getRpSwap, PoolBlackList, sleep, addWatchedToken } from "./utils";
import { context, Context, SpanStatusCode, trace, Tracer } from "@opentelemetry/api";
import { parseAbi, hexToNumber, numberToHex, PublicClient, NonceManagerSource } from "viem";
import { MulticallAbi, orderbookAbi, routeProcessor3Abi, VaultBalanceAbi } from "./abis";
import {
BotConfig,
CliOptions,
ViemClient,
OwnedOrder,
TokenDetails,
OperationState,
BundledOrders,
} from "./types";

/** Standard base path for eth accounts */
Expand Down Expand Up @@ -434,7 +435,7 @@ export async function getBatchEthBalance(
viemClient.chain?.contracts?.multicall3?.address) as `0x${string}`,
allowFailure: false,
chainId: viemClient.chain.id,
abi: parseAbi(multicall3Abi),
abi: MulticallAbi,
functionName: "getEthBalance",
args: [v],
})),
Expand Down Expand Up @@ -464,7 +465,7 @@ export async function getBatchTokenBalanceForAccount(
address: v.address as `0x${string}`,
allowFailure: false,
chainId: viemClient.chain.id,
abi: parseAbi(erc20Abi),
abi: erc20Abi,
functionName: "balanceOf",
args: [address],
})),
Expand Down Expand Up @@ -886,25 +887,10 @@ export async function sweepToEth(
}
}

export async function setWatchedTokens(account: ViemClient, watchedTokens: TokenDetails[]) {
export function setWatchedTokens(account: ViemClient, watchedTokens: TokenDetails[]) {
account.BOUNTY = [...watchedTokens];
}

export function addWatchedToken(
token: TokenDetails,
watchedTokens: TokenDetails[],
account?: ViemClient,
) {
if (!watchedTokens.find((v) => v.address.toLowerCase() === token.address.toLowerCase())) {
watchedTokens.push(token);
}
if (account) {
if (!account.BOUNTY.find((v) => v.address.toLowerCase() === token.address.toLowerCase())) {
account.BOUNTY.push(token);
}
}
}

/**
* Funds the sepcified bot owned orders from the gas token
* @param ownedOrders
Expand Down Expand Up @@ -1082,35 +1068,93 @@ export async function fundOwnedOrders(
}

/**
* Creates a viem account nonce source of truth with "latest" block tag used for reading,
* viem's default nonce manager uses "pending" block tag to read the nonce of an account
* Quotes order details that are already fetched and bundled by bundleOrder()
* @param config - Config obj
* @param orderDetails - Order details to quote
* @param multicallAddressOverride - Optional multicall address
*/
export function noneSource(): NonceManagerSource {
return {
async get(parameters) {
const { address, client } = parameters;
return getTransactionCount(client as any, {
address,
blockTag: "latest",
export async function checkOwnedOrders(
config: BotConfig,
orderDetails: BundledOrders[][],
multicallAddressOverride?: string,
): Promise<OwnedOrder[]> {
const ownedOrders: any[] = [];
const result: OwnedOrder[] = [];
orderDetails.flat().forEach((v) => {
v.takeOrders.forEach((order) => {
if (
order.takeOrder.order.owner.toLowerCase() ===
config.mainAccount.account.address.toLowerCase() &&
!ownedOrders.find(
(e) =>
e.orderbook.toLowerCase() === v.orderbook.toLowerCase() &&
e.outputToken.toLowerCase() === v.sellToken.toLowerCase() &&
e.order.takeOrder.order.validOutputs[
e.order.takeOrder.outputIOIndex
].token.toLowerCase() ==
order.takeOrder.order.validOutputs[
order.takeOrder.outputIOIndex
].token.toLowerCase() &&
ethers.BigNumber.from(
e.order.takeOrder.order.validOutputs[e.order.takeOrder.outputIOIndex]
.vaultId,
).eq(
order.takeOrder.order.validOutputs[order.takeOrder.outputIOIndex]
.vaultId,
),
)
) {
ownedOrders.push({
order,
orderbook: v.orderbook,
outputSymbol: v.sellTokenSymbol,
outputToken: v.sellToken,
outputDecimals: v.sellTokenDecimals,
});
}
});
});
if (!ownedOrders.length) return result;
try {
const multicallResult = await config.viemClient.multicall({
multicallAddress:
(multicallAddressOverride as `0x${string}` | undefined) ??
config.viemClient.chain?.contracts?.multicall3?.address,
allowFailure: false,
contracts: ownedOrders.map((v) => ({
address: v.orderbook,
allowFailure: false,
chainId: config.chain.id,
abi: VaultBalanceAbi,
functionName: "vaultBalance",
args: [
// owner
v.order.takeOrder.order.owner,
// token
v.order.takeOrder.order.validOutputs[v.order.takeOrder.outputIOIndex].token,
// valut id
v.order.takeOrder.order.validOutputs[v.order.takeOrder.outputIOIndex].vaultId,
],
})),
});
for (let i = 0; i < multicallResult.length; i++) {
let vaultId =
ownedOrders[i].order.takeOrder.order.validOutputs[
ownedOrders[i].order.takeOrder.outputIOIndex
].vaultId;
if (vaultId instanceof BigNumber) vaultId = vaultId.toHexString();
result.push({
vaultId,
id: ownedOrders[i].order.id,
token: ownedOrders[i].outputToken,
symbol: ownedOrders[i].outputSymbol,
decimals: ownedOrders[i].outputDecimals,
orderbook: ownedOrders[i].orderbook,
vaultBalance: ethers.BigNumber.from(multicallResult[i]),
});
},
set() {},
};
}

/**
* Perfomrs a eth_getTransactionCount rpc request
*/
async function getTransactionCount(
client: any,
{ address, blockTag = "latest", blockNumber }: any,
): Promise<number> {
const count = await client.request(
{
method: "eth_getTransactionCount",
params: [address, blockNumber ? numberToHex(blockNumber) : blockTag],
},
{ dedupe: Boolean(blockNumber) },
);
return hexToNumber(count);
}
} catch (e) {
/**/
}
return result;
}
13 changes: 10 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ import { Command } from "commander";
import { getMetaInfo } from "./config";
import { BigNumber, ethers } from "ethers";
import { Context } from "@opentelemetry/api";
import { sleep, isBigNumberish } from "./utils";
import { getOrderChanges, SgOrder } from "./query";
import { Resource } from "@opentelemetry/resources";
import { getOrderDetails, clear, getConfig } from ".";
import { ErrorSeverity, errorSnapshot } from "./error";
import { Tracer } from "@opentelemetry/sdk-trace-base";
import { ProcessPairReportStatus } from "./processOrders";
import { sleep, getOrdersTokens, isBigNumberish } from "./utils";
import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { BotConfig, BundledOrders, CliOptions, OperationState, ViemClient } from "./types";
import {
BotConfig,
CliOptions,
ViemClient,
BundledOrders,
OperationState,
ProcessPairReportStatus,
} from "./types";
import {
sweepToEth,
manageAccounts,
Expand All @@ -24,6 +30,7 @@ import {
getBatchEthBalance,
} from "./account";
import {
getOrdersTokens,
downscaleProtection,
prepareOrdersForRound,
getOrderbookOwnersProfileMapFromSg,
Expand Down
Loading

0 comments on commit 1ca78ee

Please sign in to comment.