Skip to content

Commit 6297325

Browse files
authored
Merge pull request #270 from rainlanguage/2024-12-02-l1-gas
L1 gas
2 parents a74635d + bb85a07 commit 6297325

23 files changed

+471
-204
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@typescript-eslint/no-non-null-assertion": "off",
2727
"@typescript-eslint/no-var-requires": "off",
2828
"@typescript-eslint/no-empty-function": "off",
29+
"@typescript-eslint/no-namespace": "off",
2930
"semi": [ "error", "always" ],
3031
"prettier/prettier": [
3132
"error",

package-lock.json

Lines changed: 14 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,16 @@
6161
"@nomicfoundation/hardhat-viem": "^2.0.2",
6262
"@nomiclabs/hardhat-ethers": "^2.0.0",
6363
"@nomiclabs/hardhat-waffle": "2.0.3",
64+
"@types/chai": "^4.2.0",
6465
"@types/mocha": "^10.0.0",
6566
"@types/node": "^18.13.0",
6667
"@typescript-eslint/eslint-plugin": "^5.42.0",
6768
"@typescript-eslint/parser": "^5.42.0",
6869
"chai": "^4.3.6",
6970
"copyfiles": "^2.4.1",
71+
"eslint": "^8.26.0",
7072
"eslint-config-prettier": "^9.1.0",
7173
"eslint-plugin-prettier": "^5.1.1",
72-
"eslint": "^8.26.0",
7374
"hardhat": "^2.17.8",
7475
"hardhat-contract-sizer": "^2.1.1",
7576
"hardhat-gas-reporter": "^1.0.4",
@@ -78,8 +79,8 @@
7879
"jsdoc-to-markdown": "^7.1.1",
7980
"mocha": "^10.1.0",
8081
"mockttp": "^3.12.0",
81-
"rimraf": "^5.0.0",
8282
"prettier": "^3.1.1",
83+
"rimraf": "^5.0.0",
8384
"ts-node": "^10.9.1",
8485
"typescript": "^5.0.4"
8586
}

src/account.ts

Lines changed: 51 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ChainId, RPParams } from "sushi";
22
import { BigNumber, ethers } from "ethers";
3+
import { estimateGasCost, getTxFee } from "./gas";
34
import { ErrorSeverity, errorSnapshot } from "./error";
45
import { Native, Token, WNATIVE } from "sushi/currency";
56
import { ROUTE_PROCESSOR_4_ADDRESS } from "sushi/config";
@@ -8,8 +9,15 @@ import { createViemClient, getDataFetcher } from "./config";
89
import { mnemonicToAccount, privateKeyToAccount } from "viem/accounts";
910
import { erc20Abi, multicall3Abi, orderbookAbi, routeProcessor3Abi } from "./abis";
1011
import { context, Context, SpanStatusCode, trace, Tracer } from "@opentelemetry/api";
11-
import { BotConfig, CliOptions, ViemClient, TokenDetails, OwnedOrder } from "./types";
1212
import { parseAbi, hexToNumber, numberToHex, PublicClient, NonceManagerSource } from "viem";
13+
import {
14+
BotConfig,
15+
CliOptions,
16+
ViemClient,
17+
OwnedOrder,
18+
TokenDetails,
19+
OperationState,
20+
} from "./types";
1321

1422
/** Standard base path for eth accounts */
1523
export const BasePath = "m/44'/60'/0'/0/" as const;
@@ -112,9 +120,7 @@ export async function initAccounts(
112120
confirmations: 4,
113121
timeout: 100_000,
114122
});
115-
const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(
116-
receipt.gasUsed,
117-
);
123+
const txCost = ethers.BigNumber.from(getTxFee(receipt, config));
118124
if (receipt.status === "success") {
119125
accounts[i].BALANCE = topupAmountBn;
120126
mainAccount.BALANCE =
@@ -151,6 +157,7 @@ export async function manageAccounts(
151157
avgGasCost: BigNumber,
152158
lastIndex: number,
153159
wgc: ViemClient[],
160+
state: OperationState,
154161
tracer?: Tracer,
155162
ctx?: Context,
156163
) {
@@ -159,11 +166,11 @@ export async function manageAccounts(
159166
for (let i = config.accounts.length - 1; i >= 0; i--) {
160167
if (config.accounts[i].BALANCE.lt(avgGasCost.mul(4))) {
161168
try {
162-
const gasPrice = await config.viemClient.getGasPrice();
163169
await sweepToMainWallet(
164170
config.accounts[i],
165171
config.mainAccount,
166-
gasPrice,
172+
state,
173+
config,
167174
tracer,
168175
ctx,
169176
);
@@ -256,9 +263,7 @@ export async function manageAccounts(
256263
confirmations: 4,
257264
timeout: 100_000,
258265
});
259-
const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(
260-
receipt.gasUsed,
261-
);
266+
const txCost = ethers.BigNumber.from(getTxFee(receipt, config));
262267
if (receipt.status === "success") {
263268
accountsToAdd--;
264269
acc.BALANCE = topupAmountBN;
@@ -476,10 +481,12 @@ export async function getBatchTokenBalanceForAccount(
476481
export async function sweepToMainWallet(
477482
fromWallet: ViemClient,
478483
toWallet: ViemClient,
479-
gasPrice: bigint,
484+
state: OperationState,
485+
config: BotConfig,
480486
tracer?: Tracer,
481487
ctx?: Context,
482488
) {
489+
const gasPrice = state.gasPrice;
483490
const mainSpan = tracer?.startSpan("sweep-wallet-funds", undefined, ctx);
484491
const mainCtx = mainSpan ? trace.setSpan(context.active(), mainSpan) : undefined;
485492
mainSpan?.setAttribute("details.wallet", fromWallet.account.address);
@@ -488,7 +495,6 @@ export async function sweepToMainWallet(
488495
fromWallet.BOUNTY.map((v) => v.symbol),
489496
);
490497

491-
gasPrice = ethers.BigNumber.from(gasPrice).mul(107).div(100).toBigInt();
492498
const erc20 = new ethers.utils.Interface(erc20Abi);
493499
const txs: {
494500
bounty: TokenDetails;
@@ -499,7 +505,7 @@ export async function sweepToMainWallet(
499505
};
500506
}[] = [];
501507
const failedBounties: TokenDetails[] = [];
502-
let cumulativeGasLimit = ethers.constants.Zero;
508+
let cumulativeGas = ethers.constants.Zero;
503509
for (let i = 0; i < fromWallet.BOUNTY.length; i++) {
504510
const bounty = fromWallet.BOUNTY[i];
505511
try {
@@ -517,29 +523,28 @@ export async function sweepToMainWallet(
517523
continue;
518524
}
519525
const tx = {
526+
gasPrice,
520527
to: bounty.address as `0x${string}`,
521528
data: erc20.encodeFunctionData("transfer", [
522529
toWallet.account.address,
523530
balance,
524531
]) as `0x${string}`,
525532
};
526-
const gas = await fromWallet.estimateGas(tx);
533+
// const gas = await fromWallet.estimateGas(tx);
534+
const gas = (await estimateGasCost(tx, fromWallet, config, state.l1GasPrice))
535+
.totalGasCost;
527536
txs.push({ tx, bounty, balance: ethers.utils.formatUnits(balance, bounty.decimals) });
528-
cumulativeGasLimit = cumulativeGasLimit.add(gas);
537+
cumulativeGas = cumulativeGas.add(gas);
529538
} catch {
530539
addWatchedToken(bounty, failedBounties);
531540
}
532541
}
533542

534-
if (cumulativeGasLimit.mul(gasPrice).mul(125).div(100).gt(fromWallet.BALANCE)) {
543+
if (cumulativeGas.mul(125).div(100).gt(fromWallet.BALANCE)) {
535544
const span = tracer?.startSpan("fund-wallet-to-sweep", undefined, mainCtx);
536545
span?.setAttribute("details.wallet", fromWallet.account.address);
537546
try {
538-
const transferAmount = cumulativeGasLimit
539-
.mul(gasPrice)
540-
.mul(125)
541-
.div(100)
542-
.sub(fromWallet.BALANCE);
547+
const transferAmount = cumulativeGas.mul(125).div(100).sub(fromWallet.BALANCE);
543548
span?.setAttribute("details.amount", ethers.utils.formatUnits(transferAmount));
544549
const hash = await toWallet.sendTx({
545550
to: fromWallet.account.address,
@@ -550,7 +555,7 @@ export async function sweepToMainWallet(
550555
confirmations: 4,
551556
timeout: 100_000,
552557
});
553-
const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(receipt.gasUsed);
558+
const txCost = ethers.BigNumber.from(getTxFee(receipt, config));
554559
if (receipt.status === "success") {
555560
span?.setStatus({
556561
code: SpanStatusCode.OK,
@@ -592,7 +597,7 @@ export async function sweepToMainWallet(
592597
confirmations: 4,
593598
timeout: 100_000,
594599
});
595-
const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(receipt.gasUsed);
600+
const txCost = ethers.BigNumber.from(getTxFee(receipt, config));
596601
if (receipt.status === "success") {
597602
if (!toWallet.BOUNTY.find((v) => v.address === txs[i].bounty.address)) {
598603
toWallet.BOUNTY.push(txs[i].bounty);
@@ -626,32 +631,31 @@ export async function sweepToMainWallet(
626631
const span = tracer?.startSpan("sweep-remaining-gas-to-main-wallet", undefined, mainCtx);
627632
span?.setAttribute("details.wallet", fromWallet.account.address);
628633
try {
629-
const gasLimit = ethers.BigNumber.from(
630-
await fromWallet.estimateGas({
631-
to: toWallet.account.address,
632-
value: 0n,
633-
}),
634+
const estimation = await estimateGasCost(
635+
{ to: toWallet.account.address, value: 0n, gasPrice } as any,
636+
fromWallet,
637+
config,
638+
state.l1GasPrice,
634639
);
640+
635641
const remainingGas = ethers.BigNumber.from(
636642
await fromWallet.getBalance({ address: fromWallet.account.address }),
637643
);
638-
const transferAmount = remainingGas.sub(gasLimit.mul(gasPrice));
644+
const transferAmount = remainingGas.sub(estimation.totalGasCost);
639645
if (transferAmount.gt(0)) {
640646
span?.setAttribute("details.amount", ethers.utils.formatUnits(transferAmount));
641647
const hash = await fromWallet.sendTx({
642648
gasPrice,
643649
to: toWallet.account.address,
644650
value: transferAmount.toBigInt(),
645-
gas: gasLimit.toBigInt(),
651+
gas: estimation.gas,
646652
});
647653
const receipt = await fromWallet.waitForTransactionReceipt({
648654
hash,
649655
confirmations: 4,
650656
timeout: 100_000,
651657
});
652-
const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(
653-
receipt.gasUsed,
654-
);
658+
const txCost = ethers.BigNumber.from(getTxFee(receipt, config));
655659
if (receipt.status === "success") {
656660
toWallet.BALANCE = toWallet.BALANCE.add(transferAmount);
657661
fromWallet.BALANCE = fromWallet.BALANCE.sub(txCost).sub(transferAmount);
@@ -691,16 +695,19 @@ export async function sweepToMainWallet(
691695
* Sweep bot's bounties to eth
692696
* @param config - The config obj
693697
*/
694-
export async function sweepToEth(config: BotConfig, tracer?: Tracer, ctx?: Context) {
698+
export async function sweepToEth(
699+
config: BotConfig,
700+
state: OperationState,
701+
tracer?: Tracer,
702+
ctx?: Context,
703+
) {
695704
const skipped: TokenDetails[] = [];
696705
const rp4Address = ROUTE_PROCESSOR_4_ADDRESS[
697706
config.chain.id as keyof typeof ROUTE_PROCESSOR_4_ADDRESS
698707
] as `0x${string}`;
699708
const rp = new ethers.utils.Interface(routeProcessor3Abi);
700709
const erc20 = new ethers.utils.Interface(erc20Abi);
701-
const gasPrice = ethers.BigNumber.from(await config.mainAccount.getGasPrice())
702-
.mul(107)
703-
.div(100);
710+
const gasPrice = ethers.BigNumber.from(state.gasPrice);
704711
for (let i = 0; i < config.mainAccount.BOUNTY.length; i++) {
705712
const bounty = config.mainAccount.BOUNTY[i];
706713
const span = tracer?.startSpan("sweep-to-gas", undefined, ctx);
@@ -906,30 +913,15 @@ export function addWatchedToken(
906913
export async function fundOwnedOrders(
907914
ownedOrders: OwnedOrder[],
908915
config: BotConfig,
916+
state: OperationState,
909917
): Promise<{ ownedOrder?: OwnedOrder; error: string }[]> {
910918
const failedFundings: { ownedOrder?: OwnedOrder; error: string }[] = [];
911919
const ob = new ethers.utils.Interface(orderbookAbi);
912920
const erc20 = new ethers.utils.Interface(erc20Abi);
913921
const rp = new ethers.utils.Interface(routeProcessor3Abi);
914922
const rp4Address =
915923
ROUTE_PROCESSOR_4_ADDRESS[config.chain.id as keyof typeof ROUTE_PROCESSOR_4_ADDRESS];
916-
let gasPrice: BigNumber;
917-
for (let i = 0; i < 4; i++) {
918-
try {
919-
gasPrice = ethers.BigNumber.from(await config.viemClient.getGasPrice())
920-
.mul(107)
921-
.div(100);
922-
break;
923-
} catch (e) {
924-
if (i == 3)
925-
return [
926-
{
927-
error: errorSnapshot("failed to get gas price", e),
928-
},
929-
];
930-
else await sleep(10000 * (i + 1));
931-
}
932-
}
924+
const gasPrice = ethers.BigNumber.from(state.gasPrice);
933925
if (config.selfFundOrders) {
934926
for (let i = 0; i < ownedOrders.length; i++) {
935927
const ownedOrder = ownedOrders[i];
@@ -973,7 +965,7 @@ export async function fundOwnedOrders(
973965
config.mainAccount.account.address,
974966
rp4Address,
975967
config.dataFetcher,
976-
gasPrice!,
968+
gasPrice,
977969
);
978970
const initSellAmount = ethers.BigNumber.from(route.amountOutBI);
979971
let sellAmount: BigNumber;
@@ -988,7 +980,7 @@ export async function fundOwnedOrders(
988980
config.mainAccount.account.address,
989981
rp4Address,
990982
config.dataFetcher,
991-
gasPrice!,
983+
gasPrice,
992984
);
993985
if (topupAmount.lte(route.amountOutBI)) {
994986
finalRpParams = rpParams;
@@ -1013,9 +1005,7 @@ export async function fundOwnedOrders(
10131005
confirmations: 4,
10141006
timeout: 100_000,
10151007
});
1016-
const swapTxCost = ethers.BigNumber.from(
1017-
swapReceipt.effectiveGasPrice,
1018-
).mul(swapReceipt.gasUsed);
1008+
const swapTxCost = ethers.BigNumber.from(getTxFee(swapReceipt, config));
10191009
config.mainAccount.BALANCE = config.mainAccount.BALANCE.sub(swapTxCost);
10201010
if (swapReceipt.status === "success") {
10211011
config.mainAccount.BALANCE = config.mainAccount.BALANCE.sub(
@@ -1050,8 +1040,8 @@ export async function fundOwnedOrders(
10501040
timeout: 100_000,
10511041
});
10521042
const approveTxCost = ethers.BigNumber.from(
1053-
approveReceipt.effectiveGasPrice,
1054-
).mul(approveReceipt.gasUsed);
1043+
getTxFee(approveReceipt, config),
1044+
);
10551045
config.mainAccount.BALANCE =
10561046
config.mainAccount.BALANCE.sub(approveTxCost);
10571047
if (approveReceipt.status === "reverted") {
@@ -1073,9 +1063,7 @@ export async function fundOwnedOrders(
10731063
confirmations: 4,
10741064
timeout: 100_000,
10751065
});
1076-
const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(
1077-
receipt.gasUsed,
1078-
);
1066+
const txCost = ethers.BigNumber.from(getTxFee(receipt, config));
10791067
config.mainAccount.BALANCE = config.mainAccount.BALANCE.sub(txCost);
10801068
if (receipt.status === "success") {
10811069
ownedOrder.vaultBalance = ownedOrder.vaultBalance.add(topupAmount);

0 commit comments

Comments
 (0)