@biconomy/modules
Version:
This package provides different validation modules/plugins for ERC4337 compatible modular account
132 lines • 7.13 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiChainValidationModule = void 0;
const common_1 = require("@biconomy/common");
const ethers_1 = require("ethers");
const merkletreejs_1 = __importDefault(require("merkletreejs"));
const Constants_1 = require("./utils/Constants");
const utils_1 = require("ethers/lib/utils");
const BaseValidationModule_1 = require("./BaseValidationModule");
const aa_core_1 = require("@alchemy/aa-core");
class MultiChainValidationModule extends BaseValidationModule_1.BaseValidationModule {
constructor(moduleConfig) {
super(moduleConfig);
this.version = "V1_0_0";
}
static async create(moduleConfig) {
const instance = new MultiChainValidationModule(moduleConfig);
if (moduleConfig.moduleAddress) {
instance.moduleAddress = moduleConfig.moduleAddress;
}
else if (moduleConfig.version) {
const moduleAddr = Constants_1.MULTICHAIN_VALIDATION_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_MULTICHAIN_MODULE;
// Note: in this case Version remains the default one
}
instance.signer = moduleConfig.signer;
return instance;
}
getAddress() {
return this.moduleAddress;
}
async getSigner() {
return Promise.resolve(this.signer);
}
async getDummySignature() {
const moduleAddress = ethers_1.ethers.utils.getAddress(this.getAddress());
const dynamicPart = moduleAddress.substring(2).padEnd(40, "0");
return `0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000${dynamicPart}000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000`;
}
// Note: other modules may need additional attributes to build init data
async getInitData() {
const ecdsaOwnerAddress = await this.signer.getAddress();
const moduleRegistryAbi = "function initForSmartAccount(address owner)";
const ecdsaModuleRegistryInterface = new ethers_1.ethers.utils.Interface([moduleRegistryAbi]);
const ecdsaOwnershipInitData = ecdsaModuleRegistryInterface.encodeFunctionData("initForSmartAccount", [ecdsaOwnerAddress]);
return ecdsaOwnershipInitData;
}
async signUserOpHash(userOpHash) {
const sig = await this.signer.signMessage((0, utils_1.arrayify)(userOpHash));
common_1.Logger.log("ecdsa signature ", sig);
return sig;
}
/**
* Signs a message using the appropriate method based on the type of signer.
*
* @param {Bytes | string | Uint8Array} message - The message to be signed.
* @returns {Promise<string>} A promise resolving to the signature or error message.
* @throws {Error} If the signer type is invalid or unsupported.
*/
async signMessage(message) {
if (this.signer instanceof aa_core_1.WalletClientSigner) {
return super.signMessageWalletClientSigner(message, this.signer);
}
else if (this.signer instanceof ethers_1.Signer) {
return super.signMessageSigner(message, this.signer);
}
else {
throw new Error("Invalid signer type");
}
}
async signUserOps(multiChainUserOps) {
var _a, _b, _c, _d;
try {
const leaves = [];
// Iterate over each userOp and process them
for (const multiChainOp of multiChainUserOps) {
const validUntil = (_a = multiChainOp.validUntil) !== null && _a !== void 0 ? _a : 0;
const validAfter = (_b = multiChainOp.validAfter) !== null && _b !== void 0 ? _b : 0;
const leaf = (0, utils_1.hexConcat)([
(0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(validUntil), 6),
(0, utils_1.hexZeroPad)(ethers_1.ethers.utils.hexlify(validAfter), 6),
(0, utils_1.hexZeroPad)((0, common_1.getUserOpHash)(multiChainOp.userOp, this.entryPointAddress, multiChainOp.chainId), 32),
]);
leaves.push((0, utils_1.keccak256)(leaf));
}
// Create a new Merkle tree using the leaves array
const merkleTree = new merkletreejs_1.default(leaves, utils_1.keccak256, { sortPairs: true });
let multichainSignature = await this.signer.signMessage((0, utils_1.arrayify)(merkleTree.getHexRoot()));
const potentiallyIncorrectV = parseInt(multichainSignature.slice(-2), 16);
if (![27, 28].includes(potentiallyIncorrectV)) {
const correctV = potentiallyIncorrectV + 27;
multichainSignature = multichainSignature.slice(0, -2) + correctV.toString(16);
}
// Create an array to store updated userOps
const updatedUserOps = [];
common_1.Logger.log("merkle root ", merkleTree.getHexRoot());
for (let i = 0; i < leaves.length; i++) {
const merkleProof = merkleTree.getHexProof(leaves[i]);
common_1.Logger.log("merkle proof ", merkleProof);
const validUntil = (_c = multiChainUserOps[i].validUntil) !== null && _c !== void 0 ? _c : 0;
const validAfter = (_d = multiChainUserOps[i].validAfter) !== null && _d !== void 0 ? _d : 0;
// Create the moduleSignature
const moduleSignature = utils_1.defaultAbiCoder.encode(["uint48", "uint48", "bytes32", "bytes32[]", "bytes"], [validUntil, validAfter, merkleTree.getHexRoot(), merkleProof, multichainSignature]);
// Note: Because accountV2 does not directly call this method. hence we need to add validation module address to the signature
const signatureWithModuleAddress = utils_1.defaultAbiCoder.encode(["bytes", "address"], [moduleSignature, this.getAddress()]);
// Update userOp with the final signature
const updatedUserOp = {
...multiChainUserOps[i].userOp,
signature: signatureWithModuleAddress,
};
updatedUserOps.push(updatedUserOp);
}
return updatedUserOps;
}
catch (error) {
common_1.Logger.error("Error in signing multi chain userops", error);
throw new Error(JSON.stringify(error));
}
}
}
exports.MultiChainValidationModule = MultiChainValidationModule;
//# sourceMappingURL=MultichainValidationModule.js.map