UNPKG

@abstraxn/account

Version:

@abstraxn/account: Empower ERC-4337 smart accounts with seamless APIs for enhanced decentralized finance experiences.

734 lines 39.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstraxnSmartAccount = void 0; // Import necessary libraries and modules const ethers_1 = require("ethers"); const Constants_1 = require("../utils/Constants"); const Instances_1 = require("../utils/Instances"); const providers_1 = require("@ethersproject/providers"); const common_1 = require("@abstraxn/common"); const modules_1 = require("@abstraxn/modules"); const SmartAccount_v2_ABI_1 = require("../abi/SmartAccount_v2_ABI"); const utils_1 = require("ethers/lib/utils"); const lru_cache_1 = require("lru-cache"); const paymaster_1 = require("@abstraxn/paymaster"); // Class representing AbstraxnSmartAccount class AbstraxnSmartAccount { // Constructor for AbstraxnSmartAccount constructor(_config) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; // LRUCache for checking if a contract is deployed this.isContractDeployedCache = new lru_cache_1.LRUCache({ max: 500, }); // Initialize properties with provided configuration or default values this.chainId = _config.chainId; this.provider = (_a = _config.provider) !== null && _a !== void 0 ? _a : new providers_1.JsonRpcProvider(common_1.RPC_PROVIDER_URLS[this.chainId]); this.activeValidationModule = (_b = _config.defaultValidationModule) !== null && _b !== void 0 ? _b : modules_1.BaseValidationModule; this.defaultValidationModule = (_c = _config.defaultValidationModule) !== null && _c !== void 0 ? _c : modules_1.BaseValidationModule; this.bundler = _config.bundler; this.signer = _config.signer; this.index = (_d = _config.index) !== null && _d !== void 0 ? _d : 0; this.entryPointAddress = (_e = _config.entryPointAddress) !== null && _e !== void 0 ? _e : Constants_1.DEFAULT_ENTRYPOINT_ADDRESS; this.implementationAddress = (_f = _config.implementationAddress) !== null && _f !== void 0 ? _f : Constants_1.DEFAULT_IMPLEMENTATION_ADDRESS; this.smartFactoryAddress = (_g = _config.smartFactoryAddress) !== null && _g !== void 0 ? _g : Constants_1.FACTORY_ADDRESS; this.proxyCreationCode = (_h = _config.proxyCreationCode) !== null && _h !== void 0 ? _h : Constants_1.PROXY_CREATION_CODE; this.fallbackHandlerAddress = (_j = _config.fallbackHandlerAddress) !== null && _j !== void 0 ? _j : Constants_1.DEFAULT_FALLBACK_HANDLER_ADDRESS; if (_config.paymaster) { this.paymaster = _config.paymaster; } } // Static method to create an instance of AbstraxnSmartAccount static async create(abstraxnSmartAccountConfig) { const instance = new AbstraxnSmartAccount(abstraxnSmartAccountConfig); if (abstraxnSmartAccountConfig.defaultValidationModule) { instance.defaultValidationModule = abstraxnSmartAccountConfig.defaultValidationModule; } else { instance.defaultValidationModule = await modules_1.ECDSAOwnershipValidationModule.create({ signer: abstraxnSmartAccountConfig.signer, }); } return instance; } // Method to get the account address for the smart account async getAccountAddress() { const accountAddress = await this.getCounterFactualAddress(); return accountAddress; } // Check if the provider is defined isProviderDefined() { if (!this.provider) throw new Error("Provider is undefined"); return true; } // Check if an account is deployed at a given address async isAccountDeployed(address) { if (this.isContractDeployedCache.get(address)) { return true; } this.isProviderDefined(); let isDeployed = false; const contractCode = await this.provider.getCode(address); if (contractCode.length > 2) { this.isContractDeployedCache.set(address, true); isDeployed = true; } else { isDeployed = false; } return isDeployed; } // Get the initialization code for the smart account async getInitCode() { if (await this.isAccountDeployed(await this.getAccountAddress())) { return "0x"; } return this.getAccountInitCode(); } // Get the initialization code for creating a smart account async getAccountInitCode() { const factoryContract = this.smartFactoryAddress; const contract = (0, Instances_1.smartAccountFactoryContract)(this.provider, factoryContract); return (0, utils_1.hexConcat)([ factoryContract, contract.interface.encodeFunctionData("deployCounterFactualAccount", [ this.defaultValidationModule.getAddress(), await this.defaultValidationModule.getInitData(), this.index, ]), ]); } // Get the counterfactual address for the smart account async getCounterFactualAddress() { const validationModule = this.defaultValidationModule; const index = this.index; try { console.log('this.fallbackHandlerAddress', this.fallbackHandlerAddress); const contract = (0, Instances_1.smartAccount)(this.implementationAddress, this.provider); const initCalldata = contract.interface.encodeFunctionData("init", [ this.fallbackHandlerAddress, validationModule.getAddress(), await validationModule.getInitData(), ]); const proxyCreationCodeHash = (0, utils_1.solidityKeccak256)(["bytes", "uint256"], [this.proxyCreationCode, this.implementationAddress]); const salt = (0, utils_1.solidityKeccak256)(["bytes32", "uint256"], [(0, utils_1.keccak256)(initCalldata), index]); const counterFactualAddress = (0, utils_1.getCreate2Address)(this.smartFactoryAddress, salt, proxyCreationCodeHash); return counterFactualAddress; } catch (e) { throw new Error(`Failed to get counterfactual address, ${e}`); } } // Async method to get the smart account contract instance async _getAccountContract() { let accountContract = new ethers_1.ethers.Contract(await this.getAccountAddress(), SmartAccount_v2_ABI_1.SMART_ACCOUNTV2_ABI, this.provider); return accountContract; } // Async method to get the nonce for a specific nonce space (or default to 0) async getNonce(nonceKey) { const nonceSpace = nonceKey !== null && nonceKey !== void 0 ? nonceKey : 0; try { const accountContract = await this._getAccountContract(); const nonce = await accountContract.nonce(nonceSpace); return nonce; } catch (e) { console.debug("Failed to get nonce from deployed account. Returning 0 as nonce"); return ethers_1.BigNumber.from(0); } } // Private method to get the nonce for building user operations async getBuildUserOpNonce(nonceOptions) { var _a; let nonce = ethers_1.BigNumber.from(0); try { if (nonceOptions === null || nonceOptions === void 0 ? void 0 : nonceOptions.nonceOverride) { nonce = ethers_1.BigNumber.from(nonceOptions === null || nonceOptions === void 0 ? void 0 : nonceOptions.nonceOverride); } else { const _nonceSpace = (_a = nonceOptions === null || nonceOptions === void 0 ? void 0 : nonceOptions.nonceKey) !== null && _a !== void 0 ? _a : 0; nonce = await this.getNonce(_nonceSpace); } } catch (error) { // Not throwing this error as nonce would be 0 if this.getNonce() throws an exception, which is the expected flow for undeployed accounts console.log("Error while getting nonce for the account. This is expected for undeployed accounts; set nonce to 0"); } return nonce; } // Check if an active validation module is defined isActiveValidationModuleDefined() { if (!this.activeValidationModule) throw new Error("Must provide an instance of the active validation module."); return true; } // Async method to get a dummy signature based on the provided validation module async getDummySignature(params) { this.isActiveValidationModuleDefined(); return this.activeValidationModule.getDummySignature(params); } // Private method to get gas fee values, considering bundler and overrides async getGasFeeValues(overrides, skipBundlerGasEstimation) { const gasFeeValues = { maxFeePerGas: overrides === null || overrides === void 0 ? void 0 : overrides.maxFeePerGas, maxPriorityFeePerGas: overrides === null || overrides === void 0 ? void 0 : overrides.maxPriorityFeePerGas, }; try { if (this.bundler && !gasFeeValues.maxFeePerGas && !gasFeeValues.maxPriorityFeePerGas && (skipBundlerGasEstimation !== null && skipBundlerGasEstimation !== void 0 ? skipBundlerGasEstimation : true)) { const gasFeeEstimation = await this.bundler.getGasFeeValues(); gasFeeValues.maxFeePerGas = gasFeeEstimation.maxFeePerGas; gasFeeValues.maxPriorityFeePerGas = gasFeeEstimation.maxPriorityFeePerGas; } return gasFeeValues; } catch (error) { console.error("Error while getting gasFeeValues from the bundler. Provided bundler might not have the getGasFeeValues endpoint", error); return gasFeeValues; } } // Async method to encode an "execute" transaction for a single recipient async encodeExecute(to, value, data) { const accountContract = await this._getAccountContract(); return accountContract.interface.encodeFunctionData("execute", [to, value, data]); } // Async method to encode an "executeBatch" transaction for multiple recipients async encodeExecuteBatch(to, value, data) { const accountContract = await this._getAccountContract(); return accountContract.interface.encodeFunctionData("executeBatch", [to, value, data]); } // Validate if required fields are present in the UserOperation validateUserOp(userOp, requiredFields) { for (const field of requiredFields) { if (!userOp[field]) { throw new Error(`${String(field)} is missing in the UserOp`); } } return true; } // Async method to get the gas limit for the verification process async getVerificationGasLimit(initCode) { // Verification gas should be max(initGas(wallet deployment) + validateUserOp + validatePaymasterUserOp, postOp) const initGas = await this.estimateCreationGas(initCode); const validateUserOpGas = ethers_1.BigNumber.from(Constants_1.DefaultGasLimits.validatePaymasterUserOpGas + Constants_1.DefaultGasLimits.validateUserOpGas); const postOpGas = ethers_1.BigNumber.from(Constants_1.DefaultGasLimits.postOpGas); let verificationGasLimit = ethers_1.BigNumber.from(validateUserOpGas).add(initGas); if (ethers_1.BigNumber.from(postOpGas).gt(verificationGasLimit)) { verificationGasLimit = postOpGas; } if (this.defaultValidationModule.getAddress() === modules_1.DEFAULT_PASSKEY_MODULE && initCode != '0x') { verificationGasLimit = verificationGasLimit.add(600000); } return verificationGasLimit; } // Async method to get the gas limit for the verification process async getCallGasLimit(userOp) { let callGasLimit = await this.provider.estimateGas({ from: this.entryPointAddress, to: userOp.sender, data: userOp.callData, }); if (userOp.initCode && userOp.initCode != '0x') { callGasLimit = ethers_1.BigNumber.from(callGasLimit).add(100000); } return callGasLimit; } // Async method to estimate gas for the deployment of a smart contract async estimateCreationGas(initCode) { if (initCode == null || initCode === "0x") return 0; const deployerAddress = initCode.substring(0, 42); const deployerCallData = "0x" + initCode.substring(42); return this.provider.estimateGas({ to: deployerAddress, data: deployerCallData }); } // Async method to get the gas limit for the pre-verification step in the user operation async getPreVerificationGas(userOp) { return (0, Constants_1.calcPreVerificationGas)(userOp); } // Async method to calculate gas values for various steps in the user operation async calculateUserOpGasValues(userOp) { var _a, _b, _c, _d; if (!this.provider) throw new Error("Provider is not present for making rpc calls"); let feeData = null; if (userOp.maxFeePerGas === undefined || userOp.maxFeePerGas === null || userOp.maxPriorityFeePerGas === undefined || userOp.maxPriorityFeePerGas === null) { feeData = await this.provider.getFeeData(); } if (userOp.maxFeePerGas === undefined || userOp.maxFeePerGas === null) { userOp.maxFeePerGas = (_b = (_a = feeData === null || feeData === void 0 ? void 0 : feeData.maxFeePerGas) !== null && _a !== void 0 ? _a : feeData === null || feeData === void 0 ? void 0 : feeData.gasPrice) !== null && _b !== void 0 ? _b : (await this.provider.getGasPrice()); } if (userOp.maxPriorityFeePerGas === undefined || userOp.maxPriorityFeePerGas === null) { userOp.maxPriorityFeePerGas = (_d = (_c = feeData === null || feeData === void 0 ? void 0 : feeData.maxPriorityFeePerGas) !== null && _c !== void 0 ? _c : feeData === null || feeData === void 0 ? void 0 : feeData.gasPrice) !== null && _d !== void 0 ? _d : (await this.provider.getGasPrice()); } if (userOp.initCode) userOp.verificationGasLimit = await this.getVerificationGasLimit(userOp.initCode); userOp.callGasLimit = await this.getCallGasLimit(userOp); userOp.preVerificationGas = await this.getPreVerificationGas(userOp); return userOp; } // Async method to estimate gas values for a user operation async estimateUserOpGas(params) { var _a, _b, _c, _d; // Extract parameters let userOp = params.userOp; const { overrides, skipBundlerGasEstimation, paymasterServiceData } = params; const requiredFields = ["sender", "nonce", "initCode", "callData"]; // Validate required fields in userOp this.validateUserOp(userOp, requiredFields); let finalUserOp = userOp; const skipBundlerCall = skipBundlerGasEstimation !== null && skipBundlerGasEstimation !== void 0 ? skipBundlerGasEstimation : true; // Override gas values in userOp if provided in overrides params if (overrides) { userOp = { ...userOp, ...overrides }; } if (skipBundlerCall) { if (this.paymaster) { if ((0, Constants_1.isNullOrUndefined)(userOp.maxFeePerGas) || (0, Constants_1.isNullOrUndefined)(userOp.maxPriorityFeePerGas)) { throw new Error("maxFeePerGas and maxPriorityFeePerGas are required for skipBundlerCall mode"); } if ((paymasterServiceData === null || paymasterServiceData === void 0 ? void 0 : paymasterServiceData.mode) === paymaster_1.PaymasterMode.SPONSORED) { // Making call to paymaster to get gas estimations for userOp const { callGasLimit, verificationGasLimit, preVerificationGas, paymasterAndData } = await this.paymaster.getPaymasterAndData(userOp, paymasterServiceData); if (paymasterAndData === "0x" && (callGasLimit === undefined || verificationGasLimit === undefined || preVerificationGas === undefined)) { throw new Error("Since you intend to use sponsorship paymaster, please check and make sure policies are set on the dashboard"); } finalUserOp.verificationGasLimit = verificationGasLimit !== null && verificationGasLimit !== void 0 ? verificationGasLimit : userOp.verificationGasLimit; finalUserOp.callGasLimit = callGasLimit !== null && callGasLimit !== void 0 ? callGasLimit : userOp.callGasLimit; finalUserOp.preVerificationGas = preVerificationGas !== null && preVerificationGas !== void 0 ? preVerificationGas : userOp.preVerificationGas; finalUserOp.paymasterAndData = paymasterAndData !== null && paymasterAndData !== void 0 ? paymasterAndData : userOp.paymasterAndData; } else { // use dummy values for gas limits as fee quote call will ignore this later. finalUserOp.callGasLimit = Constants_1.DefaultGasLimit.callGasLimit; finalUserOp.verificationGasLimit = Constants_1.DefaultGasLimit.verificationGasLimit; finalUserOp.preVerificationGas = Constants_1.DefaultGasLimit.preVerificationGas; } } else { // Skip paymaster call and set paymasterAndData to "0x" console.warn("Skipped paymaster call. If you are using paymasterAndData, generate data externally"); finalUserOp = await this.calculateUserOpGasValues(userOp); finalUserOp.paymasterAndData = "0x"; } } else { if (!this.bundler) throw new Error("Bundler is not provided"); delete userOp.maxFeePerGas; delete userOp.maxPriorityFeePerGas; // Making a call to bundler to get gas estimations for userOp const { callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas } = await this.bundler.estimateUserOpGas(userOp); // If neither user sent gas fee nor the bundler, estimate gas from the provider if ((0, Constants_1.isNullOrUndefined)(userOp.maxFeePerGas) && (0, Constants_1.isNullOrUndefined)(userOp.maxPriorityFeePerGas) && ((0, Constants_1.isNullOrUndefined)(maxFeePerGas) || (0, Constants_1.isNullOrUndefined)(maxPriorityFeePerGas))) { const feeData = await this.provider.getFeeData(); finalUserOp.maxFeePerGas = (_b = (_a = feeData.maxFeePerGas) !== null && _a !== void 0 ? _a : feeData.gasPrice) !== null && _b !== void 0 ? _b : (await this.provider.getGasPrice()); finalUserOp.maxPriorityFeePerGas = (_d = (_c = feeData.maxPriorityFeePerGas) !== null && _c !== void 0 ? _c : feeData.gasPrice) !== null && _d !== void 0 ? _d : (await this.provider.getGasPrice()); } else { finalUserOp.maxFeePerGas = maxFeePerGas !== null && maxFeePerGas !== void 0 ? maxFeePerGas : userOp.maxFeePerGas; finalUserOp.maxPriorityFeePerGas = maxPriorityFeePerGas !== null && maxPriorityFeePerGas !== void 0 ? maxPriorityFeePerGas : userOp.maxPriorityFeePerGas; } finalUserOp.verificationGasLimit = verificationGasLimit !== null && verificationGasLimit !== void 0 ? verificationGasLimit : userOp.verificationGasLimit; finalUserOp.callGasLimit = callGasLimit !== null && callGasLimit !== void 0 ? callGasLimit : userOp.callGasLimit; finalUserOp.preVerificationGas = preVerificationGas !== null && preVerificationGas !== void 0 ? preVerificationGas : userOp.preVerificationGas; finalUserOp.paymasterAndData = "0x"; } finalUserOp = await (0, Constants_1.convertToFactor)(finalUserOp); return finalUserOp; } // Async method to build a UserOperation based on a list of transactions async buildUserOp(transactions, buildUseropDto) { var _a; // Extract transaction details const to = transactions.map((element) => element.to); const data = transactions.map((element) => { var _a; return (_a = element.data) !== null && _a !== void 0 ? _a : "0x"; }); const value = transactions.map((element) => { var _a; return (_a = element.value) !== null && _a !== void 0 ? _a : ethers_1.BigNumber.from("0"); }); // Fetch necessary data asynchronously const initCodeFetchPromise = this.getInitCode(); const dummySignatureFetchPromise = this.getDummySignature(buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.params); const [nonceFromFetch, initCode, signature, finalGasFeeValue] = await Promise.all([ this.getBuildUserOpNonce(buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.nonceOptions), initCodeFetchPromise, dummySignatureFetchPromise, this.getGasFeeValues(buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.overrides, buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.skipBundlerGasEstimation), ]); // Validate transactions array if (transactions.length === 0) { throw new Error("Transactions array cannot be empty"); } // Determine the callData based on the number of transactions and user preferences let callData = ""; if (transactions.length > 1 || (buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.forceEncodeForBatch)) { callData = await this.encodeExecuteBatch(to, value, data); } else { // transactions.length must be 1 callData = await this.encodeExecute(to[0], value[0], data[0]); } // Build the UserOperation object let userOp = { sender: await this.getAccountAddress(), nonce: nonceFromFetch, initCode, callData: callData, maxFeePerGas: finalGasFeeValue.maxFeePerGas || undefined, maxPriorityFeePerGas: finalGasFeeValue.maxPriorityFeePerGas || undefined, }; // For this Smart Account, the current validation module's dummy signature will be used to estimate gas userOp.signature = signature; // Note: Can change the default behavior of calling estimations using bundler/local userOp = await this.estimateUserOpGas({ userOp, overrides: buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.overrides, skipBundlerGasEstimation: buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.skipBundlerGasEstimation, paymasterServiceData: buildUseropDto === null || buildUseropDto === void 0 ? void 0 : buildUseropDto.paymasterServiceData, }); userOp.paymasterAndData = (_a = userOp.paymasterAndData) !== null && _a !== void 0 ? _a : "0x"; return userOp; } // sendUserOp to bundler async sendUserOp(userOp, params) { delete userOp.signature; const userOperation = await this.signUserOp(userOp, params); const bundlerResponse = await this.sendSignedUserOp(userOperation, params); return bundlerResponse; } // Method to sign a UserOperation async signUserOp(userOp, params) { // Ensure that an active validation module is defined this.isActiveValidationModuleDefined(); const requiredFields = [ "sender", "nonce", "initCode", "callData", "callGasLimit", "verificationGasLimit", "preVerificationGas", "maxFeePerGas", "maxPriorityFeePerGas", "paymasterAndData", ]; // Validate required fields in userOp this.validateUserOp(userOp, requiredFields); // Calculate the hash of the UserOperation const userOpHash = await this.getUserOpHash(userOp); // Sign the hash using the active validation module // Note: If the account is undeployed, use ERC-6492 // Review: Should only be needed for signMessage /*if (!(await this.isAccountDeployed(await this.getAccountAddress()))) { const coder = new ethers.utils.AbiCoder(); const populatedTransaction = await this.factory?.populateTransaction.deployCounterFactualAccount( await this.defaultValidationModule.getAddress(), await this.defaultValidationModule.getInitData(), this.index, ); moduleSig = coder.encode(["address", "bytes", "bytes"], [this.factoryAddress, populatedTransaction?.data, moduleSig]) + "6492649264926492649264926492649264926492649264926492649264926492"; // magic suffix userOp.signature = moduleSig; return userOp as UserOperation; }*/ // Encode the signature with the module address if (this.defaultValidationModule.getAddress().toLocaleLowerCase() === modules_1.DEFAULT_MULTISIG_MODULE.toLocaleLowerCase()) { const { validUntil, validAfter } = await this.validThru(); if (!params || !(params === null || params === void 0 ? void 0 : params.signatures)) { throw new Error("Signatures are not provided"); } const sigConcat = await this.buildSignatureBytes(params === null || params === void 0 ? void 0 : params.signatures); const coder = new ethers_1.ethers.utils.AbiCoder(); let signPacked = coder.encode(["uint48", "uint48", "bytes"], [validAfter, validUntil, sigConcat]); // let signPacked = ethers.utils.solidityPack(["uint48", "uint48", "bytes"], [validAfter, validUntil, sigConcat]) const packSig = coder.encode(["bytes", "address"], [signPacked, this.activeValidationModule.getAddress()]); // const packSig = ethers.utils.solidityPack(["bytes", "address"], [signPacked, this.activeValidationModule.getAddress()]); userOp.signature = packSig; } else if (this.defaultValidationModule.getAddress().toLocaleLowerCase() === modules_1.DEFAULT_PASSKEY_MODULE.toLocaleLowerCase()) { const { validUntil, validAfter } = await this.validThru(); const coder = new ethers_1.ethers.utils.AbiCoder(); const encodedSig = coder.encode(["bytes", "uint256", "uint256", "uint48", "uint48", "bytes", "string"], [params === null || params === void 0 ? void 0 : params.challenge, params === null || params === void 0 ? void 0 : params.r, params === null || params === void 0 ? void 0 : params.s, validUntil, validAfter, params === null || params === void 0 ? void 0 : params.authenticatorData, params === null || params === void 0 ? void 0 : params.clientDataJsonPost]); const signatureWithModuleAddress = ethers_1.ethers.utils.solidityPack(["bytes", "address"], [encodedSig, this.activeValidationModule.getAddress()]); userOp.signature = signatureWithModuleAddress; } else { const coder = new ethers_1.ethers.utils.AbiCoder(); const moduleSig = await this.activeValidationModule.signUserOpHash(userOpHash, params); const signatureWithModuleAddress = coder.encode(["bytes", "address"], [moduleSig, this.activeValidationModule.getAddress()]); // const signatureWithModuleAddress = ethers.utils.solidityPack( // ["bytes", "address"], // [moduleSig, this.activeValidationModule.getAddress()], // ); userOp.signature = signatureWithModuleAddress; } return userOp; } // Method to send a signed UserOperation to the bundler async sendSignedUserOp(userOp, params) { const requiredFields = [ "sender", "nonce", "initCode", "callData", "callGasLimit", "verificationGasLimit", "preVerificationGas", "maxFeePerGas", "maxPriorityFeePerGas", "paymasterAndData", "signature", ]; // Validate required fields in userOp this.validateUserOp(userOp, requiredFields); // Ensure that a bundler is provided if (!this.bundler) throw new Error("Bundler is not provided"); // Send the signed UserOperation to the bundler const bundlerResponse = await this.bundler.sendUserOp(userOp, params === null || params === void 0 ? void 0 : params.simulationType); return bundlerResponse; } // Method to calculate the hash of a UserOperation async getUserOpHash(userOp) { // Calculate the hash of the UserOperation using keccak256 const userOpHash = (0, utils_1.keccak256)((0, Constants_1.packUserOp)(userOp, true)); // Encode additional information for hashing const enc = utils_1.defaultAbiCoder.encode(["bytes32", "address", "uint256"], [userOpHash, this.entryPointAddress, this.chainId]); // Final hash calculation return (0, utils_1.keccak256)(enc); } validateUserOpAndPaymasterRequest(userOp, tokenPaymasterRequest) { var _a; if ((0, Constants_1.isNullOrUndefined)(userOp.callData)) { throw new Error("UserOp callData cannot be undefined"); } const feeTokenAddress = (_a = tokenPaymasterRequest === null || tokenPaymasterRequest === void 0 ? void 0 : tokenPaymasterRequest.feeQuote) === null || _a === void 0 ? void 0 : _a.tokenAddress; if (!feeTokenAddress || feeTokenAddress == ethers_1.ethers.constants.AddressZero) { throw new Error("Invalid or missing token address. Token address must be part of the feeQuote in tokenPaymasterRequest"); } const spender = tokenPaymasterRequest === null || tokenPaymasterRequest === void 0 ? void 0 : tokenPaymasterRequest.spender; if (!spender || spender == ethers_1.ethers.constants.AddressZero) { throw new Error("Invalid or missing spender address. Sepnder address must be part of tokenPaymasterRequest"); } } async buildTokenPaymasterUserOp(userOp, tokenPaymasterRequest) { this.validateUserOpAndPaymasterRequest(userOp, tokenPaymasterRequest); try { let batchTo = []; let batchValue = []; let batchData = []; let newCallData = userOp.callData; if (this.paymaster) { // Make a call to paymaster.buildTokenApprovalTransaction() with necessary details // Review: might request this form of an array of Transaction const approvalRequest = await this.paymaster.buildTokenApprovalTransaction(tokenPaymasterRequest, this.provider); if (approvalRequest.data == "0x" || approvalRequest.to == ethers_1.ethers.constants.AddressZero) { return userOp; } if ((0, Constants_1.isNullOrUndefined)(userOp.callData)) { throw new Error("UserOp callData cannot be undefined"); } const account = await this._getAccountContract(); const decodedSmartAccountData = account.interface.parseTransaction({ data: userOp.callData.toString(), }); if (!decodedSmartAccountData) { throw new Error("Could not parse userOp call data for this smart account"); } const smartAccountExecFunctionName = decodedSmartAccountData.name; if (smartAccountExecFunctionName === "execute" || smartAccountExecFunctionName === "execute_ncC") { const methodArgsSmartWalletExecuteCall = decodedSmartAccountData.args; const toOriginal = methodArgsSmartWalletExecuteCall[0]; const valueOriginal = methodArgsSmartWalletExecuteCall[1]; const dataOriginal = methodArgsSmartWalletExecuteCall[2]; batchTo.push(toOriginal); batchValue.push(valueOriginal); batchData.push(dataOriginal); } else if (smartAccountExecFunctionName === "executeBatch" || smartAccountExecFunctionName === "executeBatch_y6U") { const methodArgsSmartWalletExecuteCall = decodedSmartAccountData.args; batchTo = methodArgsSmartWalletExecuteCall[0]; batchValue = methodArgsSmartWalletExecuteCall[1]; batchData = methodArgsSmartWalletExecuteCall[2]; } if (approvalRequest.to && approvalRequest.data && approvalRequest.value) { batchTo = [approvalRequest.to, ...batchTo]; batchValue = [approvalRequest.value, ...batchValue]; batchData = [approvalRequest.data, ...batchData]; newCallData = await this.encodeExecuteBatch(batchTo, batchValue, batchData); const decodedSmartAccountDataNew = account.interface.parseTransaction({ data: newCallData.toString(), }); } const finalUserOp = { ...userOp, callData: newCallData, }; return finalUserOp; } } catch (error) { console.error("Failed to update callData with error", error); return userOp; } return userOp; } async enableModule(moduleAddress) { const tx = await this.getEnableModuleData(moduleAddress); const partialUserOp = await this.buildUserOp([tx]); return this.sendUserOp(partialUserOp); } async getEnableModuleData(moduleAddress) { const accountContract = await this._getAccountContract(); const data = accountContract.interface.encodeFunctionData("enableModule", [moduleAddress]); const tx = { to: await this.getAccountAddress(), value: "0", data: data, }; return tx; } async getSetupAndEnableModuleData(moduleAddress, moduleSetupData) { const accountContract = await this._getAccountContract(); const data = accountContract.interface.encodeFunctionData("setupAndEnableModule", [moduleAddress, moduleSetupData]); const tx = { to: await this.getAccountAddress(), value: "0", data: data, }; return tx; } async disableModule(prevModule, moduleAddress) { const tx = await this.getDisableModuleData(prevModule, moduleAddress); const partialUserOp = await this.buildUserOp([tx]); return this.sendUserOp(partialUserOp); } async getDisableModuleData(prevModule, moduleAddress) { const accountContract = await this._getAccountContract(); const data = accountContract.interface.encodeFunctionData("disableModule", [prevModule, moduleAddress]); const tx = { to: await this.getAccountAddress(), value: "0", data: data, }; return tx; } async isModuleEnabled(moduleName) { const accountContract = await this._getAccountContract(); return accountContract.isModuleEnabled(moduleName); } async validThru() { var today = new Date(); var firstMinute = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0); var lastMinute = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59); var firstMinuteTimestamp = firstMinute.getTime(); var lastMinuteTimestamp = lastMinute.getTime(); const hex = (Number((lastMinuteTimestamp.valueOf() / 1000).toFixed(0))).toString(16); const hex1 = (Number((firstMinuteTimestamp.valueOf() / 1000).toFixed(0))).toString(16); let str = '0x'; let str1 = '0x'; for (let i = 0; i < 14 - hex.length; i++) { str += '0'; } for (let i = 0; i < 14 - hex1.length; i++) { str1 += '0'; } str += hex; str1 += hex1; return { validUntil: str, validAfter: str1 }; } async getSignForMultiSigWallet(signer, userOp) { const { validUntil, validAfter } = await this.validThru(); let signature = await signer._signTypedData({ chainId: this.chainId, verifyingContract: modules_1.DEFAULT_MULTISIG_MODULE, }, { SafeOp: [ { name: "safe", type: "address", }, { name: "nonce", type: "uint256", }, { name: "initCode", type: "bytes", }, { name: "callData", type: "bytes", }, { name: "callGasLimit", type: "uint256", }, { name: "verificationGasLimit", type: "uint256", }, { name: "preVerificationGas", type: "uint256", }, { name: "maxFeePerGas", type: "uint256", }, { name: "maxPriorityFeePerGas", type: "uint256", }, { name: "paymasterAndData", type: "bytes", }, { name: "validAfter", type: "uint48", }, { name: "validUntil", type: "uint48", }, { name: "entryPoint", type: "address", }, ], }, { safe: userOp.sender, nonce: userOp.nonce, initCode: userOp.initCode, callData: userOp.callData, callGasLimit: userOp.callGasLimit, verificationGasLimit: userOp.verificationGasLimit, preVerificationGas: userOp.preVerificationGas, maxFeePerGas: userOp.maxFeePerGas, maxPriorityFeePerGas: userOp.maxPriorityFeePerGas, paymasterAndData: userOp.paymasterAndData, validAfter: validAfter, validUntil: validUntil, entryPoint: this.entryPointAddress, }); return signature; } async buildSignatureBytes(signs) { let signatureBytes = "0x"; for (const sig of signs) { signatureBytes += sig.slice(2); } return signatureBytes; } } exports.AbstraxnSmartAccount = AbstraxnSmartAccount; //# sourceMappingURL=AbstraxnSmartAccount.service.js.map