From c49217caa2395822ee18b69cc82b925afc79d63b Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 20 Aug 2023 18:27:24 +0530 Subject: [PATCH 01/10] init --- .../modules/src/MultichainValidationModule.ts | 39 +++++++++++++++++++ packages/modules/src/utils/Constants.ts | 6 +++ 2 files changed, 45 insertions(+) diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index e69de29bb..80ae69f13 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -0,0 +1,39 @@ +import { UserOperation, ChainId } from '@biconomy/core-types' +import { Logger, getUserOpHash } from '@biconomy/common' +import MerkleTree from 'merkletreejs' +import { ECDSAOwnershipValidationModule } from "./ECDSAOwnershipValidationModule"; +import { MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION } from "./utils/Constants"; +import { ECDSAOwnershipValidationModuleConfig, ModuleVersion } from "./utils/Types"; +export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { + + constructor(moduleConfig: ECDSAOwnershipValidationModuleConfig) { + super(moduleConfig) + + if (moduleConfig.moduleAddress) { + this.moduleAddress = moduleConfig.moduleAddress + } else if (moduleConfig.version) { + const moduleAddr = MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION[moduleConfig.version] + if (!moduleAddr) { + throw new Error(`Invalid version ${moduleConfig.version}`) + } + this.moduleAddress = moduleAddr + this.version = moduleConfig.version as ModuleVersion + } + } + + // TODO: Actually depends on the module address so maybe we can make it dynamic + getDummySignature(): string { + return '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000F993fc8Dc0EE7aece7abf0d6B6939f9d67875dBa000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' + } + + async signUserOp(userOp: UserOperation): Promise { + Logger.log('userOp', userOp) + throw new Error("Method not implemented."); + } + + async signUserOps(userOps: UserOperation[]): Promise { + Logger.log('userOps', userOps) + throw new Error("Method not implemented."); + } + +} \ No newline at end of file diff --git a/packages/modules/src/utils/Constants.ts b/packages/modules/src/utils/Constants.ts index b7da723c2..44a802856 100644 --- a/packages/modules/src/utils/Constants.ts +++ b/packages/modules/src/utils/Constants.ts @@ -27,6 +27,12 @@ export const SESSION_MANAGER_MODULE_ADDRESSES_BY_VERSION = { // and so on } +export const MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION = { + V1_0_0: '0xCCCC01Bef3F9a28814b88aC36a819e96eec47E15' + // 'V1_0_1' : '0xd9cf3caaa21db25f16ad6db43eb9932ab77c8e76' + // and so on +} + export const DEFAULT_SESSION_KEY_MODULE = '0x0000000000000000000000000000000000000000' // similarly others here or in module / signer classes From 1d87be7c0d3cc92ece3aec4f1de567a28b1d47f6 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Mon, 21 Aug 2023 00:20:15 +0530 Subject: [PATCH 02/10] signUserOps in multichain validation module --- .../modules/src/MultichainValidationModule.ts | 105 +++++++++++++----- packages/modules/src/utils/Types.ts | 7 +- 2 files changed, 83 insertions(+), 29 deletions(-) diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 80ae69f13..922ff7c49 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -1,39 +1,88 @@ import { UserOperation, ChainId } from '@biconomy/core-types' import { Logger, getUserOpHash } from '@biconomy/common' +import { ethers } from 'ethers' import MerkleTree from 'merkletreejs' -import { ECDSAOwnershipValidationModule } from "./ECDSAOwnershipValidationModule"; -import { MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION } from "./utils/Constants"; -import { ECDSAOwnershipValidationModuleConfig, ModuleVersion } from "./utils/Types"; +import { ECDSAOwnershipValidationModule } from './ECDSAOwnershipValidationModule' +import { MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION } from './utils/Constants' +import { keccak256, arrayify, defaultAbiCoder } from 'ethers/lib/utils' +import { + ECDSAOwnershipValidationModuleConfig, + ModuleVersion, + MultiChainUserOpDto +} from './utils/Types' export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { + // Review: this.chainId should not be in spec of this class + // May not inherit from ECDSAOwnershipValidationModule - constructor(moduleConfig: ECDSAOwnershipValidationModuleConfig) { - super(moduleConfig) - - if (moduleConfig.moduleAddress) { - this.moduleAddress = moduleConfig.moduleAddress - } else if (moduleConfig.version) { - const moduleAddr = MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION[moduleConfig.version] - if (!moduleAddr) { - throw new Error(`Invalid version ${moduleConfig.version}`) - } - this.moduleAddress = moduleAddr - this.version = moduleConfig.version as ModuleVersion - } - } + constructor(moduleConfig: ECDSAOwnershipValidationModuleConfig) { + super(moduleConfig) - // TODO: Actually depends on the module address so maybe we can make it dynamic - getDummySignature(): string { - return '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000F993fc8Dc0EE7aece7abf0d6B6939f9d67875dBa000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' + if (moduleConfig.moduleAddress) { + this.moduleAddress = moduleConfig.moduleAddress + } else if (moduleConfig.version) { + const moduleAddr = MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION[moduleConfig.version] + if (!moduleAddr) { + throw new Error(`Invalid version ${moduleConfig.version}`) + } + this.moduleAddress = moduleAddr + this.version = moduleConfig.version as ModuleVersion } + } - async signUserOp(userOp: UserOperation): Promise { - Logger.log('userOp', userOp) - throw new Error("Method not implemented."); - } + // TODO: Actually depends on the module address so maybe we can make it dynamic + getDummySignature(): string { + return '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000CCCC01Bef3F9a28814b88aC36a819e96eec47E15000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' + } + + async signUserOp(userOp: UserOperation): Promise { + Logger.log('userOp', userOp) + throw new Error('Method not implemented.') + } - async signUserOps(userOps: UserOperation[]): Promise { - Logger.log('userOps', userOps) - throw new Error("Method not implemented."); + async signUserOps(multiChainUserOps: MultiChainUserOpDto[]): Promise { + const leaves: string[] = [] + + // Iterate over each userOp and process them + for (const multiChainOp of multiChainUserOps) { + const userOpHash = getUserOpHash( + multiChainOp.userOp, + this.entryPointAddress, + multiChainOp.chainId + ) + leaves.push(userOpHash) } -} \ No newline at end of file + // Create a new Merkle tree using the leaves array + const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true }) + + const multichainSignature = await this.signer.signMessage(arrayify(merkleTree.getHexRoot())) + + // Create an array to store updated userOps + const updatedUserOps: UserOperation[] = [] + + for (let i = 0; i < leaves.length; i++) { + const merkleProof = merkleTree.getHexProof(leaves[i]) + + // Create the moduleSignature + const moduleSignature = defaultAbiCoder.encode( + ['bytes32', 'bytes32[]', 'bytes'], + [merkleTree.getHexRoot(), merkleProof, multichainSignature] + ) + + // add validation module address to the signature + const signatureWithModuleAddress = defaultAbiCoder.encode( + ['bytes', 'address'], + [moduleSignature, this.getAddress()] + ) + + // Update userOp with the final signature + const updatedUserOp: UserOperation = { + ...multiChainUserOps[i].userOp, + signature: signatureWithModuleAddress + } + + updatedUserOps.push(updatedUserOp) + } + return updatedUserOps + } +} diff --git a/packages/modules/src/utils/Types.ts b/packages/modules/src/utils/Types.ts index 0c77a35e6..aaaad0677 100644 --- a/packages/modules/src/utils/Types.ts +++ b/packages/modules/src/utils/Types.ts @@ -1,4 +1,4 @@ -import { ChainId } from '@biconomy/core-types' +import { ChainId, UserOperation } from '@biconomy/core-types' import { Signer } from 'ethers' export type ModuleVersion = 'V1_0_0' // | 'V1_0_1' @@ -29,3 +29,8 @@ export interface MultiChainValidationModuleConfig extends BaseValidationModuleCo signer: Signer chainId: ChainId } + +export type MultiChainUserOpDto = { + chainId: ChainId + userOp: UserOperation +} From 6a7ff6ce00f803e7ce4335baaf072268f2ba5ebd Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Mon, 21 Aug 2023 01:23:43 +0530 Subject: [PATCH 03/10] type change --- packages/modules/src/MultichainValidationModule.ts | 2 +- packages/modules/src/index.ts | 2 +- packages/modules/src/utils/Types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 922ff7c49..3bdd3e0db 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -77,7 +77,7 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { // Update userOp with the final signature const updatedUserOp: UserOperation = { - ...multiChainUserOps[i].userOp, + ...(multiChainUserOps[i].userOp as UserOperation), signature: signatureWithModuleAddress } diff --git a/packages/modules/src/index.ts b/packages/modules/src/index.ts index 21e718eac..d4f43e1b4 100644 --- a/packages/modules/src/index.ts +++ b/packages/modules/src/index.ts @@ -1,6 +1,6 @@ export * from './interfaces/IValidationModule' export * from './BaseValidationModule' export * from './ECDSAOwnershipValidationModule' +export * from './MultichainValidationModule' // export * from './SessionKeyManagerModule' -// export * from './MultiChainValidationModule' // export * from './PasskeyValidationModule' diff --git a/packages/modules/src/utils/Types.ts b/packages/modules/src/utils/Types.ts index aaaad0677..f79243d04 100644 --- a/packages/modules/src/utils/Types.ts +++ b/packages/modules/src/utils/Types.ts @@ -32,5 +32,5 @@ export interface MultiChainValidationModuleConfig extends BaseValidationModuleCo export type MultiChainUserOpDto = { chainId: ChainId - userOp: UserOperation + userOp: Partial } From 0cc977ef5ce0faf34ae68026dd5ebc8b692c6ee0 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Mon, 21 Aug 2023 01:24:25 +0530 Subject: [PATCH 04/10] dev notes --- packages/account/src/BiconomySmartAccountV2.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/account/src/BiconomySmartAccountV2.ts b/packages/account/src/BiconomySmartAccountV2.ts index f2d4d8a1a..ac7a89ddb 100644 --- a/packages/account/src/BiconomySmartAccountV2.ts +++ b/packages/account/src/BiconomySmartAccountV2.ts @@ -60,6 +60,7 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount { factory?: SmartAccountFactory_v200 defaultValidationModule: BaseValidationModule + // Review: if it must be provided. default can be used as active activeValidationModule: BaseValidationModule constructor(readonly biconomySmartAccountConfig: BiconomySmartAccountV2Config) { From da0015c23801f91da317207e5e96f1db0687e850 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Mon, 21 Aug 2023 11:57:09 +0530 Subject: [PATCH 05/10] updated constants --- packages/account/src/utils/Constants.ts | 12 ++++++------ .../modules/src/ECDSAOwnershipValidationModule.ts | 2 +- packages/modules/src/MultichainValidationModule.ts | 2 +- packages/modules/src/utils/Constants.ts | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/account/src/utils/Constants.ts b/packages/account/src/utils/Constants.ts index 7b5421721..87671e77c 100644 --- a/packages/account/src/utils/Constants.ts +++ b/packages/account/src/utils/Constants.ts @@ -18,17 +18,17 @@ export const ENTRYPOINT_ADDRESSES: EntryPointAddresses = { } // will always be latest factory address -export const DEFAULT_BICONOMY_FACTORY_ADDRESS = '0xD8E65814d5F528fa573eF9bb5Aa22817DEE3E1bf' +export const DEFAULT_BICONOMY_FACTORY_ADDRESS = '0xd4450c80F6D0518a987144e44CEd55ec9CbC7805' export const BICONOMY_FACTORY_ADDRESSES: BiconomyFactories = { '0x000000f9ee1842bb72f6bbdd75e6d3d4e3e9594c': 'V1_0_0', - '0xD8E65814d5F528fa573eF9bb5Aa22817DEE3E1bf': 'V2_0_0' + '0xd4450c80F6D0518a987144e44CEd55ec9CbC7805': 'V2_0_0' } // will always be latest implementation address -export const DEFAULT_BICONOMY_IMPLEMENTATION_ADDRESS = '0x9777a082B23C09f81cB23C2635cCb93603D1AF42' +export const DEFAULT_BICONOMY_IMPLEMENTATION_ADDRESS = '0x3FA4BE2a60F2Ac16f96DE47630784dD9FC9De77e' export const BICONOMY_IMPLEMENTATION_ADDRESSES: BiconomyImplementations = { '0x00006b7e42e01957da540dc6a8f7c30c4d816af5': 'V1_0_0', - '0x9777a082B23C09f81cB23C2635cCb93603D1AF42': 'V2_0_0' + '0x3FA4BE2a60F2Ac16f96DE47630784dD9FC9De77e': 'V2_0_0' } export const ENTRYPOINT_ADDRESSES_BY_VERSION: EntryPointAddressesByVersion = { @@ -38,12 +38,12 @@ export const ENTRYPOINT_ADDRESSES_BY_VERSION: EntryPointAddressesByVersion = { export const BICONOMY_FACTORY_ADDRESSES_BY_VERSION: BiconomyFactoriesByVersion = { V1_0_0: '0x000000f9ee1842bb72f6bbdd75e6d3d4e3e9594c', - V2_0_0: '0x2642d30cebafeb1da6bc6e3c2cfce0e3199eff19' + V2_0_0: '0xd4450c80F6D0518a987144e44CEd55ec9CbC7805' } export const BICONOMY_IMPLEMENTATION_ADDRESSES_BY_VERSION: BiconomyImplementationsByVersion = { V1_0_0: '0x00006b7e42e01957da540dc6a8f7c30c4d816af5', - V2_0_0: '0xf1080f5f874ea8170e423738791e0e9a8aad87fd' + V2_0_0: '0x3FA4BE2a60F2Ac16f96DE47630784dD9FC9De77e' } export const EIP1559_UNSUPPORTED_NETWORKS: Array = [97, 56, 1442, 1101] diff --git a/packages/modules/src/ECDSAOwnershipValidationModule.ts b/packages/modules/src/ECDSAOwnershipValidationModule.ts index bebcb26a6..2955794b3 100644 --- a/packages/modules/src/ECDSAOwnershipValidationModule.ts +++ b/packages/modules/src/ECDSAOwnershipValidationModule.ts @@ -48,7 +48,7 @@ export class ECDSAOwnershipValidationModule extends BaseValidationModule { // TODO: Actually depends on the module address so maybe we can make it dynamic getDummySignature(): string { - return '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000F993fc8Dc0EE7aece7abf0d6B6939f9d67875dBa000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' + return '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000D19910aAd41540669EfBf720f5dE69fCAc2e4000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' } // Note: other modules may need additional attributes to build init data diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 3bdd3e0db..05bc79a39 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -31,7 +31,7 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { // TODO: Actually depends on the module address so maybe we can make it dynamic getDummySignature(): string { - return '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000CCCC01Bef3F9a28814b88aC36a819e96eec47E15000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' + return '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000002E817fe3749B81dA801fc08B247E081ec20eB080000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' } async signUserOp(userOp: UserOperation): Promise { diff --git a/packages/modules/src/utils/Constants.ts b/packages/modules/src/utils/Constants.ts index 44a802856..47e9563a6 100644 --- a/packages/modules/src/utils/Constants.ts +++ b/packages/modules/src/utils/Constants.ts @@ -13,23 +13,23 @@ export const ENTRYPOINT_ADDRESSES_BY_VERSION = { V0_0_6: '0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789' } -export const DEFAULT_ECDSA_OWNERSHIP_MODULE = '0xF993fc8Dc0EE7aece7abf0d6B6939f9d67875dBa' +export const DEFAULT_ECDSA_OWNERSHIP_MODULE = '0x000D19910aAd41540669EfBf720f5dE69fCAc2e4' export const ECDSA_OWNERSHIP_MODULE_ADDRESSES_BY_VERSION = { - V1_0_0: '0xd9cf3caaa21db25f16ad6db43eb9932ab77c8e76' - // 'V1_0_1' : '0xd9cf3caaa21db25f16ad6db43eb9932ab77c8e76' + V1_0_0: '0x000D19910aAd41540669EfBf720f5dE69fCAc2e4' + // 'V1_0_1' : '0x000D19910aAd41540669EfBf720f5dE69fCAc2e4' // and so on } export const SESSION_MANAGER_MODULE_ADDRESSES_BY_VERSION = { - V1_0_0: '0xd9cf3caaa21db25f16ad6db43eb9932ab77c8e76' - // 'V1_0_1' : '0xd9cf3caaa21db25f16ad6db43eb9932ab77c8e76' + V1_0_0: '0xc1918869FcC4584ec9f29BB82a55aC07B8048cf2' + // 'V1_0_1' : '0xc1918869FcC4584ec9f29BB82a55aC07B8048cf2' // and so on } export const MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION = { - V1_0_0: '0xCCCC01Bef3F9a28814b88aC36a819e96eec47E15' - // 'V1_0_1' : '0xd9cf3caaa21db25f16ad6db43eb9932ab77c8e76' + V1_0_0: '0x2E817fe3749B81dA801fc08B247E081ec20eB080' + // 'V1_0_1' : '0x2E817fe3749B81dA801fc08B247E081ec20eB080' // and so on } From a0bebd816baf7e753d504771756289ee48a11a4d Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Tue, 22 Aug 2023 00:11:05 +0530 Subject: [PATCH 06/10] dev notes + update multichain signing --- packages/account/src/BaseSmartAccount.ts | 1 + .../modules/src/MultichainValidationModule.ts | 28 +++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/account/src/BaseSmartAccount.ts b/packages/account/src/BaseSmartAccount.ts index 21951550b..bbc5111c8 100644 --- a/packages/account/src/BaseSmartAccount.ts +++ b/packages/account/src/BaseSmartAccount.ts @@ -394,6 +394,7 @@ export abstract class BaseSmartAccount implements IBaseSmartAccount { * return the account's address. * this value is valid even before deploying the contract. */ + // Review: Probably should accept index as well as we rely on factory! async getAccountAddress(): Promise { if (this.accountAddress == null) { // means it needs deployment diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 05bc79a39..13325a52b 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -4,7 +4,7 @@ import { ethers } from 'ethers' import MerkleTree from 'merkletreejs' import { ECDSAOwnershipValidationModule } from './ECDSAOwnershipValidationModule' import { MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION } from './utils/Constants' -import { keccak256, arrayify, defaultAbiCoder } from 'ethers/lib/utils' +import { keccak256, arrayify, defaultAbiCoder, hexConcat, hexZeroPad } from 'ethers/lib/utils' import { ECDSAOwnershipValidationModuleConfig, ModuleVersion, @@ -42,14 +42,24 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { async signUserOps(multiChainUserOps: MultiChainUserOpDto[]): Promise { const leaves: string[] = [] + // TODO + const validUntil = 0 // unlimited + const validAfter = 0 + + // TODO // error handling + // Iterate over each userOp and process them for (const multiChainOp of multiChainUserOps) { - const userOpHash = getUserOpHash( - multiChainOp.userOp, - this.entryPointAddress, - multiChainOp.chainId - ) - leaves.push(userOpHash) + const leaf = hexConcat([ + hexZeroPad(ethers.utils.hexlify(validUntil), 6), + hexZeroPad(ethers.utils.hexlify(validAfter), 6), + hexZeroPad( + getUserOpHash(multiChainOp.userOp, this.entryPointAddress, multiChainOp.chainId), + 32 + ) + ]) + + leaves.push(leaf) } // Create a new Merkle tree using the leaves array @@ -65,8 +75,8 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { // Create the moduleSignature const moduleSignature = defaultAbiCoder.encode( - ['bytes32', 'bytes32[]', 'bytes'], - [merkleTree.getHexRoot(), merkleProof, multichainSignature] + ['uint48', 'uint48', 'bytes32', 'bytes32[]', 'bytes'], + [validUntil, validAfter, merkleTree.getHexRoot(), merkleProof, multichainSignature] ) // add validation module address to the signature From f67e339bcff8d9712df8406b4d123affcd4d4aa4 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Tue, 22 Aug 2023 02:47:54 +0530 Subject: [PATCH 07/10] fix: signing issue --- packages/modules/src/MultichainValidationModule.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 13325a52b..dfcd0f20b 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -59,7 +59,7 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { ) ]) - leaves.push(leaf) + leaves.push(keccak256(leaf)) } // Create a new Merkle tree using the leaves array @@ -70,9 +70,13 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { // Create an array to store updated userOps const updatedUserOps: UserOperation[] = [] + Logger.log('merkle root ', merkleTree.getHexRoot()) + for (let i = 0; i < leaves.length; i++) { const merkleProof = merkleTree.getHexProof(leaves[i]) + Logger.log('merkle proof ', merkleProof) + // Create the moduleSignature const moduleSignature = defaultAbiCoder.encode( ['uint48', 'uint48', 'bytes32', 'bytes32[]', 'bytes'], From 24dd3997c00575f318a12246e2824e0fafc31760 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Tue, 22 Aug 2023 14:12:23 +0530 Subject: [PATCH 08/10] make dummy sig dynamic based on module address --- packages/modules/src/ECDSAOwnershipValidationModule.ts | 5 +++-- packages/modules/src/MultichainValidationModule.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/modules/src/ECDSAOwnershipValidationModule.ts b/packages/modules/src/ECDSAOwnershipValidationModule.ts index 2955794b3..856007bb8 100644 --- a/packages/modules/src/ECDSAOwnershipValidationModule.ts +++ b/packages/modules/src/ECDSAOwnershipValidationModule.ts @@ -46,9 +46,10 @@ export class ECDSAOwnershipValidationModule extends BaseValidationModule { return await Promise.resolve(this.signer) } - // TODO: Actually depends on the module address so maybe we can make it dynamic getDummySignature(): string { - return '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000D19910aAd41540669EfBf720f5dE69fCAc2e4000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' + const moduleAddress = 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 diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index dfcd0f20b..9b893f6a1 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -29,9 +29,10 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { } } - // TODO: Actually depends on the module address so maybe we can make it dynamic getDummySignature(): string { - return '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000002E817fe3749B81dA801fc08B247E081ec20eB080000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000' + const moduleAddress = ethers.utils.getAddress(this.getAddress()) + const dynamicPart = moduleAddress.substring(2).padEnd(40, '0') + return `0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000${dynamicPart}000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000` } async signUserOp(userOp: UserOperation): Promise { From 1b8581c8a4535f7f647bc066625c833a7eea702d Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Tue, 22 Aug 2023 20:34:21 +0530 Subject: [PATCH 09/10] refactor --- .../modules/src/MultichainValidationModule.ts | 96 ++++++++++--------- packages/modules/src/utils/Types.ts | 2 + 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 9b893f6a1..85825831c 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -41,63 +41,67 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { } async signUserOps(multiChainUserOps: MultiChainUserOpDto[]): Promise { - const leaves: string[] = [] - - // TODO - const validUntil = 0 // unlimited - const validAfter = 0 - - // TODO // error handling - - // Iterate over each userOp and process them - for (const multiChainOp of multiChainUserOps) { - const leaf = hexConcat([ - hexZeroPad(ethers.utils.hexlify(validUntil), 6), - hexZeroPad(ethers.utils.hexlify(validAfter), 6), - hexZeroPad( - getUserOpHash(multiChainOp.userOp, this.entryPointAddress, multiChainOp.chainId), - 32 - ) - ]) + try { + const leaves: string[] = [] + + // Iterate over each userOp and process them + for (const multiChainOp of multiChainUserOps) { + const validUntil = multiChainOp.validUntil ?? 0 + const validAfter = multiChainOp.validAfter ?? 0 + const leaf = hexConcat([ + hexZeroPad(ethers.utils.hexlify(validUntil), 6), + hexZeroPad(ethers.utils.hexlify(validAfter), 6), + hexZeroPad( + getUserOpHash(multiChainOp.userOp, this.entryPointAddress, multiChainOp.chainId), + 32 + ) + ]) + + leaves.push(keccak256(leaf)) + } - leaves.push(keccak256(leaf)) - } + // Create a new Merkle tree using the leaves array + const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true }) - // Create a new Merkle tree using the leaves array - const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true }) + const multichainSignature = await this.signer.signMessage(arrayify(merkleTree.getHexRoot())) - const multichainSignature = await this.signer.signMessage(arrayify(merkleTree.getHexRoot())) + // Create an array to store updated userOps + const updatedUserOps: UserOperation[] = [] - // Create an array to store updated userOps - const updatedUserOps: UserOperation[] = [] + Logger.log('merkle root ', merkleTree.getHexRoot()) - Logger.log('merkle root ', merkleTree.getHexRoot()) + for (let i = 0; i < leaves.length; i++) { + const merkleProof = merkleTree.getHexProof(leaves[i]) - for (let i = 0; i < leaves.length; i++) { - const merkleProof = merkleTree.getHexProof(leaves[i]) + Logger.log('merkle proof ', merkleProof) - Logger.log('merkle proof ', merkleProof) + const validUntil = multiChainUserOps[i].validUntil ?? 0 + const validAfter = multiChainUserOps[i].validAfter ?? 0 - // Create the moduleSignature - const moduleSignature = defaultAbiCoder.encode( - ['uint48', 'uint48', 'bytes32', 'bytes32[]', 'bytes'], - [validUntil, validAfter, merkleTree.getHexRoot(), merkleProof, multichainSignature] - ) + // Create the moduleSignature + const moduleSignature = defaultAbiCoder.encode( + ['uint48', 'uint48', 'bytes32', 'bytes32[]', 'bytes'], + [validUntil, validAfter, merkleTree.getHexRoot(), merkleProof, multichainSignature] + ) - // add validation module address to the signature - const signatureWithModuleAddress = defaultAbiCoder.encode( - ['bytes', 'address'], - [moduleSignature, this.getAddress()] - ) + // add validation module address to the signature + const signatureWithModuleAddress = defaultAbiCoder.encode( + ['bytes', 'address'], + [moduleSignature, this.getAddress()] + ) - // Update userOp with the final signature - const updatedUserOp: UserOperation = { - ...(multiChainUserOps[i].userOp as UserOperation), - signature: signatureWithModuleAddress - } + // Update userOp with the final signature + const updatedUserOp: UserOperation = { + ...(multiChainUserOps[i].userOp as UserOperation), + signature: signatureWithModuleAddress + } - updatedUserOps.push(updatedUserOp) + updatedUserOps.push(updatedUserOp) + } + return updatedUserOps + } catch (error) { + Logger.error('Error in signing multi chain userops', error) + throw new Error(JSON.stringify(error)) } - return updatedUserOps } } diff --git a/packages/modules/src/utils/Types.ts b/packages/modules/src/utils/Types.ts index f79243d04..bcfabc39f 100644 --- a/packages/modules/src/utils/Types.ts +++ b/packages/modules/src/utils/Types.ts @@ -31,6 +31,8 @@ export interface MultiChainValidationModuleConfig extends BaseValidationModuleCo } export type MultiChainUserOpDto = { + validUntil?: number + validAfter?: number chainId: ChainId userOp: Partial } From bf4479e31b2f0e5d3d172d4a728fbf63db6a8865 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Tue, 22 Aug 2023 21:41:56 +0530 Subject: [PATCH 10/10] make multichain validation module not inherit from ecdsa --- .../modules/src/MultichainValidationModule.ts | 57 ++++++++++++++----- packages/modules/src/utils/Types.ts | 1 - 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 85825831c..a5b347746 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -1,22 +1,25 @@ -import { UserOperation, ChainId } from '@biconomy/core-types' +import { UserOperation } from '@biconomy/core-types' import { Logger, getUserOpHash } from '@biconomy/common' -import { ethers } from 'ethers' +import { Signer, ethers } from 'ethers' import MerkleTree from 'merkletreejs' -import { ECDSAOwnershipValidationModule } from './ECDSAOwnershipValidationModule' import { MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION } from './utils/Constants' -import { keccak256, arrayify, defaultAbiCoder, hexConcat, hexZeroPad } from 'ethers/lib/utils' import { - ECDSAOwnershipValidationModuleConfig, - ModuleVersion, - MultiChainUserOpDto -} from './utils/Types' -export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { - // Review: this.chainId should not be in spec of this class - // May not inherit from ECDSAOwnershipValidationModule - - constructor(moduleConfig: ECDSAOwnershipValidationModuleConfig) { + keccak256, + arrayify, + defaultAbiCoder, + hexConcat, + hexZeroPad, + Bytes +} from 'ethers/lib/utils' +import { ModuleVersion, MultiChainUserOpDto, MultiChainValidationModuleConfig } from './utils/Types' +import { BaseValidationModule } from './BaseValidationModule' +export class MultiChainValidationModule extends BaseValidationModule { + signer: Signer + moduleAddress!: string + version: ModuleVersion = 'V1_0_0' + + constructor(moduleConfig: MultiChainValidationModuleConfig) { super(moduleConfig) - if (moduleConfig.moduleAddress) { this.moduleAddress = moduleConfig.moduleAddress } else if (moduleConfig.version) { @@ -27,6 +30,15 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { this.moduleAddress = moduleAddr this.version = moduleConfig.version as ModuleVersion } + this.signer = moduleConfig.signer + } + + getAddress(): string { + return this.moduleAddress + } + + async getSigner(): Promise { + return await Promise.resolve(this.signer) } getDummySignature(): string { @@ -35,11 +47,28 @@ export class MultiChainValidationModule extends ECDSAOwnershipValidationModule { return `0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000${dynamicPart}000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000` } + // Note: other modules may need additional attributes to build init data + async getInitData(): Promise { + const ecdsaOwnerAddress = await this.signer.getAddress() + const moduleRegistryAbi = 'function initForSmartAccount(address owner)' + const ecdsaModuleRegistryInterface = new ethers.utils.Interface([moduleRegistryAbi]) + const ecdsaOwnershipInitData = ecdsaModuleRegistryInterface.encodeFunctionData( + 'initForSmartAccount', + [ecdsaOwnerAddress] + ) + return ecdsaOwnershipInitData + } + async signUserOp(userOp: UserOperation): Promise { Logger.log('userOp', userOp) throw new Error('Method not implemented.') } + async signMessage(message: Bytes | string): Promise { + Logger.log('message', message) + throw new Error('Method not implemented.') + } + async signUserOps(multiChainUserOps: MultiChainUserOpDto[]): Promise { try { const leaves: string[] = [] diff --git a/packages/modules/src/utils/Types.ts b/packages/modules/src/utils/Types.ts index bcfabc39f..432a2cbf5 100644 --- a/packages/modules/src/utils/Types.ts +++ b/packages/modules/src/utils/Types.ts @@ -27,7 +27,6 @@ export interface MultiChainValidationModuleConfig extends BaseValidationModuleCo moduleAddress?: string version?: ModuleVersion signer: Signer - chainId: ChainId } export type MultiChainUserOpDto = {