diff --git a/config/default.json b/config/default.json index 57a08476..45033f5b 100644 --- a/config/default.json +++ b/config/default.json @@ -16,7 +16,9 @@ 196, 2810, 997, 713715, 3799, 167009, 80084, 5845, 167000, 1328, 1329, 995, 28882, 288, 920637907288165, 1740, 1750, 4202, 1135, 2818 ], - "supportedNetworksV07": [84532, 8453, 10, 11155420], + "supportedNetworksV07": [ + 84532, 8453, 10, 11155420, 56, 42161, 137, 100, 80084 + ], "EIP1559SupportedNetworks": [ 1, 137, 42161, 10, 43114, 43113, 8453, 59144, 204, 5611, 421614, 11155111, 84532, 168587773, 81457, 42170, 169, 56400, 11155420, 80002, 27827, 4653, @@ -32,7 +34,7 @@ "supportedNetworks": [1, 11155111], "confidenceLevel": 99 }, - "supportsDebugTraceCall": [1, 11155111], + "supportsDebugTraceCall": [], "supportedTransactionType": { "1": ["BUNDLER"], "137": ["BUNDLER"], @@ -1023,7 +1025,9 @@ }, "entryPointV07Data": { "0x0000000071727De22E5E9d8BAf0edAc6f37da032": { - "supportedChainIds": [84532, 8453, 10, 11155420] + "supportedChainIds": [ + 84532, 8453, 10, 11155420, 42161, 56, 42161, 137, 100, 80084 + ] } }, "zeroAddress": "0x0000000000000000000000000000000000000000", diff --git a/package.json b/package.json index 7fb8f687..7a141e8c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "@arbitrum/sdk": "^3.1.3", + "@biconomy/gas-estimations": "0.2.51", "@slack/web-api": "^6.8.0", "@types/config": "^3.3.3", "@types/crypto-js": "^4.1.1", @@ -33,7 +34,6 @@ "crypto-js": "^4.1.1", "dd-trace": "^4.17.0", "dotenv": "^16.0.3", - "entry-point-gas-estimations": "1.0.2", "ethereumjs-util": "^7.1.5", "express": "^4.18.2", "express-handlebars": "^6.0.6", @@ -59,11 +59,13 @@ "rest-api-errors": "^1.2.5", "serialize-error": "8.1.0", "typescript": "^5.3.3", - "viem": "^2.21.28" + "viem": "^2.21.49" }, "devDependencies": { "@biconomy/account": "^4.5.7", + "@biconomy/sdk": "^0.0.19", "@eslint/js": "^9.13.0", + "@rhinestone/module-sdk": "^0.1.32", "@types/amqplib": "^0.8.2", "@types/big.js": "^6.1.5", "@types/consolidate": "^0.14.1", diff --git a/src/admin-scripts/getRequiredPrefund.ts b/src/admin-scripts/getRequiredPrefund.ts index 1b1d1349..34f7d3a6 100644 --- a/src/admin-scripts/getRequiredPrefund.ts +++ b/src/admin-scripts/getRequiredPrefund.ts @@ -1,5 +1,6 @@ - -function getRequiredPrefund(userOp: { +import { formatEther } from "viem"; + +export function getRequiredPrefund(userOp: { paymasterAndData: string; callGasLimit: bigint; verificationGasLimit: bigint; @@ -17,11 +18,13 @@ function getRequiredPrefund(userOp: { } const prefund = getRequiredPrefund({ - paymasterAndData: "0x", - callGasLimit: 651983n, - verificationGasLimit: 1459741n, - preVerificationGas: 313507n, - maxFeePerGas: 15796905452n, + paymasterAndData: + "0x00000f79b7faf42eebadba19acc07cd08af447890000000000000000000000005430eac2228c12577ed5179a4dee3aaa56a238a600000000000000000000000000000000000000000000000000000000676469c800000000000000000000000000000000000000000000000000000000676462c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413824432901286f0f7794774baa5b55728fdccee0a7884049992c7f82471299477f978db2c8ce464433153e134f39af1a265406475b104bef19aa9cd2758e90da1c00000000000000000000000000000000000000000000000000000000000000", + callGasLimit: 24276n, + verificationGasLimit: 297166n, + preVerificationGas: 493353887n, + maxFeePerGas: 16261619n, }); -console.log(`Required prefund: ${prefund} wei`); +console.log(`Required prefund: ${formatEther(prefund)} ETH`); +// old pvg: 523923235734 diff --git a/src/common/constants/index.ts b/src/common/constants/index.ts index db28d69a..31e10d0a 100644 --- a/src/common/constants/index.ts +++ b/src/common/constants/index.ts @@ -50,6 +50,7 @@ export enum BLOCKCHAINS { MORPH_MAINNET = 2818, MORPH_HOLESKY_TESTNET = 2810, FIRECHAIN_TESTNET = 997, + FIRECHAIN_MAINNET = 995, SEI_DEVNET_ARCTIC_1 = 713715, SEI_TESTNET_ATLANTIC_2 = 1328, SEI_MAINNET = 1329, @@ -63,4 +64,7 @@ export enum BLOCKCHAINS { METAL_L2_MAINNET = 1750, LISK_TESTNET = 4202, LISK_MAINNET = 1135, + BOBA_SEPOLIA_TESTNET = 28882, + BOBA_MAINNET = 288, + MOONBEAM_MAINNET = 1284, } diff --git a/src/common/service-manager/index.ts b/src/common/service-manager/index.ts index e33db589..522b8fcd 100644 --- a/src/common/service-manager/index.ts +++ b/src/common/service-manager/index.ts @@ -1,7 +1,6 @@ import nodeconfig from "config"; import { getContract, parseEther } from "viem"; import { chain } from "lodash"; -import { ENTRY_POINT_ABI } from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6"; import { config } from "../../config"; import { EVMAccount, IEVMAccount } from "../../relayer/account"; import { BundlerConsumer } from "../../relayer/consumer"; @@ -45,8 +44,9 @@ import { UserOperationStateDAO } from "../db/dao/UserOperationStateDAO"; import { customJSONStringify, parseError } from "../utils"; import { GasPriceService } from "../gas-price"; import { CacheFeesJob } from "../gas-price/jobs/CacheFees"; -import { ENTRY_POINT_V07_ABI } from "../entrypoint-v7/abiv7"; import { FlashbotsClient } from "../network/FlashbotsClient"; +import { ENTRYPOINT_V6_ABI } from "@biconomy/gas-estimations"; +import { ENTRYPOINT_V7_ABI } from "@biconomy/gas-estimations"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -358,7 +358,7 @@ async function setupNetwork( entryPointMap[chainId].push({ address: entryPointAddress, entryPointContract: getContract({ - abi: ENTRY_POINT_ABI, + abi: ENTRYPOINT_V6_ABI, address: entryPointAddress as `0x${string}`, client: { public: networkService.provider, @@ -376,7 +376,7 @@ async function setupNetwork( entryPointMapV07[chainId].push({ address: entryPointAddress, entryPointContract: getContract({ - abi: ENTRY_POINT_V07_ABI, + abi: ENTRYPOINT_V7_ABI, address: entryPointAddress as `0x${string}`, client: { public: networkService.provider, diff --git a/src/common/simulation/BundlerSimulationService.test.ts b/src/common/simulation/BundlerSimulationService.test.ts deleted file mode 100644 index c633cf25..00000000 --- a/src/common/simulation/BundlerSimulationService.test.ts +++ /dev/null @@ -1,157 +0,0 @@ -it.todo("fixme"); -// import { GasEstimator } from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6/GasEstimator/GasEstimator"; -// import { config } from "../../config"; -// import { GasPriceService } from "../gas-price"; -// import { EVMNetworkService } from "../network"; -// import { UserOperationType } from "../types"; -// import { BundlerSimulationService } from "./BundlerSimulationService"; -// import RpcError from "../utils/rpc-error"; - -// describe("BundlerSimulationService", () => { -// const networkService = new EVMNetworkService({ -// chainId: 137, -// rpcUrl: "https://random-rpc-url.com", -// }); -// const gasPriceService = {} as unknown as GasPriceService; -// const bundlerSimulationService = new BundlerSimulationService( -// networkService, -// gasPriceService, -// ); - -// describe("user op rejection", () => { -// it("user op has insufficient max priority fee per gas", async () => { -// bundlerSimulationService.gasEstimator = { -// calculatePreVerificationGas: jest.fn().mockResolvedValue(50000n), -// } as unknown as GasEstimator; -// bundlerSimulationService.gasPriceService = { -// getBaseFeePerGas: jest.fn().mockResolvedValue(50000n), -// } as unknown as GasPriceService; -// const networkMaxFeePerGas = 10n; -// const networkMaxPriorityFeePerGas = 10n; - -// const userOp: UserOperationType = { -// sender: "0xabc", -// nonce: 1n, -// initCode: "0x", -// callData: "0xdef", -// paymasterAndData: "0x", -// callGasLimit: 5000n, -// verificationGasLimit: 5000n, -// preVerificationGas: 5000n, -// maxPriorityFeePerGas: 1n, -// maxFeePerGas: 10n, -// signature: "0xsignature", -// }; - -// try { -// await bundlerSimulationService.checkUserOperationForRejection({ -// userOp, -// networkMaxFeePerGas, -// networkMaxPriorityFeePerGas, -// }); -// } catch (error) { -// expect((error as RpcError).message).toEqual( -// `maxPriorityFeePerGas in userOp: ${userOp.maxPriorityFeePerGas} is lower than expected maxPriorityFeePerGas: ${Number(networkMaxPriorityFeePerGas) * config.maxPriorityFeePerGasThresholdPercentage}`, -// ); -// } -// }); - -// it("user op has insufficient max fee per gas", async () => { -// bundlerSimulationService.gasEstimator = { -// calculatePreVerificationGas: jest.fn().mockResolvedValue(50000n), -// } as unknown as GasEstimator; - -// const networkMaxFeePerGas = 10n; -// const networkMaxPriorityFeePerGas = 10n; - -// const userOp: UserOperationType = { -// sender: "0xabc", -// nonce: 1n, -// initCode: "0x", -// callData: "0xdef", -// paymasterAndData: "0x", -// callGasLimit: 5000n, -// verificationGasLimit: 5000n, -// preVerificationGas: 5000n, -// maxPriorityFeePerGas: 20n, -// maxFeePerGas: 1n, -// signature: "0xsignature", -// }; -// try { -// await bundlerSimulationService.checkUserOperationForRejection({ -// userOp, -// networkMaxFeePerGas, -// networkMaxPriorityFeePerGas, -// }); -// } catch (error) { -// expect((error as RpcError).message).toEqual( -// `maxFeePerGas in userOp: ${userOp.maxFeePerGas} is lower than expected maxFeePerGas: ${Number(networkMaxFeePerGas) * config.maxFeePerGasThresholdPercentage}`, -// ); -// } -// }); - -// it("user op has insufficient preVerificationGas", async () => { -// bundlerSimulationService.gasEstimator = { -// calculatePreVerificationGas: jest.fn().mockResolvedValue(50000n), -// } as unknown as GasEstimator; - -// const networkMaxFeePerGas = 1n; -// const networkMaxPriorityFeePerGas = 1n; - -// const userOp: UserOperationType = { -// sender: "0xabc", -// nonce: 1n, -// initCode: "0x", -// callData: "0xdef", -// paymasterAndData: "0x", -// callGasLimit: 5000n, -// verificationGasLimit: 5000n, -// preVerificationGas: 5000n, -// maxPriorityFeePerGas: 10n, -// maxFeePerGas: 10n, -// signature: "0xsignature", -// }; -// try { -// bundlerSimulationService.checkUserOperationForRejection({ -// userOp, -// networkMaxFeePerGas, -// networkMaxPriorityFeePerGas, -// }); -// } catch (error) { -// expect((error as RpcError).message).toEqual( -// `preVerificationGas in userOp: ${userOp.preVerificationGas} is lower than expected preVerificationGas: ${50000 * config.preVerificationGasThresholdPercentage}`, -// ); -// } -// }); - -// it("user op has sufficient max fee values and preVerificationGas", async () => { -// bundlerSimulationService.gasEstimator = { -// calculatePreVerificationGas: jest.fn().mockResolvedValue(5n), -// } as unknown as GasEstimator; - -// const networkMaxFeePerGas = 1n; -// const networkMaxPriorityFeePerGas = 1n; - -// const userOp: UserOperationType = { -// sender: "0xabc", -// nonce: 1n, -// initCode: "0x", -// callData: "0xdef", -// paymasterAndData: "0x", -// callGasLimit: 5000n, -// verificationGasLimit: 5000n, -// preVerificationGas: 5000n, -// maxPriorityFeePerGas: 10n, -// maxFeePerGas: 10n, -// signature: "0xsignature", -// }; -// const response = -// await bundlerSimulationService.checkUserOperationForRejection({ -// userOp, -// networkMaxFeePerGas, -// networkMaxPriorityFeePerGas, -// }); -// expect(response).toBe(true); -// }); -// }); -// }); diff --git a/src/common/simulation/BundlerSimulationService.ts b/src/common/simulation/BundlerSimulationService.ts index 87969166..bafa953b 100644 --- a/src/common/simulation/BundlerSimulationService.ts +++ b/src/common/simulation/BundlerSimulationService.ts @@ -1,18 +1,15 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable prefer-const */ import { - Address, decodeErrorResult, encodeAbiParameters, encodeFunctionData, - Hex, keccak256, parseAbiParameters, toHex, zeroAddress, } from "viem"; import nodeconfig from "config"; -import { IGasEstimator } from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6"; import { config } from "../../config"; import { IEVMAccount } from "../../relayer/account"; import { @@ -40,7 +37,11 @@ import { } from "./types"; import { BLOCKCHAINS } from "../constants"; import { IGasPriceService } from "../gas-price"; -import { GasEstimationsAdapter } from "./GasEstimationsWrapper"; +import { UserOperationV6 } from "@biconomy/gas-estimations"; +import { GasEstimator } from "@biconomy/gas-estimations"; +import { createGasEstimator } from "@biconomy/gas-estimations"; +import { isEstimateUserOperationGasResultV6 } from "@biconomy/gas-estimations"; +import { CallTracerResult, findErrorsInTrace } from "./trace"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -73,9 +74,9 @@ export class BundlerSimulationService { constructor({ networkService, gasPriceService, - gasEstimator = new GasEstimationsAdapter({ + gasEstimator = createGasEstimator({ chainId: networkService.chainId, - rpcUrl: networkService.rpcUrl, + rpc: networkService.provider, }), }: IBundlerSimulationServiceOptions) { this.networkService = networkService; @@ -87,14 +88,17 @@ export class BundlerSimulationService { * Used for pretty printing the service configuration * @returns JSON stringified object with API keys removed */ - toJSON(): Omit<IBundlerSimulationServiceOptions, "gasPriceService"> { + toJSON(): Omit< + IBundlerSimulationServiceOptions, + "gasPriceService" | "newGasEstimator" + > { return { networkService: this.networkService, gasEstimator: this.gasEstimator, - // TODO: add the gasPriceService after you write it's toJSON method }; } + // TODO: Add option to pass an entrypoint address to the new gas estimator async estimateUserOperationGas( estimateUserOperationGasData: EstimateUserOperationGasDataType, ): Promise<EstimateUserOperationGasReturnType> { @@ -102,8 +106,6 @@ export class BundlerSimulationService { const { userOp, entryPointContract, chainId, stateOverrideSet } = estimateUserOperationGasData; - this.gasEstimator.setEntryPointAddress(entryPointContract.address); - const start = performance.now(); log.info( `userOp received: ${customJSONStringify( @@ -123,55 +125,14 @@ export class BundlerSimulationService { "0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b"; } - // for userOp completeness - if ( - !userOp.maxFeePerGas || - userOp.maxFeePerGas === BigInt(0) || - (userOp.maxFeePerGas as unknown as string) === "0x" || - (userOp.maxFeePerGas as unknown as string) === "0" - ) { - // setting a non zero value as division with maxFeePerGas will happen - userOp.maxFeePerGas = BigInt(1); - if ( - config.optimismNetworks.includes(chainId) || - config.mantleNetworks.includes(chainId) - ) { - const gasPrice = await this.gasPriceService.getGasPrice(); - if (typeof gasPrice === "bigint") { - userOp.maxFeePerGas = gasPrice; - } else { - const { maxFeePerGas } = gasPrice; - userOp.maxFeePerGas = maxFeePerGas; - } - } - } - - // for userOp completeness - userOp.callGasLimit = BigInt(20000000); - userOp.verificationGasLimit = BigInt(10000000); - userOp.preVerificationGas = BigInt(100000); - - // for userOp completeness - if ( - !userOp.maxPriorityFeePerGas || - userOp.maxPriorityFeePerGas === BigInt(0) || - (userOp.maxPriorityFeePerGas as unknown as string) === "0x" || - (userOp.maxPriorityFeePerGas as unknown as string) === "0" - ) { - // setting a non zero value as division with maxPriorityFeePerGas will happen - userOp.maxPriorityFeePerGas = BigInt(1); - if ( - config.optimismNetworks.includes(chainId) || - config.mantleNetworks.includes(chainId) - ) { - const gasPrice = await this.gasPriceService.getGasPrice(); - if (typeof gasPrice === "bigint") { - userOp.maxPriorityFeePerGas = gasPrice; - } else { - const { maxPriorityFeePerGas } = gasPrice; - userOp.maxPriorityFeePerGas = maxPriorityFeePerGas; - } - } + const gasPrice = await this.gasPriceService.getGasPrice(); + if (typeof gasPrice === "bigint") { + userOp.maxFeePerGas = gasPrice; + userOp.maxPriorityFeePerGas = gasPrice; + } else { + const { maxFeePerGas } = gasPrice; + userOp.maxFeePerGas = maxFeePerGas; + userOp.maxPriorityFeePerGas = maxFeePerGas; } log.info( @@ -180,22 +141,6 @@ export class BundlerSimulationService { )} on chainId: ${chainId}`, ); - let supportsEthCallStateOverride = true; - let supportsEthCallByteCodeOverride = true; - if (config.networksNotSupportingEthCallStateOverrides.includes(chainId)) { - supportsEthCallStateOverride = false; - } else if ( - config.networksNotSupportingEthCallBytecodeStateOverrides.includes( - chainId, - ) - ) { - supportsEthCallByteCodeOverride = false; - } - - if (userOp.initCode !== "0x") { - supportsEthCallByteCodeOverride = false; - } - let baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); if (chainId === BLOCKCHAINS.OP_BNB_MAINNET && baseFeePerGas === 0n) { baseFeePerGas = BigInt( @@ -204,18 +149,25 @@ export class BundlerSimulationService { } const response = await this.gasEstimator.estimateUserOperationGas({ - userOperation: userOp, - stateOverrideSet, - supportsEthCallByteCodeOverride, - supportsEthCallStateOverride, + unEstimatedUserOperation: userOp, baseFeePerGas, + stateOverrides: stateOverrideSet, + partialOptions: { + entryPointAddress: entryPointContract.address, + }, }); + log.info( - `estimation respone from gas estimation package: ${customJSONStringify( - response, - )} on chainId: ${chainId}`, + { chainId }, + `estimateUserOperationGas: ${customJSONStringify(response)}`, ); + if (!isEstimateUserOperationGasResultV6(response)) { + throw new Error( + "Invalid response from the gas estimator. Expected a V6 response.", + ); + } + const { validAfter, validUntil } = response; let { verificationGasLimit, callGasLimit, preVerificationGas } = response; @@ -258,6 +210,7 @@ export class BundlerSimulationService { callGasLimit + BigInt(verificationGasLimitMultiplier) * verificationGasLimit + preVerificationGas; + log.info(`totalGas: ${totalGas} on chainId: ${chainId}`); const pvgMarkUp = config.pvgMarkUp[chainId] || 0.1; // setting default pvgMarkUp to 10% incase the value for a given chainId is not set @@ -507,6 +460,8 @@ export class BundlerSimulationService { validationData: ValidationData, entryPointContract: EntryPointContractType, ): Promise<boolean> { + // TODO: Check if balance >= required prefund if wallet deployment because we can't simulate deployment tx's + const { userOp, networkMaxFeePerGas, networkMaxPriorityFeePerGas } = validationData; @@ -575,22 +530,27 @@ export class BundlerSimulationService { ); } - // Check the user operation for validation and execution errors (if debug_traceCall is supported) - if ( - nodeconfig.has("supportsDebugTraceCall") && - nodeconfig - .get<number[]>("supportsDebugTraceCall") - .includes(this.networkService.chainId) - ) { - await this.validateUserOperation(entryPointContract, userOp); - } - if (!config.disableFeeValidation.includes(this.networkService.chainId)) { - const { preVerificationGas: networkPreVerificationGas } = - await this.gasEstimator.calculatePreVerificationGas({ - userOperation: userOp, - baseFeePerGas, - }); + const userOperation: UserOperationV6 = { + sender: userOp.sender, + nonce: userOp.nonce, + initCode: userOp.initCode, + paymasterAndData: userOp.paymasterAndData, + signature: userOp.signature, + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + callData: userOp.callData, + callGasLimit: BigInt(userOp.callGasLimit), + verificationGasLimit: BigInt(userOp.verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + }; + + const networkPreVerificationGas = + await this.gasEstimator.estimatePreVerificationGas( + userOperation, + BigInt(baseFeePerGas), + ); + log.info(`networkPreVerificationGas: ${networkPreVerificationGas}`); const minimumAcceptablePreVerificationGas = @@ -608,99 +568,58 @@ export class BundlerSimulationService { } } - log.info( - `maxFeePerGas, maxPriorityFeePerGas and preVerification are within acceptable limits`, - ); - return true; - } - - /** - * Simulation checks for validation and execution errors when submitting a user operation to the EntryPoint contract - * @param entryPointContract EntryPoint contract to simulate against - * @param userOp User operation to simulate - */ - private async validateUserOperation( - entryPointContract: EntryPointContractType, - userOp: UserOperationType, - ) { - try { - // We call debug_traceCall to simulation stack trace - const traceResult = await this.debugTraceCall(entryPointContract, userOp); + // Check the user operation for validation and execution errors (if debug_traceCall is supported) + if ( + // we can't simulate deployment transactions, they will fail because of insufficient entrypoint deposit + userOp.initCode === "0x" && + nodeconfig.has("supportsDebugTraceCall") && + nodeconfig + .get<number[]>("supportsDebugTraceCall") + .includes(this.networkService.chainId) + ) { + const traceStart = performance.now(); + + const traceResult: CallTracerResult = + await this.networkService.provider.request({ + method: "debug_traceCall" as any, // coalesce so viem doesn't complain + params: [ + { + from: zeroAddress, // so we don't get balance errors + to: entryPointContract.address, + data: encodeFunctionData({ + abi: entryPointContract.abi, + functionName: "handleOps", + args: [[userOp], "0xc75Bb3956c596efc6DB663cd3e2f64929d6AB0fc"], + }), + }, + "latest", + { + tracer: "callTracer", + tracerConfig: { + onlyTopCall: false, // we need deep traces + disableStack: false, // and we want the stack + enableReturnData: true, // and the return data so we can decode the error + }, + }, + ], + }); + const traceEnd = performance.now(); - // simulateValidationShould always return a revert, otherwise something is terribly wrong 😱 - if (!isReverted(traceResult)) { - logger.error( - `simulateValidation: didn't revert, traceResult: ${customJSONStringify(traceResult)}`, - ); - throw new RpcError( - "Simulation failed", - BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - ); - } + log.info(`debug_traceCall took ${traceEnd - traceStart} milliseconds`); - // we try to decode the error message against the EP contract ABI - const { errorName, args } = decodeErrorResult({ - abi: entryPointContract.abi, - data: traceResult.output, - }); + const errors = findErrorsInTrace(traceResult, []); - // When simulation is successfull it returns {errorName: ValidationResult, args: [...]}, - // and (for example) this is what a signature error looks like: { errorName: FailedOp, args: [0,AA23 reverted (or OOG)] } - if (errorName !== "ValidationResult") { - logger.warn( - `simulateValidation: errorName: ${errorName}, args: ${customJSONStringify(args)}`, - ); - throw new RpcError( - `${errorName}: ${args[1]}`, - BUNDLER_ERROR_CODES.WALLET_TRANSACTION_REVERTED, - ); + if (errors.length > 0) { + log.info(`Errors found in trace: ${customJSONStringify(errors)}`); + log.info(customJSONStringify(traceResult)); + throw new Error(errors.join(", ")); } - } catch (error: any) { - // this is a sanity check, in case debug_traceCall fails for whatever reason - logger.error( - { error: customJSONStringify(error) }, - `simulateUserOp failed`, - ); - throw new Error(error); } - } - /** - * Calls the debug_traceCall RPC method to simulate the validation of a user operation. - * See the following links for more information: - * - https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#javascript-based-tracing - * - https://docs.chainstack.com/reference/ethereum-tracecall - * @param entryPointContract The entry point contract - * @param userOp The user operation to be traced - * @returns CallTracerResult - */ - private async debugTraceCall( - entryPointContract: EntryPointContractType, - userOp: UserOperationType, - ): Promise<CallTracerResult> { - return this.networkService.provider.request({ - method: "debug_traceCall" as any, // coalesce so viem doesn't complain - params: [ - { - from: zeroAddress, // so we don't get balance errors - to: entryPointContract.address, - data: encodeFunctionData({ - abi: entryPointContract.abi, - functionName: "simulateValidation", - args: [userOp] as any, - }), - }, - "latest", - { - tracer: "callTracer", - tracerConfig: { - onlyTopCall: true, // don't need deep traces - disableStack: false, // but we want the stack - enableReturnData: true, // and the return data so we can decode the error - }, - }, - ], - }); + log.info( + `maxFeePerGas, maxPriorityFeePerGas and preVerification are within acceptable limits`, + ); + return true; } removeSpecialCharacters(input: string): string { @@ -736,15 +655,6 @@ export class BundlerSimulationService { } } -/** - * Checks if the trace result is reverted - * @param traceResult The result of the trace call - * @returns true if the trace result is reverted - */ -function isReverted(traceResult: CallTracerResult) { - return traceResult.error.toLowerCase().includes("reverted"); -} - // The following are the dependencies of the BundlerSimulationService class // 💡 TIP: Always pick only the required fields from the interface @@ -755,20 +665,6 @@ export type SimulationNetworkService = Pick< >; export type SimulationGasEstimator = Pick< - IGasEstimator, - | "estimateUserOperationGas" - | "setEntryPointAddress" - | "calculatePreVerificationGas" + GasEstimator, + "estimateUserOperationGas" | "estimatePreVerificationGas" >; - -interface CallTracerResult { - from: Address; - gas: Hex; - gasUsed: Hex; - to: Address; - input: Hex; - output: Hex; - value: Hex; - type: string; - error: string; -} diff --git a/src/common/simulation/BundlerSimulationService.v7.ts b/src/common/simulation/BundlerSimulationService.v7.ts index d21c4b07..ec6186f4 100644 --- a/src/common/simulation/BundlerSimulationService.v7.ts +++ b/src/common/simulation/BundlerSimulationService.v7.ts @@ -1,12 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable prefer-const */ -import { decodeErrorResult, encodeFunctionData, toHex } from "viem"; import { - createGasEstimator, - createOptimismGasEstimator, - EstimateUserOperationGas, - IGasEstimator, -} from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v7"; + decodeErrorResult, + encodeFunctionData, + toHex, + zeroAddress, +} from "viem"; import { config } from "../../config"; import { IEVMAccount } from "../../relayer/account"; import { @@ -15,7 +14,11 @@ import { } from "../../server/api/shared/middleware"; import { logger } from "../logger"; import { INetworkService } from "../network"; -import { EVMRawTransactionType, UserOperationType } from "../types"; +import { + EntryPointV07ContractType, + EVMRawTransactionType, + UserOperationType, +} from "../types"; import { customJSONStringify, parseError } from "../utils"; import RpcError from "../utils/rpc-error"; import { @@ -30,6 +33,16 @@ import { getUserOpHash, packUserOperation, } from "../entrypoint-v7/PackedUserOperation"; +import { GasEstimator } from "@biconomy/gas-estimations"; +import { createGasEstimator } from "@biconomy/gas-estimations"; +import { + EstimateUserOperationGasResult, + isEstimateUserOperationGasResultV7, +} from "@biconomy/gas-estimations"; +import nodeconfig from "config"; +import { CallTracerResult, findErrorsInTrace } from "./trace"; +import { toPackedUserOperation } from "@biconomy/gas-estimations"; +import { ZodError } from "zod"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -41,7 +54,7 @@ export class BundlerSimulationServiceV07 { gasPriceService: IGasPriceService; - gasEstimator: IGasEstimator; + gasEstimator: GasEstimator; constructor( networkService: INetworkService<IEVMAccount, EVMRawTransactionType>, @@ -50,16 +63,9 @@ export class BundlerSimulationServiceV07 { this.networkService = networkService; this.gasPriceService = gasPriceService; this.gasEstimator = createGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - chainId: this.networkService.chainId, + chainId: networkService.chainId, + rpc: networkService.provider, }); - - if (config.optimismNetworks.includes(this.networkService.chainId)) { - this.gasEstimator = createOptimismGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - chainId: this.networkService.chainId, - }); - } } async estimateUserOperationGas( @@ -69,8 +75,6 @@ export class BundlerSimulationServiceV07 { const { userOp, entryPointContract, chainId, stateOverrideSet } = estimateUserOperationGasData; - this.gasEstimator.setEntryPointAddress(entryPointContract.address); - const start = performance.now(); log.info( `userOp received: ${customJSONStringify( @@ -78,58 +82,23 @@ export class BundlerSimulationServiceV07 { )} on chainId: ${chainId}`, ); - // for userOp completeness - if ( - !userOp.maxFeePerGas || - userOp.maxFeePerGas === BigInt(0) || - (userOp.maxFeePerGas as unknown as string) === "0x" || - (userOp.maxFeePerGas as unknown as string) === "0" - ) { - // setting a non zero value as division with maxFeePerGas will happen - userOp.maxFeePerGas = BigInt(1); - if ( - config.optimismNetworks.includes(chainId) || - config.mantleNetworks.includes(chainId) - ) { - const gasPrice = await this.gasPriceService.getGasPrice(); - if (typeof gasPrice === "bigint") { - userOp.maxFeePerGas = gasPrice; - } else { - const { maxFeePerGas } = gasPrice; - userOp.maxFeePerGas = maxFeePerGas; - } - } + const gasPrice = await this.gasPriceService.getGasPrice(); + if (typeof gasPrice === "bigint") { + userOp.maxFeePerGas = gasPrice; + userOp.maxPriorityFeePerGas = gasPrice; + } else { + const { maxFeePerGas } = gasPrice; + userOp.maxFeePerGas = maxFeePerGas; + userOp.maxPriorityFeePerGas = maxFeePerGas; } // for userOp completeness userOp.callGasLimit = BigInt(5000000); userOp.verificationGasLimit = BigInt(5000000); userOp.preVerificationGas = BigInt(5000000); - userOp.maxPriorityFeePerGas = BigInt(userOp.maxPriorityFeePerGas); - userOp.maxFeePerGas = BigInt(userOp.maxFeePerGas); - // for userOp completeness - if ( - !userOp.maxPriorityFeePerGas || - userOp.maxPriorityFeePerGas === BigInt(0) || - (userOp.maxPriorityFeePerGas as unknown as string) === "0x" || - (userOp.maxPriorityFeePerGas as unknown as string) === "0" - ) { - // setting a non zero value as division with maxPriorityFeePerGas will happen - userOp.maxPriorityFeePerGas = BigInt(1); - if ( - config.optimismNetworks.includes(chainId) || - config.mantleNetworks.includes(chainId) - ) { - const gasPrice = await this.gasPriceService.getGasPrice(); - if (typeof gasPrice === "bigint") { - userOp.maxPriorityFeePerGas = gasPrice; - } else { - const { maxPriorityFeePerGas } = gasPrice; - userOp.maxPriorityFeePerGas = maxPriorityFeePerGas; - } - } - } + userOp.factory = userOp.factory ?? "0x"; + userOp.factoryData = userOp.factoryData ?? "0x"; log.info( `userOp to be used for estimation: ${customJSONStringify( @@ -137,40 +106,32 @@ export class BundlerSimulationServiceV07 { )} on chainId: ${chainId}`, ); - let supportsEthCallStateOverride = true; - let supportsEthCallByteCodeOverride = true; - if (config.networksNotSupportingEthCallStateOverrides.includes(chainId)) { - supportsEthCallStateOverride = false; - } else if ( - config.networksNotSupportingEthCallBytecodeStateOverrides.includes( - chainId, - ) - ) { - supportsEthCallByteCodeOverride = false; - } - - let response: EstimateUserOperationGas; const baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); + let response: EstimateUserOperationGasResult; try { response = await this.gasEstimator.estimateUserOperationGas({ - userOperation: userOp, - stateOverrideSet, - supportsEthCallByteCodeOverride, - supportsEthCallStateOverride, + unEstimatedUserOperation: userOp, baseFeePerGas, + stateOverrides: stateOverrideSet, + partialOptions: { + entryPointAddress: entryPointContract.address, + }, }); - - log.info( - `estimation response from gas estimation package: ${customJSONStringify( - response, - )} on chainId: ${chainId}`, - ); } catch (error: any) { + let errorMessage: string = error.message; + log.error( - `[entry-point-gas-estimations] package threw an error: ${error.message}`, + { chainId }, + `[@biconomy/gas-estimations] package threw an error: ${errorMessage}`, + ); + throw errorMessage; + } + + if (!isEstimateUserOperationGasResultV7(response)) { + throw new Error( + "Invalid response from the gas estimator. Expected a V7 response.", ); - throw error; } let { @@ -181,6 +142,11 @@ export class BundlerSimulationServiceV07 { paymasterVerificationGasLimit, } = response; + log.info( + { chainId }, + `estimateUserOperationGas: ${customJSONStringify(response)}`, + ); + callGasLimit += BigInt(Math.ceil(Number(callGasLimit) * 0.1)); verificationGasLimit += BigInt( Math.ceil(Number(verificationGasLimit) * 0.1), @@ -192,17 +158,20 @@ export class BundlerSimulationServiceV07 { Math.ceil(Number(paymasterVerificationGasLimit) * 0.1), ); - const verificationGasLimitMultiplier = userOp.paymaster === "0x" ? 1 : 3; - const totalGas = + const requiredGas = + verificationGasLimit + callGasLimit + - BigInt(verificationGasLimitMultiplier) * verificationGasLimit + + preVerificationGas + + paymasterVerificationGasLimit + + paymasterPostOpGasLimit + preVerificationGas; - log.info(`totalGas: ${totalGas} on chainId: ${chainId}`); + + log.info({ chainId }, `requiredGas: ${requiredGas}`); const pvgMarkUp = config.pvgMarkUp[chainId] || 0.1; // setting default pvgMarkUp to 10% incase the value for a given chainId is not set preVerificationGas += BigInt( - Math.ceil(Number(toHex(totalGas)) * pvgMarkUp), + Math.ceil(Number(toHex(requiredGas)) * pvgMarkUp), ); log.info( @@ -254,6 +223,10 @@ export class BundlerSimulationServiceV07 { try { const { userOp, entryPointContract, chainId } = simulateValidationData; + // for userOp completeness + userOp.factory = userOp.factory ?? "0x"; + userOp.factoryData = userOp.factoryData ?? "0x"; + log.info( `userOp received: ${customJSONStringify( userOp, @@ -264,11 +237,14 @@ export class BundlerSimulationServiceV07 { await this.gasPriceService.get1559GasPrice(); let gasPrice = Math.ceil(Number(maxFeePerGas) * 2).toString(16); - await this.checkUserOperationForRejection({ - userOp, - networkMaxPriorityFeePerGas: maxPriorityFeePerGas, - networkMaxFeePerGas: maxFeePerGas, - }); + await this.checkUserOperationForRejection( + { + userOp, + networkMaxPriorityFeePerGas: maxPriorityFeePerGas, + networkMaxFeePerGas: maxFeePerGas, + }, + entryPointContract, + ); const packed = packUserOperation(userOp); const data = encodeFunctionData({ @@ -444,7 +420,10 @@ export class BundlerSimulationServiceV07 { */ async checkUserOperationForRejection( validationData: ValidationDataV07, + entryPointContract: EntryPointV07ContractType, ): Promise<boolean> { + // TODO: Check if balance >= required prefund if wallet deployment because we can't simulate deployment tx's + const { userOp, networkMaxFeePerGas, networkMaxPriorityFeePerGas } = validationData; @@ -499,11 +478,11 @@ export class BundlerSimulationServiceV07 { const baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); - const { preVerificationGas: networkPreVerificationGas } = - await this.gasEstimator.calculatePreVerificationGas({ - userOperation: userOp, - baseFeePerGas, - }); + const networkPreVerificationGas = + await this.gasEstimator.estimatePreVerificationGas( + userOp, + BigInt(baseFeePerGas), + ); log.info(`networkPreVerificationGas: ${networkPreVerificationGas}`); const minimumAcceptablePreVerificationGas = @@ -519,6 +498,64 @@ export class BundlerSimulationServiceV07 { ); } + // Check the user operation for validation and execution errors (if debug_traceCall is supported) + if ( + // we can't simulate deployment transactions, they will fail because of insufficient entrypoint deposit + userOp.factory === "0x" && + userOp.factoryData === "0x" && + nodeconfig.has("supportsDebugTraceCall") && + nodeconfig + .get<number[]>("supportsDebugTraceCall") + .includes(this.networkService.chainId) + ) { + const traceStart = performance.now(); + + const packedUserOperation = toPackedUserOperation(userOp); + + log.info( + { chainId: this.networkService.chainId }, + `packedUserOperation: ${customJSONStringify(packedUserOperation)}`, + ); + const traceResult: CallTracerResult = + await this.networkService.provider.request({ + method: "debug_traceCall" as any, // coalesce so viem doesn't complain + params: [ + { + from: zeroAddress, // so we don't get balance errors + to: entryPointContract.address, + data: encodeFunctionData({ + abi: entryPointContract.abi, + functionName: "handleOps", + args: [ + [packedUserOperation], + "0xc75Bb3956c596efc6DB663cd3e2f64929d6AB0fc", + ], + }), + }, + "latest", + { + tracer: "callTracer", + tracerConfig: { + onlyTopCall: false, // we need deep traces + disableStack: false, // and we want the stack + enableReturnData: true, // and the return data so we can decode the error + }, + }, + ], + }); + const traceEnd = performance.now(); + + log.info(`debug_traceCall took ${traceEnd - traceStart} milliseconds`); + + const errors = findErrorsInTrace(traceResult, []); + + if (errors.length > 0) { + log.info(`Errors found in trace: ${customJSONStringify(errors)}`); + log.info(customJSONStringify(traceResult)); + throw new Error(errors.join(", ")); + } + } + log.info( `maxFeePerGas, maxPriorityFeePerGas and preVerification are within acceptable limits`, ); diff --git a/src/common/simulation/GasEstimationsWrapper.ts b/src/common/simulation/GasEstimationsWrapper.ts deleted file mode 100644 index 0c842b3b..00000000 --- a/src/common/simulation/GasEstimationsWrapper.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - CalculatePreVerificationGas, - CalculatePreVerificationGasParams, - createArbitrumGasEstimator, - createGasEstimator, - createKakarotGasEstimator, - createMantleGasEstimator, - createMorphGasEstimator, - createOptimismGasEstimator, - createScrollGasEstimator, - createSeiGasEstimator, - EstimateUserOperationGas, - EstimateUserOperationGasParams, -} from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6"; -import { SimulationGasEstimator } from "./BundlerSimulationService"; -import config from "config"; -import { hideRpcUrlApiKey } from "../network/utils"; - -/** - * The following is the adapter for the gas estimations class exposed by the entry-point-gas-estimations package. - * Because the original class is not designed properly, we wrap it with this adapter to make it more usable. - */ -export class GasEstimationsAdapter implements SimulationGasEstimator { - public readonly chainId: number; - public readonly rpcUrl: string; - - private wrappedGasEstimator: { - estimator: SimulationGasEstimator; - type: estimatorTypeChoices; - }; - - constructor({ - chainId, - rpcUrl, - gasEstimator = factoryCreateGasEstimator({ chainId, rpcUrl }), - }: IGasEstimationsAdapterOptions) { - this.chainId = chainId; - this.rpcUrl = rpcUrl; - this.wrappedGasEstimator = gasEstimator; - } - - /** - * Used for pretty printing the service configuration - * @returns JSON stringified object with API keys removed - */ - toJSON(): Omit<IGasEstimationsAdapterOptions, "gasEstimator"> & { - type: estimatorTypeChoices; - } { - return { - chainId: this.chainId, - rpcUrl: hideRpcUrlApiKey(this.rpcUrl), - type: this.wrappedGasEstimator.type, - }; - } - - setEntryPointAddress(entryPointAddress: `0x${string}`): void { - return this.wrappedGasEstimator.estimator.setEntryPointAddress( - entryPointAddress, - ); - } - - estimateUserOperationGas( - params: EstimateUserOperationGasParams, - ): Promise<EstimateUserOperationGas> { - return this.wrappedGasEstimator.estimator.estimateUserOperationGas(params); - } - - calculatePreVerificationGas( - params: CalculatePreVerificationGasParams, - ): Promise<CalculatePreVerificationGas> { - return this.wrappedGasEstimator.estimator.calculatePreVerificationGas( - params, - ); - } -} - -/** - * A Factory Method for creating the nested gas estimators based on the chainId - */ -const factoryCreateGasEstimator = ({ - chainId, - rpcUrl, -}: IGasEstimationsAdapterOptions): { - estimator: SimulationGasEstimator; - type: estimatorTypeChoices; -} => { - if (config.get<number[]>("optimismNetworks").includes(chainId)) { - return { - estimator: createOptimismGasEstimator({ - rpcUrl, - }), - type: "optimism", - }; - } - - if (config.get<number[]>("arbitrumNetworks").includes(chainId)) { - return { - estimator: createArbitrumGasEstimator({ - rpcUrl, - }), - type: "arbitrum", - }; - } - - if (config.get<number[]>("mantleNetworks").includes(chainId)) { - return { - estimator: createMantleGasEstimator({ - rpcUrl, - }), - type: "mantle", - }; - } - - if (config.get<number[]>("scrollNetworks").includes(chainId)) { - return { - estimator: createScrollGasEstimator({ - rpcUrl, - }), - type: "scroll", - }; - } - - if (config.get<number[]>("morphNetworks").includes(chainId)) { - return { - estimator: createMorphGasEstimator({ - rpcUrl, - }), - type: "morph", - }; - } - - if (config.get<number[]>("seiNetworks").includes(chainId)) { - return { - estimator: createSeiGasEstimator({ - rpcUrl, - }), - type: "sei", - }; - } - - if (config.get<number[]>("kakarotNetworks").includes(chainId)) { - return { - estimator: createKakarotGasEstimator({ - rpcUrl, - }), - type: "kakarot", - }; - } - - return { - estimator: createGasEstimator({ - rpcUrl, - }), - type: "regular", - }; -}; - -interface IGasEstimationsAdapterOptions { - chainId: number; - rpcUrl: string; - gasEstimator?: { - estimator: SimulationGasEstimator; - type: estimatorTypeChoices; - }; -} - -type estimatorTypeChoices = - | "regular" - | "optimism" - | "arbitrum" - | "mantle" - | "scroll" - | "morph" - | "sei" - | "kakarot"; diff --git a/src/common/simulation/trace.ts b/src/common/simulation/trace.ts new file mode 100644 index 00000000..191509d9 --- /dev/null +++ b/src/common/simulation/trace.ts @@ -0,0 +1,49 @@ +import { Address, Hex } from "viem"; + +export interface CallTracerResult { + from: Address; + gas: Hex; + gasUsed: Hex; + to: Address; + input: Hex; + output: Hex; + value: Hex; + type: string; + error: string; +} + +export interface TraceCall { + from: string; + gas: string; + gasUsed: string; + to: string; + input: string; + output?: string; + error?: string; + calls?: TraceCall[]; + value?: string; + type: string; +} + +/** + * Recursively checks if any nested call contains errors and returns all errors. + * @param trace The top-level trace call object. + * @returns An array of errors (if found) + */ +export function findErrorsInTrace( + trace: TraceCall, + errors: string[], +): string[] { + if (trace.error) { + errors.push(trace.error); + } + + // If there are nested calls, check them recursively + if (trace.calls && trace.calls.length > 0) { + for (const nestedCall of trace.calls) { + findErrorsInTrace(nestedCall, errors); + } + } + + return errors; +} diff --git a/src/common/types/index.ts b/src/common/types/index.ts index 94696971..415fc702 100644 --- a/src/common/types/index.ts +++ b/src/common/types/index.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ENTRY_POINT_ABI } from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6"; import { GetContractReturnType, Hex } from "viem"; import { ENTRY_POINT_V07_ABI } from "../entrypoint-v7/abiv7"; import { EVMAccountInfo } from "../../relayer/account"; +import { ENTRYPOINT_V6_ABI } from "@biconomy/gas-estimations"; export enum TransactionType { FUNDING = "FUNDING", @@ -232,7 +232,7 @@ export type UpdateRequestDataType = { }; export type EntryPointContractType = GetContractReturnType< - typeof ENTRY_POINT_ABI + typeof ENTRYPOINT_V6_ABI >; export type EntryPointV07ContractType = GetContractReturnType< diff --git a/src/relayer/consumer/BundlerConsumer.ts b/src/relayer/consumer/BundlerConsumer.ts index ce52f201..a149c98c 100644 --- a/src/relayer/consumer/BundlerConsumer.ts +++ b/src/relayer/consumer/BundlerConsumer.ts @@ -1,6 +1,5 @@ import { ConsumeMessage } from "amqplib"; import { encodeFunctionData } from "viem"; -import { ENTRY_POINT_ABI } from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6"; import { ICacheService } from "../../common/cache"; import { logger } from "../../common/logger"; import { IQueue } from "../../common/queue"; @@ -25,6 +24,7 @@ import { isUserOpV06, packUserOperation, } from "../../common/entrypoint-v7/PackedUserOperation"; +import { ENTRYPOINT_V6_ABI } from "@biconomy/gas-estimations"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -88,7 +88,7 @@ export class BundlerConsumer let data; if (isV06) { data = encodeFunctionData({ - abi: ENTRY_POINT_ABI, + abi: ENTRYPOINT_V6_ABI, functionName: "handleOps", args: [[userOp], activeRelayer.getPublicKey() as `0x${string}`], }); diff --git a/src/relayer/transaction-service/EVMTransactionService.ts b/src/relayer/transaction-service/EVMTransactionService.ts index ec8a2b9d..f32850f2 100644 --- a/src/relayer/transaction-service/EVMTransactionService.ts +++ b/src/relayer/transaction-service/EVMTransactionService.ts @@ -1,7 +1,6 @@ import { Mutex } from "async-mutex"; import { Hex, TransactionReceipt, decodeFunctionData, toHex } from "viem"; import { estimateGas } from "viem/linea"; -import { ENTRY_POINT_ABI } from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6"; import { ICacheService } from "../../common/cache"; import { IGasPriceService } from "../../common/gas-price"; import { logger } from "../../common/logger"; @@ -45,6 +44,7 @@ import { IUserOperationStateDAO } from "../../common/db"; import { GasPriceType } from "../../common/gas-price/types"; import { BLOCKCHAINS } from "../../common/constants"; import pino from "pino"; +import { ENTRYPOINT_V6_ABI } from "@biconomy/gas-estimations"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -573,7 +573,7 @@ export class EVMTransactionService let gasLimitOverhead = 50000n; // Now we need the verificationGasLimit and callGasLimit from the user op. - const decodedData = decodeFunctionData({ abi: ENTRY_POINT_ABI, data }); + const decodedData = decodeFunctionData({ abi: ENTRYPOINT_V6_ABI, data }); const firstArg = decodedData.args[0]; if (Array.isArray(firstArg) && firstArg.length > 0) { diff --git a/src/server/api/v2/biconomy_getGasFeeValues/handler.ts b/src/server/api/v2/biconomy_getGasFeeValues/handler.ts index 32216d60..8522836c 100644 --- a/src/server/api/v2/biconomy_getGasFeeValues/handler.ts +++ b/src/server/api/v2/biconomy_getGasFeeValues/handler.ts @@ -3,15 +3,12 @@ import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { gasPriceServiceMap } from "../../../../common/service-manager"; import { customJSONStringify, parseError } from "../../../../common/utils"; -// import { updateRequest } from '../../auth/UpdateRequest'; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); export const getGasFeeValues = async (req: Request, res: Response) => { - // const bundlerRequestId = req.body.params[6]; - try { const { id } = req.body; const { chainId /* apiKey */ } = req.params; @@ -25,26 +22,6 @@ export const getGasFeeValues = async (req: Request, res: Response) => { )}`, ); - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: { - // callGasLimit, - // verificationGasLimit, - // preVerificationGas, - // validUntil, - // validAfter, - // maxPriorityFeePerGas: gasPrice?.maxPriorityFeePerGas, - // maxFeePerGas: gasPrice?.maxFeePerGas, - // }, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, @@ -55,26 +32,6 @@ export const getGasFeeValues = async (req: Request, res: Response) => { }); } - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: { - // callGasLimit, - // verificationGasLimit, - // preVerificationGas, - // validUntil, - // validAfter, - // maxPriorityFeePerGas: gasPrice, - // maxFeePerGas: gasPrice, - // }, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, @@ -87,20 +44,6 @@ export const getGasFeeValues = async (req: Request, res: Response) => { log.error(`Error in getGasFeeValues handler ${parseError(error)}`); const { id } = req.body; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", id: id || 1, diff --git a/src/server/api/v3/biconomy_getGasFeeValues/handler.ts b/src/server/api/v3/biconomy_getGasFeeValues/handler.ts index e13d0657..44d9ab49 100644 --- a/src/server/api/v3/biconomy_getGasFeeValues/handler.ts +++ b/src/server/api/v3/biconomy_getGasFeeValues/handler.ts @@ -4,120 +4,56 @@ import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { gasPriceServiceMap } from "../../../../common/service-manager"; import { customJSONStringify, parseError } from "../../../../common/utils"; -// import { updateRequest } from '../../auth/UpdateRequest'; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); export const getGasFeeValues = async (req: Request, res: Response) => { - // const bundlerRequestId = req.body.params[6]; - try { const { id } = req.body; const { chainId /* apiKey */ } = req.params; const gasPrice = await gasPriceServiceMap[Number(chainId)]?.getGasPrice(); + let maxFeePerGas: bigint; + let maxPriorityFeePerGas: bigint; + if (typeof gasPrice !== "bigint") { log.info( `Gas price for chainId: ${chainId} is: ${customJSONStringify( gasPrice, )}`, ); - - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: { - // callGasLimit, - // verificationGasLimit, - // preVerificationGas, - // validUntil, - // validAfter, - // maxPriorityFeePerGas: gasPrice?.maxPriorityFeePerGas, - // maxFeePerGas: gasPrice?.maxFeePerGas, - // }, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - - return res.status(STATUSES.SUCCESS).json({ - jsonrpc: "2.0", - id: id || 1, - result: { - slow: { - maxPriorityFeePerGas: toHex( - gasPrice?.maxPriorityFeePerGas, - )?.toString(), - maxFeePerGas: toHex(gasPrice?.maxFeePerGas)?.toString(), - }, - standard: { - maxPriorityFeePerGas: toHex( - gasPrice?.maxPriorityFeePerGas, - )?.toString(), - maxFeePerGas: toHex(gasPrice?.maxFeePerGas)?.toString(), - }, - fast: { - maxPriorityFeePerGas: toHex( - gasPrice?.maxPriorityFeePerGas, - )?.toString(), - maxFeePerGas: toHex(gasPrice?.maxFeePerGas)?.toString(), - }, - }, - }); + maxFeePerGas = gasPrice?.maxFeePerGas || 1n; + maxPriorityFeePerGas = gasPrice?.maxPriorityFeePerGas || 1n; + } else { + maxFeePerGas = gasPrice; + maxPriorityFeePerGas = gasPrice; } - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: { - // callGasLimit, - // verificationGasLimit, - // preVerificationGas, - // validUntil, - // validAfter, - // maxPriorityFeePerGas: gasPrice, - // maxFeePerGas: gasPrice, - // }, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, result: { - maxPriorityFeePerGas: gasPrice.toString(), - maxFeePerGas: gasPrice.toString(), + slow: { + maxPriorityFeePerGas: toHex(maxPriorityFeePerGas)?.toString(), + maxFeePerGas: toHex(maxFeePerGas)?.toString(), + }, + standard: { + maxPriorityFeePerGas: toHex(maxPriorityFeePerGas)?.toString(), + maxFeePerGas: toHex(maxFeePerGas)?.toString(), + }, + fast: { + maxPriorityFeePerGas: toHex(maxPriorityFeePerGas)?.toString(), + maxFeePerGas: toHex(maxFeePerGas)?.toString(), + }, }, }); } catch (error) { log.error(`Error in getGasFeeValues handler ${parseError(error)}`); const { id } = req.body; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", id: id || 1, diff --git a/src/test/e2e/nativeTransfer.test.ts b/src/test/e2e/nativeTransfer.test.ts index a0ca9f07..9d987e91 100644 --- a/src/test/e2e/nativeTransfer.test.ts +++ b/src/test/e2e/nativeTransfer.test.ts @@ -1,11 +1,26 @@ -import { Chain, createWalletClient, http, PrivateKeyAccount } from "viem"; +import { + createWalletClient, + http, + PrivateKeyAccount, + publicActions, +} from "viem"; import { privateKeyToAccount } from "viem/accounts"; -import { base, avalanche, optimism, polygon, bsc, arbitrum } from "viem/chains"; +import { + base, + avalanche, + optimism, + polygon, + bsc, + arbitrum, + gnosis, +} from "viem/chains"; import { BiconomySmartAccountV2, createSmartAccountClient, - PaymasterMode, + UserOperationStruct, } from "@biconomy/account"; +import { createNexusClient } from "@biconomy/sdk"; +import axios, { AxiosError } from "axios"; /** * This file contains end-to-end tests that can run against a local or production bundler instance. @@ -31,6 +46,11 @@ import { * 🔥 Don't commit un-skipped e2e tests so someone doesn't run them by accident 🔥 */ describe("e2e", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (BigInt.prototype as any).toJSON = function () { + return this.toString(); + }; + const privateKey = process.env.PRIVATE_KEY; if (!privateKey) { throw new Error("PRIVATE_KEY is not defined"); @@ -42,248 +62,942 @@ describe("e2e", () => { const valueToSend = 1n; // send only 1 wei - describe.skip("base-mainnet", () => { - const chainId = 8453; - const bundlerUrl = `${bundlerHostname}/api/v2/${chainId}/test`; + const stubPaymasterAndData = + "0x00000f79b7faf42eebadba19acc07cd08af4478900000000000000000000000029c3e9456c0eca5beb1f78763204bac6c682407700000000000000000000000000000000000000000000000000000000676410e60000000000000000000000000000000000000000000000000000000067640fba0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004170f9cfb3f7a4204e0a497c8d19a98e2157b0df2740019ba2250a4c73de30539f14ea86cc47e0c96c16fd61b4b2f7ef777cf2c306974a03d56bdcc9313040242b1c00000000000000000000000000000000000000000000000000000000000000"; - const paymasterUrl = process.env.BASE_MAINNET_PAYMASTER_URL; - if (!paymasterUrl) { - throw new Error("BASE_MAINNET_PAYMASTER_URL is not defined"); - } + describe("base-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${base.id}/test`; - it("should perform a native transfer using a paymaster", async () => { - const account = privateKeyToAccount(`0x${privateKey}`); + const paymasterUrl = process.env.BASE_MAINNET_PAYMASTER_URL; - logConfig(bundlerUrl, paymasterUrl, account); + logConfig(base.id, bundlerUrl, account, paymasterUrl); - const smartAccount = await buildSmartAccount( - base, + const signer = createWalletClient({ account, - bundlerUrl, - paymasterUrl, - valueToSend, - ); + chain: base, + transport: http(), + }).extend(publicActions); - const receipt = await sendUserOperation(account, smartAccount); - console.log(receipt); + let smartAccount: BiconomySmartAccountV2; - expect(receipt.success).toBe("true"); + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: signer, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("BASE_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial<UserOperationStruct> = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + } }); - }); - describe.skip("optimism-mainnet", () => { - const chainId = 10; + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${base.id}/test`; - const bundlerUrl = `${bundlerHostname}/api/v2/${chainId}/test`; + logConfig(base.id, bundlerUrl, account, ""); - const paymasterUrl = process.env.OPTIMISM_MAINNET_PAYMASTER_URL; - if (!paymasterUrl) { - throw new Error("OPTIMISM_MAINNET_PAYMASTER_URL is not defined"); - } + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: base, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); - it("should perform a native transfer using a paymaster", async () => { - const account = privateKeyToAccount(`0x${privateKey}`); + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); - logConfig(bundlerUrl, paymasterUrl, account); + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); - const smartAccount = await buildSmartAccount( - optimism, - account, - bundlerUrl, - paymasterUrl, - valueToSend, - ); + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); - const receipt = await sendUserOperation(account, smartAccount); - console.log(receipt); + console.log(receipt); - expect(receipt.success).toBe("true"); + expect(receipt.status).toBe("success"); + }); }); }); - describe.skip("avalanche-mainnet", () => { - const chainId = 43114; - - const bundlerUrl = `${bundlerHostname}/api/v2/${chainId}/test`; + describe("optimism-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); - const paymasterUrl = process.env.AVALANCHE_MAINNET_PAYMASTER_URL; - if (!paymasterUrl) { - throw new Error("AVALANCHE_MAINNET_PAYMASTER_URL is not defined"); - } + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${optimism.id}/test`; - it("should perform a native transfer using a paymaster", async () => { - const account = privateKeyToAccount(`0x${privateKey}`); + const paymasterUrl = process.env.OPTIMISM_MAINNET_PAYMASTER_URL; - logConfig(bundlerUrl, paymasterUrl, account); + logConfig(optimism.id, bundlerUrl, account, paymasterUrl); - const smartAccount = await buildSmartAccount( - avalanche, + const client = createWalletClient({ account, - bundlerUrl, - paymasterUrl, - valueToSend, - ); + chain: optimism, + transport: http(), + }); - const receipt = await sendUserOperation(account, smartAccount); - console.log(receipt); + let smartAccount: BiconomySmartAccountV2; - expect(receipt.success).toBe("true"); + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("OPTIMISM_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial<UserOperationStruct> = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${optimism.id}/test`; + + logConfig(optimism.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: optimism, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); }); }); - describe.skip("polygon-mainnet", () => { - const chainId = 137; + describe("avalanche-mainnet", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${avalanche.id}/test`; + const account = privateKeyToAccount(`0x${privateKey}`); - const bundlerUrl = `${bundlerHostname}/api/v2/${chainId}/test`; + const paymasterUrl = process.env.AVALANCHE_MAINNET_PAYMASTER_URL; - const paymasterUrl = process.env.POLYGON_MAINNET_PAYMASTER_URL; - if (!paymasterUrl) { - throw new Error("POLYGON_MAINNET_PAYMASTER_URL is not defined"); - } + logConfig(avalanche.id, bundlerUrl, account, paymasterUrl); - it("should perform a native transfer using a paymaster", async () => { - const account = privateKeyToAccount(`0x${privateKey}`); + const client = createWalletClient({ + account, + chain: avalanche, + transport: http(), + }); - logConfig(bundlerUrl, paymasterUrl, account); + let smartAccount: BiconomySmartAccountV2; - const smartAccount = await buildSmartAccount( - polygon, - account, + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, bundlerUrl, paymasterUrl, - valueToSend, - ); + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); - const receipt = await sendUserOperation(account, smartAccount); console.log(receipt); expect(receipt.success).toBe("true"); }); - }); - describe.skip("bsc-mainnet", () => { - const chainId = 56; + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("AVALANCHE_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial<UserOperationStruct> = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); - const bundlerUrl = `${bundlerHostname}/api/v2/${chainId}/test`; + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); - const paymasterUrl = process.env.BSC_MAINNET_PAYMASTER_URL; - if (!paymasterUrl) { - throw new Error("BSC_MAINNET_PAYMASTER_URL is not defined"); + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); } + }); + + describe("bsc-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${bsc.id}/test`; - it("should perform a native transfer using a paymaster", async () => { - const account = privateKeyToAccount(`0x${privateKey}`); + const paymasterUrl = process.env.BSC_MAINNET_PAYMASTER_URL; - logConfig(bundlerUrl, paymasterUrl, account); + logConfig(bsc.id, bundlerUrl, account, paymasterUrl); - const smartAccount = await buildSmartAccount( - bsc, + const client = createWalletClient({ account, - bundlerUrl, - paymasterUrl, - valueToSend, - ); + chain: bsc, + transport: http(), + }); - const receipt = await sendUserOperation(account, smartAccount); - console.log(receipt); + let smartAccount: BiconomySmartAccountV2; - expect(receipt.success).toBe("true"); + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("AVALANCHE_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial<UserOperationStruct> = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } }); - }); - describe.skip("arbitrum-mainnet", () => { - const chainId = 42161; + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${bsc.id}/test`; - const bundlerUrl = `${bundlerHostname}/api/v2/${chainId}/test`; + logConfig(bsc.id, bundlerUrl, account, ""); - const paymasterUrl = process.env.ARBITRUM_MAINNET_PAYMASTER_URL; - if (!paymasterUrl) { - throw new Error("ARBITRUM_MAINNET_PAYMASTER_URL is not defined"); - } + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: bsc, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe("arbitrum-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${arbitrum.id}/test`; - it("should perform a native transfer using a paymaster", async () => { - const account = privateKeyToAccount(`0x${privateKey}`); + const paymasterUrl = process.env.ARBITRUM_MAINNET_PAYMASTER_URL; - logConfig(bundlerUrl, paymasterUrl, account); + logConfig(arbitrum.id, bundlerUrl, account, paymasterUrl); - const smartAccount = await buildSmartAccount( - arbitrum, + const client = createWalletClient({ account, - bundlerUrl, - paymasterUrl, - valueToSend, - ); + chain: arbitrum, + transport: http(), + }); - const receipt = await sendUserOperation(account, smartAccount); - console.log(receipt); + let smartAccount: BiconomySmartAccountV2; - expect(receipt.success).toBe("true"); + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("ARBITRUM_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial<UserOperationStruct> = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } }); - }); -}); -/** - * A helper function to send a sponsored native transfer user operation - * @param account viem account - * @param smartAccount biconomy smart account - * @returns receipt - */ -async function sendUserOperation( - account: PrivateKeyAccount, - smartAccount: BiconomySmartAccountV2, -) { - const tx = { - to: account.address, - value: 1n, - }; + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${arbitrum.id}/test`; - const userOpResponse = await smartAccount.sendTransaction(tx, { - paymasterServiceData: { mode: PaymasterMode.SPONSORED }, - }); + logConfig(arbitrum.id, bundlerUrl, account, ""); - return userOpResponse.wait(); -} + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: arbitrum, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); -/** - * A helper function to build a biconomy smart account for a given chain - * @param chain viem chain - * @param account viem account - * @param bundlerUrl biconomy bundler url - * @param paymasterUrl biconomy paymaster url - * @param valueToSend value in wei - * @returns biconomy smart account - */ -async function buildSmartAccount( - chain: Chain, - account: PrivateKeyAccount, - bundlerUrl: string, - paymasterUrl: string | undefined, - valueToSend: bigint, -) { - const client = createWalletClient({ - account, - chain, - transport: http(), + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); }); - const smartAccount = await createSmartAccountClient({ - signer: client, - bundlerUrl, - paymasterUrl, + describe("polygon-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${polygon.id}/test`; + + const paymasterUrl = process.env.POLYGON_MAINNET_PAYMASTER_URL; + + logConfig(polygon.id, bundlerUrl, account, paymasterUrl); + + const client = createWalletClient({ + account, + chain: polygon, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("POLYGON_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial<UserOperationStruct> = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${polygon.id}/test`; + + logConfig(polygon.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: polygon, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); }); - await requireBalance(smartAccount, valueToSend); - return smartAccount; -} + describe.only("gnosis-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${gnosis.id}/test`; + + const paymasterUrl = process.env.GNOSIS_MAINNET_PAYMASTER_URL; + + logConfig(gnosis.id, bundlerUrl, account, paymasterUrl); + + const client = createWalletClient({ + account, + chain: gnosis, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("GNOSIS_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial<UserOperationStruct> = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe.skip("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${gnosis.id}/test`; + + logConfig(gnosis.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: gnosis, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); +}); function logConfig( + chainId: number, bundlerUrl: string, - paymasterUrl: string, account: PrivateKeyAccount, + paymasterUrl?: string, ) { - console.log(`Bundler URL: ${bundlerUrl}`); - console.log(`Paymaster URL: ${paymasterUrl}`); - console.log(`EOA Address: ${account.address}`); + console.log( + JSON.stringify({ + chainId, + bundlerUrl, + paymasterUrl, + eoaAddress: account.address, + }), + ); } async function requireBalance( @@ -304,3 +1018,38 @@ Balance: ${nativeBalance.amount} wei, required: ${valueToSend} wei`, ); } } + +async function sponsorUserOperation( + paymasterUrl: string, + estimatedUserOperation: UserOperationStruct, +) { + try { + const sponsorUserOperationResponse = await axios.post(paymasterUrl, { + id: 1, + jsonrpc: "2.0", + method: "pm_sponsorUserOperation", + params: [ + estimatedUserOperation, + { + mode: "SPONSORED", + calculateGasLimits: false, + expiryDuration: 300, + sponsorshipInfo: { + webhookData: {}, + smartAccountInfo: { + name: "BICONOMY", + version: "2.0.0", + }, + }, + }, + ], + }); + + const { paymasterAndData } = sponsorUserOperationResponse.data.result; + + return paymasterAndData; + } catch (err) { + const axiosError = err as AxiosError; + throw new Error(axiosError.message); + } +} diff --git a/yarn.lock b/yarn.lock index 77f1bcdf..dbc38062 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11,7 +11,7 @@ debug "^4.3.4" safe-buffer "~5.1.2" -"@adraffy/ens-normalize@^1.10.1": +"@adraffy/ens-normalize@1.11.0", "@adraffy/ens-normalize@^1.10.1": version "1.11.0" resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== @@ -787,6 +787,23 @@ dependencies: merkletreejs "^0.4.0" +"@biconomy/gas-estimations@0.2.51": + version "0.2.51" + resolved "https://registry.yarnpkg.com/@biconomy/gas-estimations/-/gas-estimations-0.2.51.tgz#8d238f615635be79f40251067be1f07c13b2bb24" + integrity sha512-ccy1ofAunjDLFjx+RWXUiav1r7MOj+Erh90eEvsFwWQufiXzoPqCYFXukuWUyb93bEBOEWjIwwVOzE31+su0gw== + dependencies: + dotenv "^16.4.5" + viem "^2.21.49" + zod "^3.22.4" + zod-validation-error "^2.1.0" + +"@biconomy/sdk@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@biconomy/sdk/-/sdk-0.0.19.tgz#91cad1280e9819588c3238130ca39ce1f1809e2a" + integrity sha512-rcxqfC+18Xy365mzOKb5oB64a8ZD4zC5GFOU1EClfal9gI0eFZRM2K3OKXOjUSjfipK5B5SiGDtsjh3DBlmNzQ== + dependencies: + "@silencelaboratories/walletprovider-sdk" "^0.3.0" + "@commitlint/config-validator@^19.5.0": version "19.5.0" resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-19.5.0.tgz#f0a4eda2109fc716ef01bb8831af9b02e3a1e568" @@ -1779,7 +1796,7 @@ dependencies: "@noble/hashes" "1.5.0" -"@noble/curves@^1.4.0", "@noble/curves@^1.6.0", "@noble/curves@~1.7.0": +"@noble/curves@1.7.0", "@noble/curves@^1.4.0", "@noble/curves@^1.6.0", "@noble/curves@~1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45" integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw== @@ -1801,7 +1818,7 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5" integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ== -"@noble/hashes@^1.4.0", "@noble/hashes@^1.5.0", "@noble/hashes@~1.6.0": +"@noble/hashes@1.6.1", "@noble/hashes@^1.4.0", "@noble/hashes@^1.5.0", "@noble/hashes@~1.6.0": version "1.6.1" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.1.tgz#df6e5943edcea504bac61395926d6fd67869a0d5" integrity sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w== @@ -1941,6 +1958,14 @@ resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.1.0.tgz#cba454c05ec201bd5547aaf55286d44682ac8eb5" integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g== +"@rhinestone/module-sdk@^0.1.32": + version "0.1.32" + resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.32.tgz#32571e606b587de984bf56cba3569c91c0b7a2a6" + integrity sha512-wNzeIepHPhD0jiw3UOar+dAb6VKv4p8D4+947n0SLZHBdJ5S7moRCCL082NCo7Pz8+/C9kVriJASFeLZmJLo4g== + dependencies: + solady "^0.0.235" + tslib "^2.7.0" + "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" @@ -1974,7 +1999,7 @@ "@noble/hashes" "~1.5.0" "@scure/base" "~1.1.7" -"@scure/bip32@^1.5.0": +"@scure/bip32@1.6.0", "@scure/bip32@^1.5.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.0.tgz#6dbc6b4af7c9101b351f41231a879d8da47e0891" integrity sha512-82q1QfklrUUdXJzjuRU7iG7D7XiFx5PHYVS0+oeNKhyDLT7WPqs6pBcM2W5ZdwOwKCwoE1Vy1se+DHjcXwCYnA== @@ -1999,7 +2024,7 @@ "@noble/hashes" "~1.5.0" "@scure/base" "~1.1.8" -"@scure/bip39@^1.4.0": +"@scure/bip39@1.5.0", "@scure/bip39@^1.4.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.0.tgz#c8f9533dbd787641b047984356531d84485f19be" integrity sha512-Dop+ASYhnrwm9+HA/HwXg7j2ZqM6yk2fyLWb5znexjctFY3+E+eU8cIWI0Pql0Qx4hPZCijlGq4OL71g+Uz30A== @@ -2024,6 +2049,15 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== +"@silencelaboratories/walletprovider-sdk@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@silencelaboratories/walletprovider-sdk/-/walletprovider-sdk-0.3.0.tgz#c637fd3413e532fe6bd5f10eab9f194f56c1f6c6" + integrity sha512-5+yS95DwIufP0qOPuk81cXZ8gepcARXwuKRDAtjtf50JVX0MOMy6pR8f/1j5PATPdImzgI8905x3ZBgXfi+FKw== + dependencies: + "@noble/curves" "^1.6.0" + js-base64 "^3.7.7" + viem "2.21.32" + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -2918,6 +2952,11 @@ abitype@1.0.6, abitype@^1.0.6: resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.6.tgz#76410903e1d88e34f1362746e2d407513c38565b" integrity sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A== +abitype@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.7.tgz#876a0005d211e1c9132825d45bcee7b46416b284" + integrity sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -4096,6 +4135,11 @@ dotenv@^16.0.3: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.6.tgz#fc88e8a664087abf3e19d61e21f7feee1849bbb1" integrity sha512-JhcR/+KIjkkjiU8yEpaB/USlzVi3i5whwOjpIRNGi9svKEXZSe+Qp6IWAjFjv+2GViAoDRCUv/QLNziQxsLqDg== +dotenv@^16.4.5: + version "16.4.7" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" + integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -4174,15 +4218,6 @@ enhanced-resolve@^5.17.1: graceful-fs "^4.2.4" tapable "^2.2.0" -entry-point-gas-estimations@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/entry-point-gas-estimations/-/entry-point-gas-estimations-1.0.2.tgz#91e76fdcdca56f601a62a111579cc03c8112de05" - integrity sha512-ulYhxwPYPpam+EUOlM+LE7foRWeXYL6lZvzXMNOIvidqIBJxVkhX1fBTZRr6P8WNiwifSyo99iE+bFg5wdAHhA== - dependencies: - viem "^2.17.3" - zod "^3.22.4" - zod-validation-error "^2.1.0" - env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -6246,6 +6281,11 @@ joycon@^3.1.1: resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== +js-base64@^3.7.7: + version "3.7.7" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" + integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== + js-sha3@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -7880,6 +7920,11 @@ socks@^2.7.1: ip-address "^9.0.5" smart-buffer "^4.2.0" +solady@^0.0.235: + version "0.0.235" + resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.235.tgz#50ab6e403ed6935012df2c16803fcd58045f9a0e" + integrity sha512-JUEXLDG7ag3HmqUnrDG7ilhafH6R9bFPpwV63O2kH4UbnS2+gRGEOqqy4k01O7tHjo3MWkDD0cpG+UY9pjy/fQ== + sonic-boom@^3.0.0, sonic-boom@^3.7.0: version "3.8.1" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.1.tgz#d5ba8c4e26d6176c9a1d14d549d9ff579a163422" @@ -8245,7 +8290,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2: +tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2, tslib@^2.7.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -8469,17 +8514,32 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -viem@^2.17.3, viem@^2.21.28: - version "2.21.53" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.53.tgz#a5ba6da48e5edded27dde286e431ae97034f4fc4" - integrity sha512-0pY8clBacAwzc59iV1vY4a6U4xvRlA5tAuhClJCKvqA6rXJzmNMMvxQ0EG79lkHr7WtBEruXz8nAmONXwnq4EQ== +viem@2.21.32: + version "2.21.32" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.32.tgz#b7f43b2004967036f83500260290cee45189f62a" + integrity sha512-2oXt5JNIb683oy7C8wuIJ/SeL3XtHVMEQpy1U2TA6WMnJQ4ScssRvyPwYLcaP6mKlrGXE/cR/V7ncWpvLUVPYQ== dependencies: + "@adraffy/ens-normalize" "1.11.0" "@noble/curves" "1.6.0" "@noble/hashes" "1.5.0" "@scure/bip32" "1.5.0" "@scure/bip39" "1.4.0" abitype "1.0.6" isows "1.0.6" + webauthn-p256 "0.0.10" + ws "8.18.0" + +viem@^2.21.49: + version "2.21.54" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.54.tgz#76d6f86ab8809078f1ac140ac1a2beadbc86b9f6" + integrity sha512-G9mmtbua3UtnVY9BqAtWdNp+3AO+oWhD0B9KaEsZb6gcrOWgmA4rz02yqEMg+qW9m6KgKGie7q3zcHqJIw6AqA== + dependencies: + "@noble/curves" "1.7.0" + "@noble/hashes" "1.6.1" + "@scure/bip32" "1.6.0" + "@scure/bip39" "1.5.0" + abitype "1.0.7" + isows "1.0.6" ox "0.1.2" webauthn-p256 "0.0.10" ws "8.18.0"