@abstraxn/bundler
Version:
Abstraxn Bundler package to interact with any bundler node as per ERC4337 standard
244 lines • 10.8 kB
JavaScript
;
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