Skip to content

Commit 1ca78ee

Browse files
committed
init
1 parent 2af8ca4 commit 1ca78ee

File tree

18 files changed

+447
-1145
lines changed

18 files changed

+447
-1145
lines changed

scripts/route.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { PublicClient } from "viem";
2+
import { Token } from "sushi/currency";
3+
import { ChainId, Router } from "sushi";
4+
import { BigNumber, ethers } from "ethers";
5+
import { getDataFetcher } from "../src/config";
6+
7+
/**
8+
* Gets the route for tokens
9+
* @param chainId - The network chain id
10+
* @param rpcs - The rpcs
11+
* @param sellAmount - The sell amount, should be in onchain token value
12+
* @param fromTokenAddress - The from token address
13+
* @param fromTokenDecimals - The from token decimals
14+
* @param toTokenAddress - The to token address
15+
* @param toTokenDecimals - The to token decimals
16+
* @param receiverAddress - The address of the receiver
17+
* @param routeProcessorAddress - The address of the RouteProcessor contract
18+
* @param abiencoded - If the result should be abi encoded or not
19+
*/
20+
export const getRouteForTokens = async (
21+
chainId: number,
22+
rpcs: string[],
23+
sellAmount: BigNumber,
24+
fromTokenAddress: string,
25+
fromTokenDecimals: number,
26+
toTokenAddress: string,
27+
toTokenDecimals: number,
28+
receiverAddress: string,
29+
routeProcessorAddress: string,
30+
abiEncoded = false,
31+
) => {
32+
const amountIn = sellAmount.toBigInt();
33+
const fromToken = new Token({
34+
chainId: chainId,
35+
decimals: fromTokenDecimals,
36+
address: fromTokenAddress,
37+
});
38+
const toToken = new Token({
39+
chainId: chainId,
40+
decimals: toTokenDecimals,
41+
address: toTokenAddress,
42+
});
43+
const dataFetcher = await getDataFetcher({
44+
chain: { id: chainId },
45+
rpc: rpcs,
46+
} as any as PublicClient);
47+
await dataFetcher.fetchPoolsForToken(fromToken, toToken);
48+
const pcMap = dataFetcher.getCurrentPoolCodeMap(fromToken, toToken);
49+
const route = Router.findBestRoute(
50+
pcMap,
51+
chainId as ChainId,
52+
fromToken,
53+
amountIn,
54+
toToken,
55+
Number(await dataFetcher.web3Client.getGasPrice()),
56+
// providers,
57+
// poolFilter
58+
);
59+
if (route.status == "NoWay") throw "NoWay";
60+
else {
61+
let routeText = "";
62+
route.legs.forEach((v, i) => {
63+
if (i === 0)
64+
routeText =
65+
routeText +
66+
v.tokenTo.symbol +
67+
"/" +
68+
v.tokenFrom.symbol +
69+
"(" +
70+
(v as any).poolName +
71+
")";
72+
else
73+
routeText =
74+
routeText +
75+
" + " +
76+
v.tokenTo.symbol +
77+
"/" +
78+
v.tokenFrom.symbol +
79+
"(" +
80+
(v as any).poolName +
81+
")";
82+
});
83+
// eslint-disable-next-line no-console
84+
console.log("Route portions: ", routeText, "\n");
85+
const rpParams = Router.routeProcessor4Params(
86+
pcMap,
87+
route,
88+
fromToken,
89+
toToken,
90+
receiverAddress as `0x${string}`,
91+
routeProcessorAddress as `0x${string}`,
92+
// permits
93+
// "0.005"
94+
);
95+
if (abiEncoded) return ethers.utils.defaultAbiCoder.encode(["bytes"], [rpParams.routeCode]);
96+
else return rpParams.routeCode;
97+
}
98+
};

scripts/sweep.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/* eslint-disable no-console */
22
import { ChainId } from "sushi";
33
import { ethers } from "ethers";
4-
import { PublicClient } from "viem";
54
import { TokenDetails } from "../src/types";
65
import { errorSnapshot } from "../src/error";
6+
import { PublicClient, erc20Abi } from "viem";
7+
import { routeProcessor3Abi } from "../src/abis";
78
import { setWatchedTokens } from "../src/account";
89
import { Native, Token, WNATIVE } from "sushi/currency";
910
import { ROUTE_PROCESSOR_4_ADDRESS } from "sushi/config";
10-
import { erc20Abi, routeProcessor3Abi } from "../src/abis";
1111
import { createViemClient, getDataFetcher } from "../src/config";
1212
import { getRpSwap, PoolBlackList, processLps, sleep } from "../src/utils";
1313
import { HDAccount, mnemonicToAccount, PrivateKeyAccount } from "viem/accounts";

src/abis.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
import { parseAbi } from "viem";
22

3-
/**
4-
* Minimal ABI for ERC20 contract only including Transfer event
5-
*/
6-
export const erc20Abi = [
7-
"event Transfer(address indexed from, address indexed to, uint256 value)",
8-
"function symbol() public view returns (string memory)",
9-
"function transfer(address to, uint256 amount) external returns (bool)",
10-
"function balanceOf(address account) external view returns (uint256)",
11-
"function approve(address spender, uint256 amount) external returns (bool)",
12-
"function allowance(address owner, address spender) external view returns (uint256)",
13-
] as const;
14-
153
// structs used in the orderbook and arb abis
164
export const IO = "(address token, uint8 decimals, uint256 vaultId)" as const;
175
export const EvaluableV3 = "(address interpreter, address store, bytes bytecode)" as const;
@@ -108,6 +96,9 @@ export const DefaultArbEvaluable = {
10896

10997
export const TakeOrderV2EventAbi = parseAbi([orderbookAbi[13]]);
11098
export const OrderbookQuoteAbi = parseAbi([orderbookAbi[14]]);
99+
export const VaultBalanceAbi = parseAbi([orderbookAbi[3]]);
100+
export const DeployerAbi = parseAbi(deployerAbi);
101+
export const MulticallAbi = parseAbi(multicall3Abi);
111102

112103
/**
113104
* Arbitrum node interface address, used to get L1 gas limit.

src/account.ts

Lines changed: 94 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import { ChainId, RPParams } from "sushi";
22
import { BigNumber, ethers } from "ethers";
3+
import { erc20Abi, PublicClient } from "viem";
34
import { estimateGasCost, getTxFee } from "./gas";
45
import { ErrorSeverity, errorSnapshot } from "./error";
56
import { Native, Token, WNATIVE } from "sushi/currency";
67
import { ROUTE_PROCESSOR_4_ADDRESS } from "sushi/config";
7-
import { getRpSwap, PoolBlackList, sleep } from "./utils";
88
import { createViemClient, getDataFetcher } from "./config";
99
import { mnemonicToAccount, privateKeyToAccount } from "viem/accounts";
10-
import { erc20Abi, multicall3Abi, orderbookAbi, routeProcessor3Abi } from "./abis";
10+
import { getRpSwap, PoolBlackList, sleep, addWatchedToken } from "./utils";
1111
import { context, Context, SpanStatusCode, trace, Tracer } from "@opentelemetry/api";
12-
import { parseAbi, hexToNumber, numberToHex, PublicClient, NonceManagerSource } from "viem";
12+
import { MulticallAbi, orderbookAbi, routeProcessor3Abi, VaultBalanceAbi } from "./abis";
1313
import {
1414
BotConfig,
1515
CliOptions,
1616
ViemClient,
1717
OwnedOrder,
1818
TokenDetails,
1919
OperationState,
20+
BundledOrders,
2021
} from "./types";
2122

2223
/** Standard base path for eth accounts */
@@ -434,7 +435,7 @@ export async function getBatchEthBalance(
434435
viemClient.chain?.contracts?.multicall3?.address) as `0x${string}`,
435436
allowFailure: false,
436437
chainId: viemClient.chain.id,
437-
abi: parseAbi(multicall3Abi),
438+
abi: MulticallAbi,
438439
functionName: "getEthBalance",
439440
args: [v],
440441
})),
@@ -464,7 +465,7 @@ export async function getBatchTokenBalanceForAccount(
464465
address: v.address as `0x${string}`,
465466
allowFailure: false,
466467
chainId: viemClient.chain.id,
467-
abi: parseAbi(erc20Abi),
468+
abi: erc20Abi,
468469
functionName: "balanceOf",
469470
args: [address],
470471
})),
@@ -886,25 +887,10 @@ export async function sweepToEth(
886887
}
887888
}
888889

889-
export async function setWatchedTokens(account: ViemClient, watchedTokens: TokenDetails[]) {
890+
export function setWatchedTokens(account: ViemClient, watchedTokens: TokenDetails[]) {
890891
account.BOUNTY = [...watchedTokens];
891892
}
892893

893-
export function addWatchedToken(
894-
token: TokenDetails,
895-
watchedTokens: TokenDetails[],
896-
account?: ViemClient,
897-
) {
898-
if (!watchedTokens.find((v) => v.address.toLowerCase() === token.address.toLowerCase())) {
899-
watchedTokens.push(token);
900-
}
901-
if (account) {
902-
if (!account.BOUNTY.find((v) => v.address.toLowerCase() === token.address.toLowerCase())) {
903-
account.BOUNTY.push(token);
904-
}
905-
}
906-
}
907-
908894
/**
909895
* Funds the sepcified bot owned orders from the gas token
910896
* @param ownedOrders
@@ -1082,35 +1068,93 @@ export async function fundOwnedOrders(
10821068
}
10831069

10841070
/**
1085-
* Creates a viem account nonce source of truth with "latest" block tag used for reading,
1086-
* viem's default nonce manager uses "pending" block tag to read the nonce of an account
1071+
* Quotes order details that are already fetched and bundled by bundleOrder()
1072+
* @param config - Config obj
1073+
* @param orderDetails - Order details to quote
1074+
* @param multicallAddressOverride - Optional multicall address
10871075
*/
1088-
export function noneSource(): NonceManagerSource {
1089-
return {
1090-
async get(parameters) {
1091-
const { address, client } = parameters;
1092-
return getTransactionCount(client as any, {
1093-
address,
1094-
blockTag: "latest",
1076+
export async function checkOwnedOrders(
1077+
config: BotConfig,
1078+
orderDetails: BundledOrders[][],
1079+
multicallAddressOverride?: string,
1080+
): Promise<OwnedOrder[]> {
1081+
const ownedOrders: any[] = [];
1082+
const result: OwnedOrder[] = [];
1083+
orderDetails.flat().forEach((v) => {
1084+
v.takeOrders.forEach((order) => {
1085+
if (
1086+
order.takeOrder.order.owner.toLowerCase() ===
1087+
config.mainAccount.account.address.toLowerCase() &&
1088+
!ownedOrders.find(
1089+
(e) =>
1090+
e.orderbook.toLowerCase() === v.orderbook.toLowerCase() &&
1091+
e.outputToken.toLowerCase() === v.sellToken.toLowerCase() &&
1092+
e.order.takeOrder.order.validOutputs[
1093+
e.order.takeOrder.outputIOIndex
1094+
].token.toLowerCase() ==
1095+
order.takeOrder.order.validOutputs[
1096+
order.takeOrder.outputIOIndex
1097+
].token.toLowerCase() &&
1098+
ethers.BigNumber.from(
1099+
e.order.takeOrder.order.validOutputs[e.order.takeOrder.outputIOIndex]
1100+
.vaultId,
1101+
).eq(
1102+
order.takeOrder.order.validOutputs[order.takeOrder.outputIOIndex]
1103+
.vaultId,
1104+
),
1105+
)
1106+
) {
1107+
ownedOrders.push({
1108+
order,
1109+
orderbook: v.orderbook,
1110+
outputSymbol: v.sellTokenSymbol,
1111+
outputToken: v.sellToken,
1112+
outputDecimals: v.sellTokenDecimals,
1113+
});
1114+
}
1115+
});
1116+
});
1117+
if (!ownedOrders.length) return result;
1118+
try {
1119+
const multicallResult = await config.viemClient.multicall({
1120+
multicallAddress:
1121+
(multicallAddressOverride as `0x${string}` | undefined) ??
1122+
config.viemClient.chain?.contracts?.multicall3?.address,
1123+
allowFailure: false,
1124+
contracts: ownedOrders.map((v) => ({
1125+
address: v.orderbook,
1126+
allowFailure: false,
1127+
chainId: config.chain.id,
1128+
abi: VaultBalanceAbi,
1129+
functionName: "vaultBalance",
1130+
args: [
1131+
// owner
1132+
v.order.takeOrder.order.owner,
1133+
// token
1134+
v.order.takeOrder.order.validOutputs[v.order.takeOrder.outputIOIndex].token,
1135+
// valut id
1136+
v.order.takeOrder.order.validOutputs[v.order.takeOrder.outputIOIndex].vaultId,
1137+
],
1138+
})),
1139+
});
1140+
for (let i = 0; i < multicallResult.length; i++) {
1141+
let vaultId =
1142+
ownedOrders[i].order.takeOrder.order.validOutputs[
1143+
ownedOrders[i].order.takeOrder.outputIOIndex
1144+
].vaultId;
1145+
if (vaultId instanceof BigNumber) vaultId = vaultId.toHexString();
1146+
result.push({
1147+
vaultId,
1148+
id: ownedOrders[i].order.id,
1149+
token: ownedOrders[i].outputToken,
1150+
symbol: ownedOrders[i].outputSymbol,
1151+
decimals: ownedOrders[i].outputDecimals,
1152+
orderbook: ownedOrders[i].orderbook,
1153+
vaultBalance: ethers.BigNumber.from(multicallResult[i]),
10951154
});
1096-
},
1097-
set() {},
1098-
};
1099-
}
1100-
1101-
/**
1102-
* Perfomrs a eth_getTransactionCount rpc request
1103-
*/
1104-
async function getTransactionCount(
1105-
client: any,
1106-
{ address, blockTag = "latest", blockNumber }: any,
1107-
): Promise<number> {
1108-
const count = await client.request(
1109-
{
1110-
method: "eth_getTransactionCount",
1111-
params: [address, blockNumber ? numberToHex(blockNumber) : blockTag],
1112-
},
1113-
{ dedupe: Boolean(blockNumber) },
1114-
);
1115-
return hexToNumber(count);
1155+
}
1156+
} catch (e) {
1157+
/**/
1158+
}
1159+
return result;
11161160
}

src/cli.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ import { Command } from "commander";
55
import { getMetaInfo } from "./config";
66
import { BigNumber, ethers } from "ethers";
77
import { Context } from "@opentelemetry/api";
8+
import { sleep, isBigNumberish } from "./utils";
89
import { getOrderChanges, SgOrder } from "./query";
910
import { Resource } from "@opentelemetry/resources";
1011
import { getOrderDetails, clear, getConfig } from ".";
1112
import { ErrorSeverity, errorSnapshot } from "./error";
1213
import { Tracer } from "@opentelemetry/sdk-trace-base";
13-
import { ProcessPairReportStatus } from "./processOrders";
14-
import { sleep, getOrdersTokens, isBigNumberish } from "./utils";
1514
import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
1615
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
1716
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
18-
import { BotConfig, BundledOrders, CliOptions, OperationState, ViemClient } from "./types";
17+
import {
18+
BotConfig,
19+
CliOptions,
20+
ViemClient,
21+
BundledOrders,
22+
OperationState,
23+
ProcessPairReportStatus,
24+
} from "./types";
1925
import {
2026
sweepToEth,
2127
manageAccounts,
@@ -24,6 +30,7 @@ import {
2430
getBatchEthBalance,
2531
} from "./account";
2632
import {
33+
getOrdersTokens,
2734
downscaleProtection,
2835
prepareOrdersForRound,
2936
getOrderbookOwnersProfileMapFromSg,

0 commit comments

Comments
 (0)