UNPKG

@abstraxn/bundler

Version:

Abstraxn Bundler package to interact with any bundler node as per ERC4337 standard

244 lines 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Bundler = void 0; const utils_1 = require("ethers/lib/utils"); const common_1 = require("@abstraxn/common"); const HelperFunction_1 = require("./utils/HelperFunction"); const Constants_1 = require("./utils/Constants"); const providers_1 = require("@ethersproject/providers"); const httpRequests_1 = require("./utils/httpRequests"); /** * This class implements IBundler interface. * Implementation sends UserOperation to a bundler URL as per ERC4337 standard. * Checkout the proposal for more details on Bundlers. */ class Bundler { constructor(bundlerConfig) { this.bundlerConfig = bundlerConfig; this.UserOpReceiptIntervals = { ...Constants_1.UserOpReceiptIntervals, ...bundlerConfig.userOpReceiptIntervals, }; this.UserOpWaitForTxHashIntervals = { ...Constants_1.UserOpWaitForTxHashIntervals, ...bundlerConfig.userOpWaitForTxHashIntervals, }; this.UserOpReceiptMaxDurationIntervals = { ...Constants_1.UserOpReceiptMaxDurationIntervals, ...bundlerConfig.userOpReceiptMaxDurationIntervals, }; this.UserOpWaitForTxHashMaxDurationIntervals = { ...Constants_1.UserOpWaitForTxHashMaxDurationIntervals, ...bundlerConfig.userOpWaitForTxHashMaxDurationIntervals, }; } getBundlerUrl() { return `${this.bundlerConfig.bundlerUrl}`; } /** * * @param chainId * @description This function will fetch gasPrices from bundler * @returns Promise<UserOpGasPricesResponse> */ async estimateUserOpGas(userOp) { // expected dummySig and possibly dummmy paymasterAndData should be provided by the caller // bundler doesn't know account and paymaster implementation userOp = (0, HelperFunction_1.transformUserOP)(userOp); const bundlerUrl = this.getBundlerUrl(); const response = await (0, httpRequests_1.sendRequest)({ url: bundlerUrl, method: httpRequests_1.HttpMethod.Post, body: { method: "eth_estimateUserOperationGas", params: [userOp, this.bundlerConfig.entryPointAddress], id: (0, HelperFunction_1.getTimestampInSeconds)(), jsonrpc: "2.0", }, }); const userOpGasResponse = response.result; for (const key in userOpGasResponse) { if (key === "maxFeePerGas" || key === "maxPriorityFeePerGas") continue; if (!userOpGasResponse[key]) { throw new Error(`Got undefined ${key} from bundler`); } } return userOpGasResponse; } /** * * @param userOp * @description This function will send signed userOp to bundler to get mined on chain * @returns Promise<UserOpResponse> */ async sendUserOp(userOp, simulationType) { const chainId = this.bundlerConfig.chainId; // transformUserOP will convert all bigNumber values to string userOp = (0, HelperFunction_1.transformUserOP)(userOp); const hexifiedUserOp = (0, HelperFunction_1.deepHexlify)(await (0, utils_1.resolveProperties)(userOp)); const simType = { simulation_type: simulationType || "validation", }; const params = [hexifiedUserOp, this.bundlerConfig.entryPointAddress, simType]; const bundlerUrl = this.getBundlerUrl(); const sendUserOperationResponse = await (0, httpRequests_1.sendRequest)({ url: bundlerUrl, method: httpRequests_1.HttpMethod.Post, body: { method: "eth_sendUserOperation", params: params, id: (0, HelperFunction_1.getTimestampInSeconds)(), jsonrpc: "2.0", }, }); const response = { message: sendUserOperationResponse.message, userOpHash: sendUserOperationResponse.result, wait: (confirmations) => { const provider = new providers_1.JsonRpcProvider(common_1.RPC_PROVIDER_URLS[chainId]); // Note: maxDuration can be defined per chainId const maxDuration = this.UserOpReceiptMaxDurationIntervals[chainId] || 30000; // default 30 seconds let totalDuration = 0; return new Promise((resolve, reject) => { const intervalValue = this.UserOpReceiptIntervals[chainId] || 5000; // default 5 seconds const intervalId = setInterval(async () => { try { const userOpResponse = await this.getUserOpReceipt(sendUserOperationResponse.result); if (userOpResponse && userOpResponse.receipt && userOpResponse.receipt.blockNumber) { if (confirmations) { const latestBlock = await provider.getBlockNumber(); const confirmedBlocks = latestBlock - userOpResponse.receipt.blockNumber; if (confirmations >= confirmedBlocks) { clearInterval(intervalId); resolve(userOpResponse); } } clearInterval(intervalId); resolve(userOpResponse); } } catch (error) { clearInterval(intervalId); reject(error); } totalDuration += intervalValue; if (totalDuration >= maxDuration) { clearInterval(intervalId); reject(new Error(`Exceeded maximum duration (${maxDuration / 1000} sec) waiting to get receipt for userOpHash ${sendUserOperationResponse.result}. Try getting the receipt manually using eth_getUserOperationReceipt rpc method on bundler`)); } }, intervalValue); }); }, waitForTxHash: () => { const maxDuration = this.UserOpWaitForTxHashMaxDurationIntervals[chainId] || 20000; // default 20 seconds let totalDuration = 0; return new Promise((resolve, reject) => { const intervalValue = this.UserOpWaitForTxHashIntervals[chainId] || 500; // default 0.5 seconds const intervalId = setInterval(() => { this.getUserOpStatus(sendUserOperationResponse.result) .then((userOpStatus) => { if (userOpStatus && userOpStatus.state && userOpStatus.transactionHash) { clearInterval(intervalId); resolve(userOpStatus); } }) .catch((error) => { clearInterval(intervalId); reject(error); }); totalDuration += intervalValue; if (totalDuration >= maxDuration) { clearInterval(intervalId); reject(new Error(`Exceeded maximum duration (${maxDuration / 1000} sec) waiting to get receipt for userOpHash ${sendUserOperationResponse.result}. Try getting the receipt manually using eth_getUserOperationReceipt rpc method on bundler`)); } }, intervalValue); }); }, }; return response; } /** * * @param userOpHash * @description This function will return userOpReceipt for a given userOpHash * @returns Promise<UserOpReceipt> */ async getUserOpReceipt(userOpHash) { const bundlerUrl = this.getBundlerUrl(); const response = await (0, httpRequests_1.sendRequest)({ url: bundlerUrl, method: httpRequests_1.HttpMethod.Post, body: { method: "eth_getUserOperationReceipt", params: [userOpHash], id: (0, HelperFunction_1.getTimestampInSeconds)(), jsonrpc: "2.0", }, }); const userOpReceipt = response.result; return userOpReceipt; } /** * * @param userOpHash * @description This function will return userOpReceipt for a given userOpHash * @returns Promise<UserOpReceipt> */ async getUserOpStatus(userOpHash) { const bundlerUrl = this.getBundlerUrl(); const response = await (0, httpRequests_1.sendRequest)({ url: bundlerUrl, method: httpRequests_1.HttpMethod.Post, body: { method: "abstraxn_getUserOperationStatus", params: [userOpHash], id: (0, HelperFunction_1.getTimestampInSeconds)(), jsonrpc: "2.0", }, }); const userOpStatus = response.result; return userOpStatus; } /** * * @param userOpHash * @param chainId * @description this function will return UserOpByHashResponse for given UserOpHash * @returns Promise<UserOpByHashResponse> */ async getUserOpByHash(userOpHash) { const bundlerUrl = this.getBundlerUrl(); const response = await (0, httpRequests_1.sendRequest)({ url: bundlerUrl, method: httpRequests_1.HttpMethod.Post, body: { method: "eth_getUserOperationByHash", params: [userOpHash], id: (0, HelperFunction_1.getTimestampInSeconds)(), jsonrpc: "2.0", }, }); const userOpByHashResponse = response.result; return userOpByHashResponse; } /** * @description This function will return the gas fee values */ async getGasFeeValues() { const bundlerUrl = this.getBundlerUrl(); const response = await (0, httpRequests_1.sendRequest)({ url: bundlerUrl, method: httpRequests_1.HttpMethod.Post, body: { method: "abstraxn_getGasFeeValues", params: [], id: (0, HelperFunction_1.getTimestampInSeconds)(), jsonrpc: "2.0", }, }); return response.result; } } exports.Bundler = Bundler; //# sourceMappingURL=Bundler.js.map