diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index 8e71008c83..c24cd64a2d 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -85,3 +85,19 @@ export class InvalidNonceKeyError extends BaseError { super(`Nonce key is ${nonceKey} but has to be less than 2**152`); } } + +/** + * Error class denoting that the provided entity id is invalid because it's overriding the native entity id. + */ +export class EntityIdOverrideError extends BaseError { + override name = "InvalidNonceKeyError"; + + /** + * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. + */ + constructor() { + super( + `Installing entityId of 0 overrides the owner's entity id in the account` + ); + } +} diff --git a/aa-sdk/core/src/index.ts b/aa-sdk/core/src/index.ts index c5d33dea24..5f66aae5cc 100644 --- a/aa-sdk/core/src/index.ts +++ b/aa-sdk/core/src/index.ts @@ -75,6 +75,7 @@ export { InvalidRpcUrlError, InvalidEntityIdError, InvalidNonceKeyError, + EntityIdOverrideError, } from "./errors/client.js"; export { EntryPointNotFoundError, 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 2d3aee96a3..090fc7b11d 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -1,6 +1,7 @@ import type { EntryPointDef, SmartAccountSigner, + AccountOp, SmartContractAccountWithSigner, ToSmartContractAccountParams, } from "@aa-sdk/core"; @@ -8,6 +9,8 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, + InvalidEntityIdError, + InvalidNonceKeyError, } from "@aa-sdk/core"; import { concatHex, @@ -15,28 +18,62 @@ import { getContract, maxUint32, maxUint152, + zeroAddress, type Address, type Chain, type Hex, type Transport, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; -import { getDefaultMAV2FactoryAddress } from "../utils.js"; -import { standardExecutor } from "../../msca/account/standardExecutor.js"; +import { + getDefaultMAV2FactoryAddress, + DEFAULT_OWNER_ENTITY_ID, +} from "../utils.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"; -export const DEFAULT_OWNER_ENTITY_ID = 0; +const executeUserOpSelector: Hex = "0x8DD7712F"; export type SignerEntity = { isGlobalValidation: boolean; entityId: number; }; +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 SMAV2Account< TSigner extends SmartAccountSigner = SmartAccountSigner > = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0"> & - SignerEntity; + SignerEntity & { + getExecutionData: (selector: Hex) => Promise; + getValidationData: ( + args: ValidationDataParams + ) => Promise; + encodeCallData: (callData: Hex) => Promise; + }; export type CreateSMAV2AccountParams< TTransport extends Transport = Transport, @@ -80,7 +117,7 @@ export async function createSMAV2Account( accountAddress, entryPoint = getEntryPoint(chain, { version: "0.7.0" }), isGlobalValidation = true, - entityId = 0, + entityId = DEFAULT_OWNER_ENTITY_ID, } = config; if (entityId > Number(maxUint32)) { @@ -110,14 +147,43 @@ export async function createSMAV2Account( ]); }; + const encodeExecute: (tx: AccountOp) => Promise = async ({ + target, + data, + value, + }) => + await encodeCallData( + encodeFunctionData({ + abi: modularAccountAbi, + functionName: "execute", + args: [target, value ?? 0n, data], + }) + ); + + 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), }); @@ -144,11 +210,62 @@ export async function createSMAV2Account( ]) as Promise; }; + const accountContract = getContract({ + address: baseAccount.address, + abi: modularAccountAbi, + client, + }); + + const getExecutionData = async (selector: Hex) => { + if (!(await baseAccount.isAccountDeployed())) { + return { + module: zeroAddress, + skipRuntimeValidation: false, + allowGlobalValidation: false, + executionHooks: [], + }; + } + + return await accountContract.read.getExecutionData([selector]); + }; + + const getValidationData = async (args: ValidationDataParams) => { + if (!(await baseAccount.isAccountDeployed())) { + return { + validationHooks: [], + executionHooks: [], + selectors: [], + validationFlags: 0, + }; + } + + const { validationModuleAddress, entityId } = args; + return await accountContract.read.getValidationData([ + serializeModuleEntity({ + moduleAddress: validationModuleAddress ?? zeroAddress, + entityId: entityId ?? Number(maxUint32), + }), + ]); + }; + + const encodeCallData = async (callData: Hex): Promise => { + const validationData = await getValidationData({ + entityId: Number(entityId), + }); + + return validationData.executionHooks.length + ? concatHex([executeUserOpSelector, callData]) + : callData; + }; + return { ...baseAccount, getAccountNonce, getSigner: () => signer, isGlobalValidation, entityId, + getExecutionData, + getValidationData, + encodeCallData, }; } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts new file mode 100644 index 0000000000..14f5739e6e --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts @@ -0,0 +1,37 @@ +import type { Address, Hex } from "viem"; + +export type ModuleEntity = { + moduleAddress: Address; + entityId: number; +}; + +export type ValidationConfig = { + moduleAddress: Address; + entityId: number; // uint32 + isGlobal: boolean; + isSignatureValidation: boolean; + isUserOpValidation: boolean; +}; + +export enum HookType { + EXECUTION = "0x00", + VALIDATION = "0x01", +} + +export type HookConfig = { + address: Address; + entityId: number; // uint32 + hookType: HookType; + hasPreHooks: boolean; + hasPostHooks: boolean; +}; + +// maps to type ValidationStorage in MAv2 implementation +export type ValidationData = { + isGlobal: boolean; // validation flag + isSignatureValidation: boolean; // validation flag + isUserOpValidation: boolean; + validationHooks: HookConfig[]; + executionHooks: Hex[]; + selectors: Hex[]; +}; diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts new file mode 100644 index 0000000000..8bd366ab76 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts @@ -0,0 +1,32 @@ +import { type Hex, toHex, concatHex } from "viem"; +import type { ValidationConfig, HookConfig, ModuleEntity } from "./types"; +import { HookType } from "./types.js"; + +export function serializeValidationConfig(config: ValidationConfig): Hex { + const isUserOpValidationBit = config.isUserOpValidation ? 1 : 0; + const isSignatureValidationBit = config.isSignatureValidation ? 2 : 0; + const isGlobalBit = config.isGlobal ? 4 : 0; + return concatHex([ + serializeModuleEntity(config), + toHex(isUserOpValidationBit + isSignatureValidationBit + isGlobalBit, { + size: 1, + }), + ]); +} + +export function serializeHookConfig(config: HookConfig): Hex { + const hookTypeBit = config.hookType === HookType.VALIDATION ? 1 : 0; + const hasPostHooksBit = config.hasPostHooks ? 2 : 0; + const hasPreHooksBit = config.hasPreHooks ? 4 : 0; + return concatHex([ + config.address, + toHex(config.entityId, { size: 4 }), + toHex(hookTypeBit + hasPostHooksBit + hasPreHooksBit, { + size: 1, + }), + ]); +} + +export function serializeModuleEntity(config: ModuleEntity): Hex { + return concatHex([config.moduleAddress, toHex(config.entityId, { size: 4 })]); +} 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..dc30cf6870 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -0,0 +1,160 @@ +import { + AccountNotFoundError, + IncompatibleClientError, + isSmartAccountClient, + EntityIdOverrideError, + type GetEntryPointFromAccount, + type SendUserOperationResult, + type UserOperationOverridesParameter, + type SmartAccountSigner, +} from "@aa-sdk/core"; +import { type Address, type Hex, encodeFunctionData, concatHex } 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 SMAV2AccountClient } from "../../client/client.js"; +import { type SMAV2Account } from "../../account/semiModularAccountV2.js"; +import { DEFAULT_OWNER_ENTITY_ID } from "../../utils.js"; + +export type InstallValidationParams< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = { + validationConfig: ValidationConfig; + selectors: Hex[]; + installData: Hex; + hooks: { + hookConfig: HookConfig; + initData: Hex; + }[]; + account?: SMAV2Account | undefined; +} & UserOperationOverridesParameter< + GetEntryPointFromAccount> +>; + +export type UninstallValidationParams< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = { + moduleAddress: Address; + entityId: number; + uninstallData: Hex; + hookUninstallDatas: Hex[]; + account?: SMAV2Account | undefined; +} & UserOperationOverridesParameter< + GetEntryPointFromAccount> +>; + +export type InstallValidationActions< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = { + installValidation: ( + args: InstallValidationParams + ) => Promise; + uninstallValidation: ( + args: UninstallValidationParams + ) => Promise; +}; + +export const installValidationActions: < + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + client: SMAV2AccountClient +) => 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 + ); + } + + // TO DO: handle installing on fallback validation (entityId == 0) with non-zero address + // if (validationConfig.entityId === DEFAULT_OWNER_ENTITY_ID) { + // throw new EntityIdOverrideError(); + // } + + const { encodeCallData } = account; + + const callData = await 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 { encodeCallData } = account; + + const callData = await 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 21a9083557..58e45bffe4 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,12 +1,22 @@ -import { custom, parseEther, publicActions } from "viem"; - -import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; - +import { custom, parseEther, publicActions, zeroAddress } from "viem"; +import { + erc7677Middleware, + LocalAccountSigner, + type SmartAccountSigner, +} from "@aa-sdk/core"; import { createSMAV2AccountClient } from "./client.js"; - import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; +import { + getDefaultPaymasterGuardModuleAddress, + getDefaultSingleSignerValidationModuleAddress, +} from "../modules/utils.js"; +import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; +import { paymaster070 } from "~test/paymaster/paymaster070.js"; +import { PaymasterGuardModule } from "../modules/paymaster-guard-module/module.js"; +import { HookType } from "../actions/common/types.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -21,6 +31,18 @@ describe("MA v2 Tests", async () => { accounts.fundedAccountOwner ); + const sessionKey: SmartAccountSigner = new LocalAccountSigner( + accounts.unfundedAccountOwner + ); + + const target = "0x000000000000000000000000000000000000dEaD"; + const sendAmount = parseEther("1"); + + const getTargetBalance = async (): Promise => + client.getBalance({ + address: target, + }); + it("sends a simple UO", async () => { const provider = await givenConnectedProvider({ signer }); @@ -29,12 +51,7 @@ describe("MA v2 Tests", async () => { value: parseEther("2"), }); - const target = "0x000000000000000000000000000000000000dEaD"; - const sendAmount = parseEther("1"); - - const startingAddressBalance = await client.getBalance({ - address: target, - }); + const startingAddressBalance = await getTargetBalance(); const result = await provider.sendUserOperation({ uo: { @@ -46,23 +63,306 @@ describe("MA v2 Tests", async () => { await provider.waitForUserOperationTransaction(result); - const newAddressBalance = await client.getBalance({ - address: target, + await expect(getTargetBalance()).resolves.toEqual( + startingAddressBalance + sendAmount + ); + }); + + it("adds a session key with no permissions", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), }); - await expect(newAddressBalance).toEqual( + let result = await provider.installValidation({ + validationConfig: { + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + const startingAddressBalance = await getTargetBalance(); + + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1, + isGlobalValidation: true, + }); + + result = await sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + txnHash = sessionKeyClient.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + await expect(getTargetBalance()).resolves.toEqual( startingAddressBalance + sendAmount ); }); + it("uninstalls a session key", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + let result = await provider.installValidation({ + validationConfig: { + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + result = await provider.uninstallValidation({ + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + uninstallData: SingleSignerValidationModule.encodeOnUninstallData({ + entityId: 1, + }), + hookUninstallDatas: [], + }); + + txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1, + isGlobalValidation: true, + }); + + await expect( + sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }) + ).rejects.toThrowError(); + }); + + it("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { + let provider = ( + await givenConnectedProvider({ + signer, + usePaymaster: true, + }) + ).extend(installValidationActions); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const paymaster = paymaster070.getPaymasterStubData(); + + const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ + entityId: 0, + paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE + }); + + const installResult = await provider.installValidation({ + validationConfig: { + moduleAddress: zeroAddress, + entityId: 0, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: "0x", + hooks: [ + { + hookConfig: { + address: getDefaultPaymasterGuardModuleAddress(provider.chain), + entityId: 0, // uint32 + hookType: HookType.VALIDATION, + hasPreHooks: true, + hasPostHooks: true, + }, + initData: hookInstallData, + }, + ], + }); + + // verify hook installtion succeeded + await provider.waitForUserOperationTransaction(installResult); + + // happy path: send a UO with correct paymaster + const result = await provider.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + // verify if correct paymaster is used + const txnHash1 = provider.waitForUserOperationTransaction(result); + await expect(txnHash1).resolves.not.toThrowError(); + + const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ + entityId: 0, + }); + + const uninstallResult = await provider.uninstallValidation({ + moduleAddress: zeroAddress, + entityId: 0, + uninstallData: "0x", + hookUninstallDatas: [hookUninstallData], + }); + + // verify uninstall + await expect( + provider.waitForUserOperationTransaction(uninstallResult) + ).resolves.not.toThrowError(); + }); + + it("installs paymaster guard module, verifies use of invalid paymaster, then uninstalls module", async () => { + let provider = ( + await givenConnectedProvider({ + signer, + usePaymaster: true, + }) + ).extend(installValidationActions); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const paymaster = paymaster070.getPaymasterStubData(); + + const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ + entityId: 0, + paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE + }); + + const installResult = await provider.installValidation({ + validationConfig: { + moduleAddress: zeroAddress, + entityId: 0, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: "0x", + hooks: [ + { + hookConfig: { + address: getDefaultPaymasterGuardModuleAddress(provider.chain), + entityId: 0, // uint32 + hookType: HookType.VALIDATION, + hasPreHooks: true, + hasPostHooks: true, + }, + initData: hookInstallData, + }, + ], + }); + + // verify hook installtion succeeded + await provider.waitForUserOperationTransaction(installResult); + + // sad path: send UO with no paymaster + let providerNoPaymaster = await givenConnectedProvider({ + signer, + usePaymaster: false, + }); + + await expect( + providerNoPaymaster.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }) + ).rejects.toThrowError(); + + const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ + entityId: 0, + }); + + const uninstallResult = await provider.uninstallValidation({ + moduleAddress: zeroAddress, + entityId: 0, + uninstallData: "0x", + hookUninstallDatas: [hookUninstallData], + }); + + // verify uninstall + await expect( + provider.waitForUserOperationTransaction(uninstallResult) + ).resolves.not.toThrowError(); + }); + const givenConnectedProvider = async ({ signer, + accountAddress, + usePaymaster = false, }: { signer: SmartAccountSigner; + accountAddress?: `0x${string}`; + usePaymaster?: boolean; }) => createSMAV2AccountClient({ chain: instance.chain, signer, + accountAddress, transport: custom(instance.getClient()), + ...(usePaymaster ? erc7677Middleware() : {}), }); }); 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 946061daa9..aba7e35df2 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -4,7 +4,7 @@ import { type SmartAccountSigner, type SmartAccountClientConfig, } from "@aa-sdk/core"; -import { type Chain, type CustomTransport, type Transport } from "viem"; +import { type Chain, type Transport } from "viem"; import { createSMAV2Account, @@ -12,6 +12,10 @@ import { type SMAV2Account, } from "../account/semiModularAccountV2.js"; +export type SMAV2AccountClient< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = SmartAccountClient>; + export type CreateSMAV2AccountClientParams< TTransport extends Transport = Transport, TChain extends Chain = Chain, @@ -27,7 +31,7 @@ export function createSMAV2AccountClient< TSigner extends SmartAccountSigner = SmartAccountSigner >( args: CreateSMAV2AccountClientParams -): Promise>>; +): Promise>; /** * Creates a MAv2 account client using the provided configuration parameters. @@ -60,7 +64,7 @@ export function createSMAV2AccountClient< */ export async function createSMAV2AccountClient({ ...config -}: CreateSMAV2AccountClientParams): Promise { +}: CreateSMAV2AccountClientParams): Promise { const maV2Account = await createSMAV2Account({ ...config, }); diff --git a/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/module.ts b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/module.ts new file mode 100644 index 0000000000..92638eb978 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/module.ts @@ -0,0 +1,35 @@ +import { encodeAbiParameters, type Address, type Hex } from "viem"; +import { paymasterGuardModuleAbi } from "./abis/paymasterGuardModuleAbi.js"; + +export const PaymasterGuardModule = { + abi: paymasterGuardModuleAbi, + encodeOnInstallData: (args: { + entityId: number; + paymaster: Address; + }): Hex => { + const { entityId, paymaster } = args; + return encodeAbiParameters( + [ + { + type: "uint32", + }, + { + type: "address", + }, + ], + [entityId, paymaster] + ); + }, + encodeOnUninstallData: (args: { entityId: number }): Hex => { + const { entityId } = args; + + return encodeAbiParameters( + [ + { + type: "uint32", + }, + ], + [entityId] + ); + }, +}; diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts index 241f89885b..73e241b70b 100644 --- a/account-kit/smart-contracts/src/ma-v2/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -12,6 +12,8 @@ import { sepolia, } from "@account-kit/infra"; +export const DEFAULT_OWNER_ENTITY_ID = 0; + export type PackSignatureParams = { // orderedHookData: HookData[]; validationSignature: Hex; diff --git a/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx b/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx new file mode 100644 index 0000000000..ef8e82cc95 --- /dev/null +++ b/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx @@ -0,0 +1,18 @@ +--- +# This file is autogenerated +title: EntityIdOverrideError +description: Overview of the EntityIdOverrideError method +--- + +# EntityIdOverrideError + +Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. +:::note +`EntityIdOverrideError` extends `BaseError`, see the docs for BaseError for all supported methods. +::: + +## Import + +```ts +import { EntityIdOverrideError } from "@aa-sdk/core"; +```