Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sma 46 multichain validation module #257

Merged
1 change: 1 addition & 0 deletions packages/account/src/BaseSmartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
if (this.accountAddress == null) {
// means it needs deployment
Expand Down
1 change: 1 addition & 0 deletions packages/account/src/BiconomySmartAccountV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
12 changes: 6 additions & 6 deletions packages/account/src/utils/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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<ChainId> = [97, 56, 1442, 1101]
5 changes: 3 additions & 2 deletions packages/modules/src/ECDSAOwnershipValidationModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 '0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000F993fc8Dc0EE7aece7abf0d6B6939f9d67875dBa000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000'
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
Expand Down
103 changes: 103 additions & 0 deletions packages/modules/src/MultichainValidationModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
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 { keccak256, arrayify, defaultAbiCoder, hexConcat, hexZeroPad } from 'ethers/lib/utils'
import {
ECDSAOwnershipValidationModuleConfig,
ModuleVersion,
MultiChainUserOpDto
} from './utils/Types'
export class MultiChainValidationModule extends ECDSAOwnershipValidationModule {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AmanRaj1608 @tomarsachin2271 since it extends the ECDSAModule chainId becomes mandatory but in real sense this module itself doesn't need to maintain chainId as it will receive it as part of dto (array of objects - userop + chainId) in singUserOps method.
what should I do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove the extend part as the MultiChainValidationModule is different module and not really depends on ECDSAOwnershipValidationModule

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok let me do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// Review: this.chainId should not be in spec of this class
AmanRaj1608 marked this conversation as resolved.
Show resolved Hide resolved
// 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
}
}

getDummySignature(): string {
const moduleAddress = ethers.utils.getAddress(this.getAddress())
const dynamicPart = moduleAddress.substring(2).padEnd(40, '0')
return `0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000${dynamicPart}000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000`
}

async signUserOp(userOp: UserOperation): Promise<string> {
Logger.log('userOp', userOp)
throw new Error('Method not implemented.')
}

async signUserOps(multiChainUserOps: MultiChainUserOpDto[]): Promise<UserOperation[]> {
const leaves: string[] = []

// TODO
const validUntil = 0 // unlimited
const validAfter = 0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be taken by the user as param

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// 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
)
])

leaves.push(keccak256(leaf))
}

// 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[] = []

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'],
[validUntil, validAfter, 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 as UserOperation),
signature: signatureWithModuleAddress
}

updatedUserOps.push(updatedUserOp)
}
return updatedUserOps
}
}
2 changes: 1 addition & 1 deletion packages/modules/src/index.ts
Original file line number Diff line number Diff line change
@@ -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'
16 changes: 11 additions & 5 deletions packages/modules/src/utils/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +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: '0x2E817fe3749B81dA801fc08B247E081ec20eB080'
// 'V1_0_1' : '0x2E817fe3749B81dA801fc08B247E081ec20eB080'
// and so on
}

Expand Down
7 changes: 6 additions & 1 deletion packages/modules/src/utils/Types.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -29,3 +29,8 @@ export interface MultiChainValidationModuleConfig extends BaseValidationModuleCo
signer: Signer
chainId: ChainId
}

export type MultiChainUserOpDto = {
chainId: ChainId
userOp: Partial<UserOperation>
}