@biconomy/modules
Version:
This package provides different validation modules/plugins for ERC4337 compatible modular account
255 lines • 12.4 kB
JavaScript
;
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