UNPKG

@biconomy/modules

Version:

This package provides different validation modules/plugins for ERC4337 compatible modular account

255 lines 12.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SessionKeyManagerModule = void 0; const ethers_1 = require("ethers"); const merkletreejs_1 = __importDefault(require("merkletreejs")); const common_1 = require("@biconomy/common"); const utils_1 = require("ethers/lib/utils"); const ethereumjs_util_1 = require("ethereumjs-util"); const Types_1 = require("./utils/Types"); const node_client_1 = __importDefault(require("@biconomy/node-client")); const Constants_1 = require("./utils/Constants"); const Uid_1 = require("./utils/Uid"); const BaseValidationModule_1 = require("./BaseValidationModule"); const SessionLocalStorage_1 = require("./session-storage/SessionLocalStorage"); class SessionKeyManagerModule extends BaseValidationModule_1.BaseValidationModule { /** * This constructor is private. Use the static create method to instantiate SessionKeyManagerModule * @param moduleConfig The configuration for the module * @returns An instance of SessionKeyManagerModule */ constructor(moduleConfig) { super(moduleConfig); this.version = "V1_0_0"; this.mockEcdsaSessionKeySig = "0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b"; /** * Method to create session data for any module. The session data is used to create a leaf in the merkle tree * @param leavesData The data of one or more leaves to be used to create session data * @returns The session data */ this.createSessionData = async (leavesData) => { var _a; const sessionKeyManagerModuleABI = "function setMerkleRoot(bytes32 _merkleRoot)"; const sessionKeyManagerModuleInterface = new ethers_1.ethers.utils.Interface([sessionKeyManagerModuleABI]); const leavesToAdd = []; const sessionIDInfo = []; for (const leafData of leavesData) { const leafDataHex = (0, utils_1.hexConcat)([ (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(leafData.validUntil), 6), (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(leafData.validAfter), 6), (0, utils_1.hexZeroPad)(leafData.sessionValidationModule, 20), leafData.sessionKeyData, ]); const generatedSessionId = (_a = leafData.preferredSessionId) !== null && _a !== void 0 ? _a : (0, Uid_1.generateRandomHex)(); leavesToAdd.push(ethers_1.ethers.utils.keccak256(leafDataHex)); sessionIDInfo.push(generatedSessionId); const sessionLeafNode = { ...leafData, sessionID: generatedSessionId, status: "PENDING", }; await this.sessionStorageClient.addSessionData(sessionLeafNode); } this.merkleTree.addLeaves(leavesToAdd); const leaves = this.merkleTree.getLeaves(); const newMerkleTree = new merkletreejs_1.default(leaves, ethereumjs_util_1.keccak256, { sortPairs: true, hashLeaves: false, }); this.merkleTree = newMerkleTree; const setMerkleRootData = sessionKeyManagerModuleInterface.encodeFunctionData("setMerkleRoot", [this.merkleTree.getHexRoot()]); await this.sessionStorageClient.setMerkleRoot(this.merkleTree.getHexRoot()); return { data: setMerkleRootData, sessionIDInfo: sessionIDInfo, }; }; } /** * Asynchronously creates and initializes an instance of SessionKeyManagerModule * @param moduleConfig The configuration for the module * @returns A Promise that resolves to an instance of SessionKeyManagerModule */ static async create(moduleConfig) { var _a; const instance = new SessionKeyManagerModule(moduleConfig); if (moduleConfig.moduleAddress) { instance.moduleAddress = moduleConfig.moduleAddress; } else if (moduleConfig.version) { const moduleAddr = Constants_1.SESSION_MANAGER_MODULE_ADDRESSES_BY_VERSION[moduleConfig.version]; if (!moduleAddr) { throw new Error(`Invalid version ${moduleConfig.version}`); } instance.moduleAddress = moduleAddr; instance.version = moduleConfig.version; } else { instance.moduleAddress = Constants_1.DEFAULT_SESSION_KEY_MANAGER_MODULE; // Note: in this case Version remains the default one } instance.nodeClient = new node_client_1.default({ txServiceUrl: (_a = moduleConfig.nodeClientUrl) !== null && _a !== void 0 ? _a : common_1.NODE_CLIENT_URL, }); if (moduleConfig.sessionStorageClient) { instance.sessionStorageClient = moduleConfig.sessionStorageClient; } else { switch (moduleConfig.storageType) { case Types_1.StorageType.LOCAL_STORAGE: instance.sessionStorageClient = new SessionLocalStorage_1.SessionLocalStorage(moduleConfig.smartAccountAddress); break; default: instance.sessionStorageClient = new SessionLocalStorage_1.SessionLocalStorage(moduleConfig.smartAccountAddress); } } const existingSessionData = await instance.sessionStorageClient.getAllSessionData(); const existingSessionDataLeafs = existingSessionData.map((sessionData) => { const leafDataHex = (0, utils_1.hexConcat)([ (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(sessionData.validUntil), 6), (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(sessionData.validAfter), 6), (0, utils_1.hexZeroPad)(sessionData.sessionValidationModule, 20), sessionData.sessionKeyData, ]); return ethers_1.ethers.utils.keccak256(leafDataHex); }); instance.merkleTree = new merkletreejs_1.default(existingSessionDataLeafs, ethereumjs_util_1.keccak256, { sortPairs: true, hashLeaves: false, }); return instance; } /** * This method is used to sign the user operation using the session signer * @param userOp The user operation to be signed * @param sessionSigner The signer to be used to sign the user operation * @returns The signature of the user operation */ async signUserOpHash(userOpHash, params) { if (!(params && params.sessionSigner)) { throw new Error("Session signer is not provided."); } const sessionSigner = params.sessionSigner; // Use the sessionSigner to sign the user operation const signature = await sessionSigner.signMessage((0, utils_1.arrayify)(userOpHash)); const sessionSignerData = await this.getLeafInfo(params); const leafDataHex = (0, utils_1.hexConcat)([ (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(sessionSignerData.validUntil), 6), (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(sessionSignerData.validAfter), 6), (0, utils_1.hexZeroPad)(sessionSignerData.sessionValidationModule, 20), sessionSignerData.sessionKeyData, ]); // Generate the padded signature with (validUntil,validAfter,sessionVerificationModuleAddress,validationData,merkleProof,signature) let paddedSignature = utils_1.defaultAbiCoder.encode(["uint48", "uint48", "address", "bytes", "bytes32[]", "bytes"], [ sessionSignerData.validUntil, sessionSignerData.validAfter, sessionSignerData.sessionValidationModule, sessionSignerData.sessionKeyData, this.merkleTree.getHexProof(ethers_1.ethers.utils.keccak256(leafDataHex)), signature, ]); if (params === null || params === void 0 ? void 0 : params.additionalSessionData) { paddedSignature += params.additionalSessionData; } return paddedSignature; } async getLeafInfo(params) { if (!(params && params.sessionSigner)) { throw new Error("Session signer is not provided."); } const sessionSigner = params.sessionSigner; let sessionSignerData; if (params === null || params === void 0 ? void 0 : params.sessionID) { sessionSignerData = await this.sessionStorageClient.getSessionData({ sessionID: params.sessionID, }); } else if (params === null || params === void 0 ? void 0 : params.sessionValidationModule) { sessionSignerData = await this.sessionStorageClient.getSessionData({ sessionValidationModule: params.sessionValidationModule, sessionPublicKey: await sessionSigner.getAddress(), }); } else { throw new Error("sessionID or sessionValidationModule should be provided."); } return sessionSignerData; } /** * Update the session data pending state to active * @param param The search param to find the session data * @param status The status to be updated * @returns */ async updateSessionStatus(param, status) { this.sessionStorageClient.updateSessionStatus(param, status); } /** * @remarks This method is used to clear all the pending sessions * @returns */ async clearPendingSessions() { this.sessionStorageClient.clearPendingSessions(); } /** * @returns SessionKeyManagerModule address */ getAddress() { return this.moduleAddress; } /** * @remarks This is the version of the module contract */ async getSigner() { throw new Error("Method not implemented."); } /** * @remarks This is the dummy signature for the module, used in buildUserOp for bundler estimation * @returns Dummy signature */ async getDummySignature(params) { common_1.Logger.log("moduleInfo ", params); if (!params) { throw new Error("Session signer is not provided."); } const sessionSignerData = await this.getLeafInfo(params); const leafDataHex = (0, utils_1.hexConcat)([ (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(sessionSignerData.validUntil), 6), (0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(sessionSignerData.validAfter), 6), (0, utils_1.hexZeroPad)(sessionSignerData.sessionValidationModule, 20), sessionSignerData.sessionKeyData, ]); // Generate the padded signature with (validUntil,validAfter,sessionVerificationModuleAddress,validationData,merkleProof,signature) let paddedSignature = utils_1.defaultAbiCoder.encode(["uint48", "uint48", "address", "bytes", "bytes32[]", "bytes"], [ sessionSignerData.validUntil, sessionSignerData.validAfter, sessionSignerData.sessionValidationModule, sessionSignerData.sessionKeyData, this.merkleTree.getHexProof(ethers_1.ethers.utils.keccak256(leafDataHex)), this.mockEcdsaSessionKeySig, ]); if (params === null || params === void 0 ? void 0 : params.additionalSessionData) { paddedSignature += params.additionalSessionData; } const dummySig = ethers_1.ethers.utils.defaultAbiCoder.encode(["bytes", "address"], [paddedSignature, this.getAddress()]); return dummySig; } /** * @remarks Other modules may need additional attributes to build init data */ async getInitData() { throw new Error("Method not implemented."); } /** * @remarks This Module dont have knowledge of signer. So, this method is not implemented */ async signMessage(message) { common_1.Logger.log("message", message); throw new Error("Method not implemented."); } } exports.SessionKeyManagerModule = SessionKeyManagerModule; //# sourceMappingURL=SessionKeyManagerModule.js.map