From 0c590bbf4f341154bb74c78738ec9698ec791d45 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Thu, 19 Dec 2024 01:58:57 -0500 Subject: [PATCH] chore: temp --- .../src/ma-v2/account/semiModularAccountV2.ts | 75 ++++--- .../install-validation/installValidation.ts | 157 ++++++++++++++ .../src/ma-v2/client/client.test.ts | 22 +- .../src/ma-v2/client/client.ts | 141 ++----------- .../src/ma-v2/decorators/modularAccountV2.ts | 192 ++++++++++++++++++ 5 files changed, 414 insertions(+), 173 deletions(-) create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/decorators/modularAccountV2.ts diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index 2bdedc49b5..7745981c52 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -24,9 +24,7 @@ import { } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; import { getDefaultMAV2FactoryAddress } from "../utils.js"; -import { standardExecutor } from "../../msca/account/standardExecutor.js"; import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; -import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; import { modularAccountAbi } from "../abis/modularAccountAbi.js"; import { serializeModuleEntity } from "../actions/common/utils.js"; @@ -70,11 +68,11 @@ export async function createSMAV2Account< TSigner extends SmartAccountSigner = SmartAccountSigner >( config: CreateSMAV2AccountParams -): Promise & CalldataEncoder>; +): Promise>; export async function createSMAV2Account( config: CreateSMAV2AccountParams -): Promise { +): Promise { const { transport, chain, @@ -116,14 +114,48 @@ export async function createSMAV2Account( ]); }; + const encodeExecute: (tx: AccountOp) => Promise = async ({ + target, + data, + value, + }) => { + let callData = encodeFunctionData({ + abi: modularAccountAbi, + functionName: "execute", + args: [target, value ?? 0n, data], + }); + + callData = (await baseAccount.isAccountDeployed()) + ? await encodeCallData(callData) + : callData; + + return callData; + }; + + const encodeBatchExecute: (txs: AccountOp[]) => Promise = async (txs) => + await encodeCallData( + encodeFunctionData({ + abi: modularAccountAbi, + functionName: "executeBatch", + args: [ + txs.map((tx) => ({ + target: tx.target, + data: tx.data, + value: tx.value ?? 0n, + })), + ], + }) + ); + const baseAccount = await toSmartContractAccount({ transport, chain, entryPoint, accountAddress, source: `SMAV2Account`, + encodeExecute, + encodeBatchExecute, getAccountInitCode, - ...standardExecutor, ...singleSignerMessageSigner(signer), }); @@ -188,39 +220,6 @@ export async function createSMAV2Account( return numHooks ? concatHex([executeUserOpSelector, callData]) : callData; }; - const encodeExecute: (tx: AccountOp) => Promise = async ({ - target, - data, - value, - }) => { - let callData = encodeFunctionData({ - abi: modularAccountAbi, - functionName: "execute", - args: [target, value ?? 0n, data], - }); - - callData = (await baseAccount.isAccountDeployed()) - ? await encodeCallData(callData) - : callData; - - return callData; - }; - - const encodeBatchExecute: (txs: AccountOp[]) => Promise = async (txs) => - encodeCallData( - encodeFunctionData({ - abi: modularAccountAbi, - functionName: "executeBatch", - args: [ - txs.map((tx) => ({ - target: tx.target, - data: tx.data, - value: tx.value ?? 0n, - })), - ], - }) - ); - return { ...baseAccount, getAccountNonce, diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts new file mode 100644 index 0000000000..d6913db1cf --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -0,0 +1,157 @@ +import { + AccountNotFoundError, + IncompatibleClientError, + isSmartAccountClient, + EntityIdOverrideError, + type GetAccountParameter, + type GetEntryPointFromAccount, + type SendUserOperationResult, + type UserOperationOverridesParameter, +} from "@aa-sdk/core"; +import { + encodeFunctionData, + concatHex, + type Address, + type Hex, + type Chain, + type Transport, +} from "viem"; +import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; +import type { HookConfig, ValidationConfig } from "../common/types.js"; +import { + serializeValidationConfig, + serializeHookConfig, + serializeModuleEntity, +} from "../common/utils.js"; +import type { SMAV2Account } from "../../account/semiModularAccountV2.js"; +import type { SMASmartAccountClient } from "../../client/client.js"; + +export type InstallValidationParams< + TSMAV2Account extends SMAV2Account | undefined = SMAV2Account | undefined +> = { + validationConfig: ValidationConfig; + selectors: Hex[]; + installData: Hex; + hooks: { + hookConfig: HookConfig; + initData: Hex; + }[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type UninstallValidationParams< + TSMAV2Account extends SMAV2Account | undefined = SMAV2Account | undefined +> = { + moduleAddress: Address; + entityId: number; + uninstallData: Hex; + hookUninstallDatas: Hex[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type InstallValidationActions< + TSMAV2Account extends SMAV2Account | undefined = SMAV2Account | undefined +> = { + installValidation: ( + args: InstallValidationParams + ) => Promise; + uninstallValidation: ( + args: UninstallValidationParams + ) => Promise; +}; + +export const installValidationActions: < + TTransport extends Transport = Transport, + TChain extends Chain = Chain, + TSMAV2Account extends SMAV2Account = SMAV2Account +>( + client: SMASmartAccountClient +) => InstallValidationActions = (client) => ({ + installValidation: async ({ + validationConfig, + selectors, + installData, + hooks, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "installValidation", + client + ); + } + + if (validationConfig.entityId === 0) { + throw new EntityIdOverrideError(); + } + + const callData = await account.encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, + + uninstallValidation: async ({ + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "uninstallValidation", + client + ); + } + + const callData = await account.encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, +}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 7ba4ade36a..f3c19149ab 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -1,21 +1,15 @@ import { custom, parseEther, publicActions } from "viem"; - -import { - LocalAccountSigner, - type SmartAccountSigner, - type SmartAccountClient, -} from "@aa-sdk/core"; - +import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; import { createSMAV2AccountClient, - type InstallValidationActions, + type SMASmartAccountClient, } from "./client.js"; - import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -68,7 +62,9 @@ describe("MA v2 Tests", async () => { }); it("adds a session key with no permissions", async () => { - let provider = await givenConnectedProvider({ signer }); + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); await setBalance(client, { address: provider.getAddress(), @@ -124,7 +120,9 @@ describe("MA v2 Tests", async () => { }); it("uninstalls a session key", async () => { - let provider = await givenConnectedProvider({ signer }); + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); await setBalance(client, { address: provider.getAddress(), @@ -193,7 +191,7 @@ describe("MA v2 Tests", async () => { }: { signer: SmartAccountSigner; accountAddress?: `0x${string}`; - }): Promise => + }): Promise => createSMAV2AccountClient({ chain: instance.chain, signer, diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index 3d023d36a1..f5f90be7b4 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -1,37 +1,24 @@ import { createSmartAccountClient, - EntityIdOverrideError, + type SmartContractAccount, type SmartAccountClient, type SmartAccountSigner, type SmartAccountClientConfig, - type UserOperationOverridesParameter, - type GetEntryPointFromAccount, - type SendUserOperationResult, + type SmartAccountClientActions, } from "@aa-sdk/core"; -import { - type Chain, - type CustomTransport, - type Transport, - type Hex, - type Address, -} from "viem"; -import { concatHex, encodeFunctionData } from "viem"; + +import { type Transport, type Chain, type CustomTransport } from "viem"; import { createSMAV2Account, - type CalldataEncoder, type CreateSMAV2AccountParams, type SMAV2Account, } from "../account/semiModularAccountV2.js"; -import type { HookConfig, ValidationConfig } from "../actions/common/types.js"; import { - serializeValidationConfig, - serializeHookConfig, - serializeModuleEntity, -} from "../actions/common/utils.js"; - -import { semiModularAccountBytecodeAbi } from "../abis/semiModularAccountBytecodeAbi.js"; + type MAV2AccountClientActions, + mav2AccountActions, +} from "../decorators/modularAccountV2.js"; export type CreateSMAV2AccountClientParams< TTransport extends Transport = Transport, @@ -43,41 +30,19 @@ export type CreateSMAV2AccountClientParams< "transport" | "account" | "chain" >; -export type InstallValidationActions = { - installValidation: ( - args: InstallValidationParams - ) => Promise; - uninstallValidation: ( - args: UninstallValidationParams - ) => Promise; -}; - -export type InstallValidationParams = { - validationConfig: ValidationConfig; - selectors: Hex[]; - installData: Hex; - hooks: { - hookConfig: HookConfig; - initData: Hex; - }[]; -} & UserOperationOverridesParameter>; - -export type UninstallValidationParams = { - moduleAddress: Address; - entityId: number; - uninstallData: Hex; - hookUninstallDatas: Hex[]; -} & UserOperationOverridesParameter>; - export function createSMAV2AccountClient< TChain extends Chain = Chain, - TSigner extends SmartAccountSigner = SmartAccountSigner, - TCalldataEncoder extends CalldataEncoder = CalldataEncoder + TSigner extends SmartAccountSigner = SmartAccountSigner >( args: CreateSMAV2AccountClientParams ): Promise< - SmartAccountClient & - InstallValidationActions + SmartAccountClient< + CustomTransport, + TChain, + SMAV2Account, + SmartAccountClientActions & + MAV2AccountClientActions> + > >; /** @@ -111,83 +76,13 @@ export function createSMAV2AccountClient< */ export async function createSMAV2AccountClient({ ...config -}: CreateSMAV2AccountClientParams): Promise< - SmartAccountClient & InstallValidationActions -> { +}: CreateSMAV2AccountClientParams): Promise { const maV2Account = await createSMAV2Account({ ...config, }); - const client = createSmartAccountClient({ + return createSmartAccountClient({ ...config, account: maV2Account, - }); - - const installValidation = async ({ - validationConfig, - selectors, - installData, - hooks, - overrides, - }: InstallValidationParams) => { - if (validationConfig.entityId === 0) { - throw new EntityIdOverrideError(); - } - - const callData = await maV2Account.encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - ...maV2Account, - overrides, - }); - }; - - const uninstallValidation = async ({ - moduleAddress, - entityId, - uninstallData, - hookUninstallDatas, - overrides, - }: UninstallValidationParams) => { - const callData = await maV2Account.encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - ...maV2Account, - overrides, - }); - }; - - return { - ...client, - installValidation, - uninstallValidation, - }; + }).extend(mav2AccountActions); } diff --git a/account-kit/smart-contracts/src/ma-v2/decorators/modularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/decorators/modularAccountV2.ts new file mode 100644 index 0000000000..8d88d61473 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/decorators/modularAccountV2.ts @@ -0,0 +1,192 @@ +import { + AccountNotFoundError, + IncompatibleClientError, + isSmartAccountClient, + zeroAddress, +} from "@aa-sdk/core"; +import type { + SmartAccountSigner, + SmartContractAccount, + GetAccountParameter, + UserOperationOverridesParameter, + GetEntryPointFromAccount, + SmartAccountClient, +} from "@aa-sdk/core"; +import { getContract, maxUint32 } from "viem"; +import type { Hex, Address, Transport, Chain } from "viem"; +import type { SMAV2Account } from "../account/semiModularAccountV2.js"; +import { modularAccountAbi } from "../abis/modularAccountAbi.js"; +import { serializeModuleEntity } from "../actions/common/utils.js"; + +export type ExecutionDataView = { + module: Address; + skipRuntimeValidation: boolean; + allowGlobalValidation: boolean; + executionHooks: readonly Hex[]; +}; + +export type ValidationDataView = { + validationHooks: readonly Hex[]; + executionHooks: readonly Hex[]; + selectors: readonly Hex[]; + validationFlags: number; +}; + +export type ValidationDataParams = + | { + validationModuleAddress: Address; + entityId?: never; + } + | { + validationModuleAddress?: never; + entityId: number; + }; + +export type GetCalldataParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner, + TEntryPointVersion extends GetEntryPointFromAccount = GetEntryPointFromAccount +> = { callData: Hex } & GetAccountParameter> & + UserOperationOverridesParameter; + +export type GetExecutionDataParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner, + TEntryPointVersion extends GetEntryPointFromAccount = GetEntryPointFromAccount +> = { selector: Hex } & GetAccountParameter> & + UserOperationOverridesParameter; + +export type GetValidationDataParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner, + TEntryPointVersion extends GetEntryPointFromAccount = GetEntryPointFromAccount +> = ValidationDataParams & + GetAccountParameter> & + UserOperationOverridesParameter; + +export type MAV2AccountClientActions< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner +> = { + encodeCallData: (args: GetCalldataParams) => Promise; + getExecutionData: ( + args: GetExecutionDataParams + ) => Promise; + getValidationData: ( + args: GetValidationDataParams + ) => Promise; +}; + +export const mav2AccountActions: < + TTransport extends Transport = Transport, + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined, + TChain extends Chain | undefined = Chain | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + client: SmartAccountClient +) => MAV2AccountClientActions = (client) => ({ + getExecutionData: async ({ + selector, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "getExecutionData", + client + ); + } + + if (!(await account.isAccountDeployed())) { + return { + module: zeroAddress, + skipRuntimeValidation: false, + allowGlobalValidation: false, + executionHooks: [], + }; + } + + const accountContract = getContract({ + address: account.address, + abi: modularAccountAbi, + client, + }); + + return await accountContract.read.getExecutionData([selector]); + }, + getValidationData: async ({ + validationModuleAddress, + entityId, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "getValidationData", + client + ); + } + + if (!(await account.isAccountDeployed())) { + return { + validationHooks: [], + executionHooks: [], + selectors: false, + validationFlags: [], + }; + } + + const accountContract = getContract({ + address: account.address, + abi: modularAccountAbi, + client, + }); + + return await accountContract.read.getValidationData([ + serializeModuleEntity({ + moduleAddress: validationModuleAddress ?? zeroAddress, + entityId: entityId ?? Number(maxUint32), + }), + ]); + }, + + encodeCallData: async ({ callData, account = client.account, overrides }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "encodeCallData", + client + ); + } + + const validationData = await getValidationData({ + entityId: Number(entityId), + }); + + return account.encodeCallData(callData, overrides); + }, +});