From b424a800d0b47ec722ec825e2eea0bad553aab6f Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Tue, 12 Nov 2024 19:16:54 +0000 Subject: [PATCH 1/4] init --- src/error.ts | 8 ++++---- src/processOrders.ts | 17 +++++++++++++++-- src/types.ts | 1 + 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/error.ts b/src/error.ts index d243a670..02321edd 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,9 +1,9 @@ import { BaseError, RpcRequestError, - InvalidInputRpcError, + // InvalidInputRpcError, ExecutionRevertedError, - TransactionRejectedRpcError, + // TransactionRejectedRpcError, } from "viem"; /** @@ -46,8 +46,8 @@ export function errorSnapshot(header: string, err: any): string { export function containsNodeError(err: BaseError): boolean { try { return ( - err instanceof TransactionRejectedRpcError || - err instanceof InvalidInputRpcError || + // err instanceof TransactionRejectedRpcError || + // err instanceof InvalidInputRpcError || err instanceof ExecutionRevertedError || (err instanceof RpcRequestError && err.code === ExecutionRevertedError.code) || ("cause" in err && containsNodeError(err.cause as any)) diff --git a/src/processOrders.ts b/src/processOrders.ts index 7aa084e7..7f202b82 100644 --- a/src/processOrders.ts +++ b/src/processOrders.ts @@ -619,6 +619,19 @@ export async function processPair(args: { // submit the tx let txhash, txUrl; try { + // const gasPriceBigInt = await viemClient.getGasPrice(); + // const nonce = await viemClient.getTransactionCount({ + // blockTag: "latest", + // address: + // flashbotSigner !== undefined + // ? flashbotSigner.account.address + // : signer.account.address, + // }); + // rawtx.gasPrice = (gasPriceBigInt * 107n) / 100n; + // if (flashbotSigner) { + // rawtx.gas = await flashbotSigner.estimateGas(rawtx); + // } + // rawtx.nonce = nonce; txhash = flashbotSigner !== undefined ? await flashbotSigner.sendTransaction(rawtx) @@ -637,7 +650,7 @@ export async function processPair(args: { }, withBigintSerializer, ); - spanAttributes["txNoneNodeError"] = containsNodeError(e as BaseError); + spanAttributes["txNoneNodeError"] = !containsNodeError(e as BaseError); result.error = e; result.reason = ProcessPairHaltReason.TxFailed; throw result; @@ -775,7 +788,7 @@ export async function processPair(args: { result.report.actualGasCost = ethers.utils.formatUnits(actualGasCost); } result.error = e; - spanAttributes["txNoneNodeError"] = containsNodeError(e); + spanAttributes["txNoneNodeError"] = !containsNodeError(e); result.reason = ProcessPairHaltReason.TxMineFailed; throw result; } diff --git a/src/types.ts b/src/types.ts index f616810d..9a90e067 100644 --- a/src/types.ts +++ b/src/types.ts @@ -183,6 +183,7 @@ export type RawTx = { data: `0x${string}`; gasPrice?: bigint; gas?: bigint; + nonce?: number; }; export type DryrunValue = { From 909c7827847680017a555b7d59d0116ec9717c11 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Tue, 12 Nov 2024 20:02:10 +0000 Subject: [PATCH 2/4] Update account.ts --- src/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account.ts b/src/account.ts index 492f76b6..159a1e68 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1121,5 +1121,5 @@ async function getTransactionCount( }, { dedupe: Boolean(blockNumber) }, ); - return hexToNumber(count); + return hexToNumber(count) - 1; } From 8a20dfbeeacca9ba909ad7cff9cb4406121d3cd8 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Tue, 12 Nov 2024 21:33:04 +0000 Subject: [PATCH 3/4] update --- src/account.ts | 73 +++++++++++++++++++++++----------------- src/error.ts | 2 +- src/processOrders.ts | 19 +++-------- test/account.test.js | 3 ++ test/processPair.test.js | 5 ++- 5 files changed, 55 insertions(+), 47 deletions(-) diff --git a/src/account.ts b/src/account.ts index 159a1e68..5e5b418c 100644 --- a/src/account.ts +++ b/src/account.ts @@ -9,14 +9,7 @@ import { mnemonicToAccount, privateKeyToAccount } from "viem/accounts"; import { erc20Abi, multicall3Abi, orderbookAbi, routeProcessor3Abi } from "./abis"; import { context, Context, SpanStatusCode, trace, Tracer } from "@opentelemetry/api"; import { BotConfig, CliOptions, ViemClient, TokenDetails, OwnedOrder } from "./types"; -import { - parseAbi, - hexToNumber, - numberToHex, - PublicClient, - createNonceManager, - NonceManagerSource, -} from "viem"; +import { parseAbi, hexToNumber, numberToHex, PublicClient, NonceManagerSource } from "viem"; /** Standard base path for eth accounts */ export const BasePath = "m/44'/60'/0'/0/" as const; @@ -47,19 +40,11 @@ export async function initAccounts( isMnemonic ? mnemonicToAccount(mnemonicOrPrivateKey, { addressIndex: MainAccountDerivationIndex, - nonceManager: createNonceManager({ - source: noneSource(), - }), }) : privateKeyToAccount( (mnemonicOrPrivateKey.startsWith("0x") ? mnemonicOrPrivateKey : "0x" + mnemonicOrPrivateKey) as `0x${string}`, - { - nonceManager: createNonceManager({ - source: noneSource(), - }), - }, ), config.timeout, (config as any).testClientViem, @@ -77,9 +62,6 @@ export async function initAccounts( undefined, mnemonicToAccount(mnemonicOrPrivateKey, { addressIndex, - nonceManager: createNonceManager({ - source: noneSource(), - }), }), config.timeout, (config as any).testClientViem, @@ -124,6 +106,7 @@ export async function initAccounts( const hash = await mainAccount.sendTransaction({ to: accounts[i].account.address, value: transferAmount.toBigInt(), + nonce: await getNonce(mainAccount), }); const receipt = await mainAccount.waitForTransactionReceipt({ hash, @@ -230,9 +213,6 @@ export async function manageAccounts( undefined, mnemonicToAccount(options.mnemonic!, { addressIndex: ++lastIndex, - nonceManager: createNonceManager({ - source: noneSource(), - }), }), config.timeout, (config as any).testClientViem, @@ -271,6 +251,7 @@ export async function manageAccounts( const hash = await config.mainAccount.sendTransaction({ to: acc.account.address, value: transferAmount.toBigInt(), + nonce: await getNonce(config.mainAccount), }); const receipt = await config.mainAccount.waitForTransactionReceipt({ hash, @@ -557,10 +538,11 @@ export async function sweepToMainWallet( const hash = await toWallet.sendTransaction({ to: fromWallet.account.address, value: transferAmount.toBigInt(), + nonce: await getNonce(fromWallet), }); const receipt = await toWallet.waitForTransactionReceipt({ hash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(receipt.gasUsed); @@ -599,10 +581,12 @@ export async function sweepToMainWallet( span?.setAttribute("details.tokenAddress", txs[i].bounty.address); span?.setAttribute("details.balance", txs[i].balance); try { + const nonce = await getNonce(fromWallet); + (txs[i].tx as any).nonce = nonce; const hash = await fromWallet.sendTransaction(txs[i].tx); const receipt = await fromWallet.waitForTransactionReceipt({ hash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul(receipt.gasUsed); @@ -655,10 +639,11 @@ export async function sweepToMainWallet( to: toWallet.account.address, value: transferAmount.toBigInt(), gas: gasLimit.toBigInt(), + nonce: await getNonce(fromWallet), }); const receipt = await fromWallet.waitForTransactionReceipt({ hash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul( @@ -803,10 +788,11 @@ export async function sweepToEth(config: BotConfig, tracer?: Tracer, ctx?: Conte rp4Address, balance.mul(100), ]) as `0x${string}`, + nonce: await getNonce(config.mainAccount), }); await config.mainAccount.waitForTransactionReceipt({ hash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); } @@ -843,11 +829,13 @@ export async function sweepToEth(config: BotConfig, tracer?: Tracer, ctx?: Conte span?.end(); continue; } else { + const nonce = await getNonce(config.mainAccount); + (rawtx as any).nonce = nonce; const hash = await config.mainAccount.sendTransaction(rawtx); span?.setAttribute("txHash", hash); const receipt = await config.mainAccount.waitForTransactionReceipt({ hash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); if (receipt.status === "success") { @@ -1004,10 +992,11 @@ export async function fundOwnedOrders( to: rp4Address, value: sellAmount!.toBigInt(), data, + nonce: await getNonce(config.mainAccount), }); const swapReceipt = await config.mainAccount.waitForTransactionReceipt({ hash: swapHash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); const swapTxCost = ethers.BigNumber.from( @@ -1039,11 +1028,12 @@ export async function fundOwnedOrders( ownedOrder.orderbook, topupAmount.mul(20), ]) as `0x${string}`, + nonce: await getNonce(config.mainAccount), }); const approveReceipt = await config.mainAccount.waitForTransactionReceipt({ hash: approveHash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); const approveTxCost = ethers.BigNumber.from( @@ -1064,10 +1054,11 @@ export async function fundOwnedOrders( topupAmount, [], ]) as `0x${string}`, + nonce: await getNonce(config.mainAccount), }); const receipt = await config.mainAccount.waitForTransactionReceipt({ hash, - confirmations: 2, + confirmations: 4, timeout: 100_000, }); const txCost = ethers.BigNumber.from(receipt.effectiveGasPrice).mul( @@ -1121,5 +1112,25 @@ async function getTransactionCount( }, { dedupe: Boolean(blockNumber) }, ); - return hexToNumber(count) - 1; + return hexToNumber(count); +} + +/** + * Get an account's nonce + * @param client - The viem client + * @param address - account address + */ +export async function getNonce(client: ViemClient): Promise { + for (let i = 0; i < 3; i++) { + try { + return await client.getTransactionCount({ + address: client.account.address, + blockTag: "latest", + }); + } catch (error) { + if (i === 2) throw error; + else await sleep((i + 1) * 5000); + } + } + throw "Failed to get account's nonce"; } diff --git a/src/error.ts b/src/error.ts index 02321edd..11063e47 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,8 +1,8 @@ import { BaseError, RpcRequestError, - // InvalidInputRpcError, ExecutionRevertedError, + // InvalidInputRpcError, // TransactionRejectedRpcError, } from "viem"; diff --git a/src/processOrders.ts b/src/processOrders.ts index 7f202b82..f70f1655 100644 --- a/src/processOrders.ts +++ b/src/processOrders.ts @@ -7,8 +7,8 @@ import { arbAbis, orderbookAbi } from "./abis"; import { privateKeyToAccount } from "viem/accounts"; import { BigNumber, Contract, ethers } from "ethers"; import { Tracer } from "@opentelemetry/sdk-trace-base"; -import { fundOwnedOrders, rotateAccounts } from "./account"; import { Context, SpanStatusCode } from "@opentelemetry/api"; +import { fundOwnedOrders, getNonce, rotateAccounts } from "./account"; import { containsNodeError, ErrorSeverity, errorSnapshot } from "./error"; import { Report, @@ -619,19 +619,10 @@ export async function processPair(args: { // submit the tx let txhash, txUrl; try { - // const gasPriceBigInt = await viemClient.getGasPrice(); - // const nonce = await viemClient.getTransactionCount({ - // blockTag: "latest", - // address: - // flashbotSigner !== undefined - // ? flashbotSigner.account.address - // : signer.account.address, - // }); - // rawtx.gasPrice = (gasPriceBigInt * 107n) / 100n; - // if (flashbotSigner) { - // rawtx.gas = await flashbotSigner.estimateGas(rawtx); - // } - // rawtx.nonce = nonce; + rawtx.nonce = await getNonce(flashbotSigner ? flashbotSigner : signer); + if (flashbotSigner) { + rawtx.gas = undefined; + } txhash = flashbotSigner !== undefined ? await flashbotSigner.sendTransaction(rawtx) diff --git a/test/account.test.js b/test/account.test.js index 5deb07a9..c4d0ecb3 100644 --- a/test/account.test.js +++ b/test/account.test.js @@ -275,6 +275,7 @@ describe("Test accounts", async function () { estimateGas: async () => 25n, getBalance: async () => 10000n, sendTransaction: async () => "0x1234", + getTransactionCount: async () => 0, waitForTransactionReceipt: async () => ({ status: "success", effectiveGasPrice: ethers.BigNumber.from(5), @@ -292,6 +293,7 @@ describe("Test accounts", async function () { viemClient: { chain: { id: chainId }, call: async () => ({ data: `0x${"1" + "0".repeat(18)}` }), + getTransactionCount: async () => 0, }, }; @@ -356,6 +358,7 @@ describe("Test accounts", async function () { estimateGas: async () => 25n, getBalance: async () => 10000n, sendTransaction: async () => "0x1234", + getTransactionCount: async () => 0, call: async () => ({ data: "0x00" }), waitForTransactionReceipt: async () => ({ status: "success", diff --git a/test/processPair.test.js b/test/processPair.test.js index f63152b3..fa0833b9 100644 --- a/test/processPair.test.js +++ b/test/processPair.test.js @@ -56,6 +56,7 @@ describe("Test process pair", async function () { getGasPrice: async () => gasPrice, estimateGas: async () => gasLimitEstimation, sendTransaction: async () => txHash, + getTransactionCount: async () => 0, waitForTransactionReceipt: async () => { return { status: "success", @@ -75,6 +76,7 @@ describe("Test process pair", async function () { multicall: async () => [vaultBalance.toBigInt()], getGasPrice: async () => gasPrice.toBigInt(), getBlockNumber: async () => 123456n, + getTransactionCount: async () => 0, waitForTransactionReceipt: async () => { return { status: "success", @@ -514,6 +516,7 @@ describe("Test process pair", async function () { to: arb.address, gasPrice: gasPrice.mul(107).div(100).toString(), gas: gasLimitEstimation.toString(), + nonce: 0, from: signer.account.address, }; const expected = { @@ -543,7 +546,7 @@ describe("Test process pair", async function () { "details.outputToEthPrice": "1", "details.marketQuote.num": 0.99699, "details.marketQuote.str": "0.99699", - txNoneNodeError: false, + txNoneNodeError: true, "details.quote": JSON.stringify({ maxOutput: formatUnits(vaultBalance), ratio: formatUnits(ethers.constants.Zero), From bcc01481e006ed4ebd3aba5d46c9f01a4cdff303 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Tue, 12 Nov 2024 21:59:15 +0000 Subject: [PATCH 4/4] Update processOrders.ts --- src/processOrders.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/processOrders.ts b/src/processOrders.ts index f70f1655..21dcfc26 100644 --- a/src/processOrders.ts +++ b/src/processOrders.ts @@ -619,8 +619,8 @@ export async function processPair(args: { // submit the tx let txhash, txUrl; try { - rawtx.nonce = await getNonce(flashbotSigner ? flashbotSigner : signer); - if (flashbotSigner) { + rawtx.nonce = await getNonce(flashbotSigner !== undefined ? flashbotSigner : signer); + if (flashbotSigner !== undefined) { rawtx.gas = undefined; } txhash =