diff --git a/src/modules/utils/SmartSessionHelpers.ts b/src/modules/utils/SmartSessionHelpers.ts index ccff83ac..cae22ff1 100644 --- a/src/modules/utils/SmartSessionHelpers.ts +++ b/src/modules/utils/SmartSessionHelpers.ts @@ -4,7 +4,7 @@ import addresses from "../../__contracts/addresses.js" import { type AnyReferenceValue, parseReferenceValue } from "./Helper" import { type EnableSessionData, type Session, SmartSessionMode, type SmartSessionModeType } from "./Types" import { LibZip } from 'solady' -import { abi, encodeEnableSessionSignatureAbi } from "./abi" +import { smartSessionAbi, encodeEnableSessionSignatureAbi, universalActionPolicyAbi } from "./abi" export type Rule = { /** @@ -169,7 +169,7 @@ export const formatPermissionEnableSig = ({ }) => { return (await client.readContract({ address: addresses.SmartSession, // Review address import - abi, + abi: smartSessionAbi, functionName: 'getPermissionId', args: [session], })) as string @@ -246,3 +246,28 @@ export const encodeSmartSessionSignature = ({ throw new Error(`Unknown mode ${mode}`) } } + + // Note: this helper gives you policy data for a universal action policy + // PolicyData is a struct that contains the policy address and the init data + // Action config requires param rules. we should have a way to build rules next + export const getUniversalActionPolicy = ( + actionConfig: ActionConfig, + ): Policy => { + if (actionConfig.paramRules.rules.length > MAX_RULES) { + throw new Error(`Max number of rules is ${MAX_RULES}`) + } + + return { + address: addresses.UniActionPolicy, + initData: encodeAbiParameters(universalActionPolicyAbi, [ + { + valueLimitPerUse: actionConfig.valueLimitPerUse, + paramRules: { + length: actionConfig.paramRules.length, + rules: actionConfig.paramRules.rules, + }, + }, + ]), + deInitData: '0x', + } + } diff --git a/src/modules/utils/Types.ts b/src/modules/utils/Types.ts index 81d8a35e..183e8eb2 100644 --- a/src/modules/utils/Types.ts +++ b/src/modules/utils/Types.ts @@ -1,4 +1,4 @@ -import type { Address, Chain, Hex } from "viem" +import type { AbiFunction, Address, Chain, Hex } from "viem" import type { CallType, SimulationType, @@ -6,6 +6,7 @@ import type { SupportedSigner, UserOperationStruct } from "../../account" +import { Rule } from "./SmartSessionHelpers" export type ModuleVersion = "1.0.0-beta" // | 'V1_0_1' export interface BaseValidationModuleConfig { @@ -245,12 +246,12 @@ export const moduleTypeIds: ModuleTypeIds = { // Note: can import from ModuleKit export type Session = { - sessionValidator: Address - sessionValidatorInitData: Hex - salt: Hex - userOpPolicies: PolicyData[] - erc7739Policies: ERC7739Data - actions: ActionData[] + sessionValidator: Address // deployed SimpleSigner + sessionValidatorInitData: Hex // abi.encodePacked sessionKeyEOA + salt: Hex // random salt + userOpPolicies: PolicyData[] // empty policies by default + erc7739Policies: ERC7739Data // empty policies by default + actions: ActionData[] // make uni action policy } export type SessionEIP712 = { @@ -314,3 +315,59 @@ export type EnableSessionData = { // accountType: AccountType // Temp } +// Types for creating session with abi SVM + +// Note: keep Dan stuff for later +export type DanModuleInfo = { + /** Ephemeral sk */ + jwt: string + /** eoa address */ + eoaAddress: Hex + /** threshold */ + threshold: number + /** parties number */ + partiesNumber: number + /** userOp to be signed */ + userOperation?: Partial + /** chainId */ + chainId: number + /** selected mpc key id */ + mpcKeyId: string +} + +export interface CreateSessionDataParams { + // TimeLimitPolicy? + // /** window end for the session key */ + // validUntil: number + // /** window start for the session key */ + // validAfter: number + // /** Address of the session validation module */ + + // Note: below is only taking information specific to universal policy. + // Other two fields of seesions object (7739 policy and userOp policy will be empty by default) + // We should have means to get information on how to build those too + + sessionPublicKey: Hex + + sessionValidatorAddress: Address // constant for a type of validator + + sessionKeyData: Hex + + /** The address of the contract to be included in the policy */ + contractAddress: Hex; + + /** The specific function selector from the contract to be included in the policy */ + functionSelector: string | AbiFunction; + + /** The rules to be included in the policy */ + rules: Rule[]; + + /** The maximum value that can be transferred in a single transaction */ + valueLimit: bigint; + + /** we generate uuid based sessionId. but if you prefer to track it on your side and attach custom session identifier this can be passed */ + preferredSessionId?: string + /** Dan module info */ + danModuleInfo?: DanModuleInfo +} + diff --git a/src/modules/utils/abi.ts b/src/modules/utils/abi.ts index 0ccc15b0..80f82e06 100644 --- a/src/modules/utils/abi.ts +++ b/src/modules/utils/abi.ts @@ -306,7 +306,7 @@ export const installSmartSessionsAbi = [ { type: 'bytes' }, ] - export const abi = [ + export const smartSessionAbi = [ { inputs: [{ internalType: 'uint256', name: 'index', type: 'uint256' }], name: 'AssociatedArray_OutOfBounds', @@ -1133,4 +1133,64 @@ export const installSmartSessionsAbi = [ type: 'function', }, ] + + // Review redundancy + export const universalActionPolicyAbi = [ + { + components: [ + { + name: 'valueLimitPerUse', + type: 'uint256', + }, + { + components: [ + { + name: 'length', + type: 'uint256', + }, + { + components: [ + { + name: 'condition', + type: 'uint8', + }, + { + name: 'offset', + type: 'uint64', + }, + { + name: 'isLimited', + type: 'bool', + }, + { + name: 'ref', + type: 'bytes32', + }, + { + components: [ + { + name: 'limit', + type: 'uint256', + }, + { + name: 'used', + type: 'uint256', + }, + ], + name: 'usage', + type: 'tuple', + }, + ], + name: 'rules', + type: 'tuple[16]', + }, + ], + name: 'paramRules', + type: 'tuple', + }, + ], + name: 'ActionConfig', + type: 'tuple', + }, + ] \ No newline at end of file diff --git a/src/modules/validators/SmartSessionModule.ts b/src/modules/validators/SmartSessionModule.ts index ca7bb995..f68f887e 100644 --- a/src/modules/validators/SmartSessionModule.ts +++ b/src/modules/validators/SmartSessionModule.ts @@ -1,12 +1,16 @@ -import { type Hex } from "viem" +import { encodeFunctionData, parseAbi, type Hex } from "viem" import addresses from "../../__contracts/addresses.js" import type { SmartAccountSigner } from "../../account/index.js" import { BaseValidationModule } from "../base/BaseValidationModule.js" -import { SmartSessionMode, type Module } from "../utils/Types.js" +import { CreateSessionDataParams, SmartSessionMode, type Module } from "../utils/Types.js" import { encodeSmartSessionSignature } from "../utils/SmartSessionHelpers.js" +import { type Session } from "../utils/Types.js" const DUMMY_ECDSA_SIG = "0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c"; +const UNIVERSAL_POLICY_ADDRESS = addresses.UniversalPolicy + +// Note: flows: use mode and enable mode both should be supported. export class SmartSessionModule extends BaseValidationModule { // Notice: For smart sessions signer could be anything. Which is an implementation of ISessionValidator interface // SmartAccountSigner works if session validator is K1 like single signer. @@ -31,7 +35,8 @@ export class SmartSessionModule extends BaseValidationModule { // Note: this second argument is like ModuleInfo object which is needed for certain modules like SKM for v2 account sdk override async signUserOpHash(userOpHash: string, permissionId?: Hex): Promise{ const signature = await this.signer.signMessage({ raw: userOpHash as Hex }) - + + // Not this function is only implemented for USE mode. return encodeSmartSessionSignature({ mode: SmartSessionMode.USE, permissionId: permissionId ? permissionId : '0x', @@ -47,22 +52,9 @@ export class SmartSessionModule extends BaseValidationModule { }) as Hex } - // Note: - // Needs more helpers to create a session struct. given constant validator, policies need to be built. - // Could be in helpers - // Todo:L - // Temp comment below + // To remind again how a session looks like.. - /*Session memory session = Session({ - sessionValidator: ISessionValidator(address(yesSigner)), - salt: salt, - sessionValidatorInitData: "mockInitData", - userOpPolicies: _getEmptyPolicyDatas(address(yesPolicy)), - erc7739Policies: _getEmptyERC7739Data("mockContent", _getEmptyPolicyDatas(address(yesPolicy))), // optional and default empty - actions: _getEmptyActionDatas(_target, MockTarget.setValue.selector, address(yesPolicy)) // mocks. but usually one universal policy is enough - });*/ - - /* + /* [ { sessionValidator: OWNABLE_VALIDATOR_ADDRESS as Address, @@ -90,7 +82,60 @@ export class SmartSessionModule extends BaseValidationModule { }, }, ] - */ + */ + + // Notice: + // This is a USE mode so we need calldata to post on smart session module to make sessions enabled first. + // For enable mode we will just need to preapre digest to sign and then make a userOperation that has actual session tx. + + // Note: can later create methods like + + createSessionData = async ( + sessionRequestedInfo: CreateSessionDataParams[] + ): Promise => { + + // 1. iteraste over sessionRequestedInfo and make ActionConfig using the passed rules and value limit (calculate rules length and fit in object) + + // 2. call getUniversalActionPolicy that will give you policy object + // 3. Build actionData from this policy object and contractAddress and func selector + // type is + /*export type ActionData = { + actionTargetSelector: Hex + actionTarget: Address + actionPolicies: PolicyData[] + }*/ + // Build the session objects then apply below. + + // Review + const smartSessionBI = parseAbi([ + "function enableSessions((address,bytes,bytes32,(address,bytes)[],(string[],(address,bytes)[]),(bytes4,address,(address,bytes)[])[])[])" + ]) + + const sessions: Session[] = []; + + // const enableSessionsData = encodeFunctionData({ + // abi: smartSessionBI, + // functionName: "enableSessions", + // args: [sessions] + // }) + } + + + + // Note: + // Needs more helpers to create a session struct. given constant validator, policies need to be built. + // Could be in helpers + // Todo:L + // Temp comment below + + /*Session memory session = Session({ + sessionValidator: ISessionValidator(address(yesSigner)), + salt: salt, + sessionValidatorInitData: "mockInitData", + userOpPolicies: _getEmptyPolicyDatas(address(yesPolicy)), + erc7739Policies: _getEmptyERC7739Data("mockContent", _getEmptyPolicyDatas(address(yesPolicy))), // optional and default empty + actions: _getEmptyActionDatas(_target, MockTarget.setValue.selector, address(yesPolicy)) // mocks. but usually one universal policy is enough + });*/ }