Removing Session Keys
Session Keys are currently an experimental feature in the SDK. We are actively working on simplifying the usage, please note that there could be breaking changes as we improve this feature.
Removing session keys is done with the uninstallValidation
method.
import { function createModularAccountV2Client<TChain extends Chain = Chain, TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(args: CreateModularAccountV2AlchemyClientParams<AlchemyTransport, TChain, TSigner>): Promise<ModularAccountV2Client<TSigner, TChain, AlchemyTransport>> (+1 overload)createModularAccountV2Client } from "@account-kit/smart-contracts";
import {
const installValidationActions: <TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(client: ModularAccountV2Client<TSigner>) => InstallValidationActions<TSigner>Provides validation installation and uninstallation functionalities for a MA v2 client, ensuring compatibility with SmartAccountClient
.
installValidationActions,
const getDefaultSingleSignerValidationModuleAddress: (chain: Chain) => AddressMaps a given chain to a specific address of the single signer validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address.
getDefaultSingleSignerValidationModuleAddress,
const SingleSignerValidationModule: {
encodeOnInstallData: (args: {
entityId: number;
signer: Address;
}) => Hex;
encodeOnUninstallData: (args: {
entityId: number;
}) => Hex;
}SingleSignerValidationModule,
const modularAccountAbi: readonly [{
readonly type: "constructor";
readonly inputs: readonly [{
readonly name: "entryPoint";
readonly type: "address";
readonly internalType: "contract IEntryPoint";
}, {
readonly name: "executionInstallDelegate";
readonly type: "address";
readonly internalType: "contract ExecutionInstallDelegate";
}];
readonly stateMutability: "nonpayable";
}, ... 54 more ..., {
...;
}]modularAccountAbi,
} from "@account-kit/smart-contracts/experimental";
import { class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.
LocalAccountSigner } from "@aa-sdk/core";
import { const sepolia: Chainsepolia, function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.
alchemy } from "@account-kit/infra";
import { function generatePrivateKey(): HexgeneratePrivateKey } from "viem/accounts";
const const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client = (
await createModularAccountV2Client<Chain, LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>(args: CreateModularAccountV2AlchemyClientParams<...>): Promise<...> (+1 overload)createModularAccountV2Client({
chain: {
blockExplorers?: {
[key: string]: ChainBlockExplorer;
default: ChainBlockExplorer;
} | undefined;
... 7 more ...;
testnet?: boolean | undefined;
} & ChainConfig<...>Chain for the client.
chain: const sepolia: Chainsepolia,
transport: AlchemyTransportThe RPC transport
transport: function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.
alchemy({ apiKey: stringapiKey: "your-api-key" }),
signer: LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>signer: class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.
LocalAccountSigner.LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>Creates a LocalAccountSigner
instance using the provided private key.
privateKeyToAccountSigner(function generatePrivateKey(): HexgeneratePrivateKey()),
})
).extend: <InstallValidationActions<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>>(fn: (client: Client<...>) => InstallValidationActions<...>) => Client<...>extend(const installValidationActions: <TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(client: ModularAccountV2Client<TSigner>) => InstallValidationActions<TSigner>Provides validation installation and uninstallation functionalities for a MA v2 client, ensuring compatibility with SmartAccountClient
.
installValidationActions);
let let sessionKeyEntityId: numbersessionKeyEntityId = 1;
// Removing a basic session key
await const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.uninstallValidation: (args: UninstallValidationParams<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>) => Promise<SendUserOperationResult>uninstallValidation({
moduleAddress: `0x${string}`moduleAddress: function getDefaultSingleSignerValidationModuleAddress(chain: Chain): AddressMaps a given chain to a specific address of the single signer validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address.
getDefaultSingleSignerValidationModuleAddress(const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.chain: ChainChain for the client.
chain),
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
uninstallData: `0x${string}`uninstallData: const SingleSignerValidationModule: {
encodeOnInstallData: (args: {
entityId: number;
signer: Address;
}) => Hex;
encodeOnUninstallData: (args: {
entityId: number;
}) => Hex;
}SingleSignerValidationModule.encodeOnUninstallData: (args: {
entityId: number;
}) => HexencodeOnUninstallData({
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
}),
hookUninstallDatas: `0x${string}`[]hookUninstallDatas: [],
});
// Removing a session key with hooks
await const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.uninstallValidation: (args: UninstallValidationParams<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>) => Promise<SendUserOperationResult>uninstallValidation({
moduleAddress: `0x${string}`moduleAddress: function getDefaultSingleSignerValidationModuleAddress(chain: Chain): AddressMaps a given chain to a specific address of the single signer validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address.
getDefaultSingleSignerValidationModuleAddress(const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.chain: ChainChain for the client.
chain),
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
uninstallData: `0x${string}`uninstallData: const SingleSignerValidationModule: {
encodeOnInstallData: (args: {
entityId: number;
signer: Address;
}) => Hex;
encodeOnUninstallData: (args: {
entityId: number;
}) => Hex;
}SingleSignerValidationModule.encodeOnUninstallData: (args: {
entityId: number;
}) => HexencodeOnUninstallData({
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
}),
hookUninstallDatas: `0x${string}`[]hookUninstallDatas: [],
});
If there are hooks on the validation, you have to provide the hook uninstallation data to uninstall them too. Each module provides an encodeOnUninstallData
helper function to generate the uninstallation hook for that module.
import { function createModularAccountV2Client<TChain extends Chain = Chain, TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(args: CreateModularAccountV2AlchemyClientParams<AlchemyTransport, TChain, TSigner>): Promise<ModularAccountV2Client<TSigner, TChain, AlchemyTransport>> (+1 overload)createModularAccountV2Client } from "@account-kit/smart-contracts";
import {
const installValidationActions: <TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(client: ModularAccountV2Client<TSigner>) => InstallValidationActions<TSigner>Provides validation installation and uninstallation functionalities for a MA v2 client, ensuring compatibility with SmartAccountClient
.
installValidationActions,
const getDefaultSingleSignerValidationModuleAddress: (chain: Chain) => AddressMaps a given chain to a specific address of the single signer validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address.
getDefaultSingleSignerValidationModuleAddress,
const SingleSignerValidationModule: {
encodeOnInstallData: (args: {
entityId: number;
signer: Address;
}) => Hex;
encodeOnUninstallData: (args: {
entityId: number;
}) => Hex;
}SingleSignerValidationModule,
const modularAccountAbi: readonly [{
readonly type: "constructor";
readonly inputs: readonly [{
readonly name: "entryPoint";
readonly type: "address";
readonly internalType: "contract IEntryPoint";
}, {
readonly name: "executionInstallDelegate";
readonly type: "address";
readonly internalType: "contract ExecutionInstallDelegate";
}];
readonly stateMutability: "nonpayable";
}, ... 54 more ..., {
...;
}]modularAccountAbi,
const AllowlistModule: {
abi: readonly [{
readonly type: "function";
readonly name: "addressAllowlist";
readonly inputs: readonly [{
readonly name: "entityId";
readonly type: "uint32";
readonly internalType: "uint32";
}, {
readonly name: "target";
readonly type: "address";
readonly internalType: "address";
}, {
readonly name: "account";
readonly type: "address";
readonly internalType: "address";
}];
readonly outputs: readonly [{
readonly name: "allowed";
readonly type: "bool";
readonly internalType: "bool";
}, {
readonly name: "hasSelectorAllowlist";
readonly type: "bool";
readonly internalType: "bool";
}, {
readonly name: "hasERC20SpendLimit";
readonly type: "bool";
readonly internalType: "bool";
}];
readonly stateMutability: "view";
}, {
readonly type: "function";
readonly name: "checkAllowlistCalldata";
readonly inputs: readonly [{
readonly name: "entityId";
readonly type: "uint32";
readonly internalType: "uint32";
}, {
readonly name: "callData";
readonly type: "bytes";
readonly internalType: "bytes";
}];
readonly outputs: readonly [];
readonly stateMutability: "view";
}, {
readonly type: "function";
readonly name: "deleteAllowlist";
readonly ...AllowlistModule,
} from "@account-kit/smart-contracts/experimental";
import { class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.
LocalAccountSigner } from "@aa-sdk/core";
import { const sepolia: Chainsepolia, function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.
alchemy } from "@account-kit/infra";
import { function generatePrivateKey(): HexgeneratePrivateKey } from "viem/accounts";
const const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client = (
await createModularAccountV2Client<Chain, LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>(args: CreateModularAccountV2AlchemyClientParams<...>): Promise<...> (+1 overload)createModularAccountV2Client({
chain: {
blockExplorers?: {
[key: string]: ChainBlockExplorer;
default: ChainBlockExplorer;
} | undefined;
... 7 more ...;
testnet?: boolean | undefined;
} & ChainConfig<...>Chain for the client.
chain: const sepolia: Chainsepolia,
transport: AlchemyTransportThe RPC transport
transport: function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.
alchemy({ apiKey: stringapiKey: "your-api-key" }),
signer: LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>signer: class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.
LocalAccountSigner.LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>Creates a LocalAccountSigner
instance using the provided private key.
privateKeyToAccountSigner(function generatePrivateKey(): HexgeneratePrivateKey()),
})
).extend: <InstallValidationActions<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>>(fn: (client: Client<...>) => InstallValidationActions<...>) => Client<...>extend(const installValidationActions: <TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(client: ModularAccountV2Client<TSigner>) => InstallValidationActions<TSigner>Provides validation installation and uninstallation functionalities for a MA v2 client, ensuring compatibility with SmartAccountClient
.
installValidationActions);
let let sessionKeyEntityId: numbersessionKeyEntityId = 1;
// Removing a basic session key
await const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.uninstallValidation: (args: UninstallValidationParams<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>) => Promise<SendUserOperationResult>uninstallValidation({
moduleAddress: `0x${string}`moduleAddress: function getDefaultSingleSignerValidationModuleAddress(chain: Chain): AddressMaps a given chain to a specific address of the single signer validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address.
getDefaultSingleSignerValidationModuleAddress(const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.chain: ChainChain for the client.
chain),
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
uninstallData: `0x${string}`uninstallData: const SingleSignerValidationModule: {
encodeOnInstallData: (args: {
entityId: number;
signer: Address;
}) => Hex;
encodeOnUninstallData: (args: {
entityId: number;
}) => Hex;
}SingleSignerValidationModule.encodeOnUninstallData: (args: {
entityId: number;
}) => HexencodeOnUninstallData({
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
}),
hookUninstallDatas: `0x${string}`[]hookUninstallDatas: [],
});
const const hookEntityId: 1hookEntityId = 1;
const const target: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"target = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
// Removing a session key with an allowlist hook
await const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.uninstallValidation: (args: UninstallValidationParams<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>) => Promise<SendUserOperationResult>uninstallValidation({
moduleAddress: `0x${string}`moduleAddress: function getDefaultSingleSignerValidationModuleAddress(chain: Chain): AddressMaps a given chain to a specific address of the single signer validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address.
getDefaultSingleSignerValidationModuleAddress(const client: Client<AlchemyTransport, Chain, ModularAccountV2<LocalAccountSigner<{
address: Address;
nonceManager?: NonceManager | undefined;
sign: (parameters: {
hash: Hash;
}) => Promise<Hex>;
... 6 more ...;
type: "local";
}>>, [...], {
...;
} & ... 4 more ... & PublicActions>client.chain: ChainChain for the client.
chain),
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
uninstallData: `0x${string}`uninstallData: const SingleSignerValidationModule: {
encodeOnInstallData: (args: {
entityId: number;
signer: Address;
}) => Hex;
encodeOnUninstallData: (args: {
entityId: number;
}) => Hex;
}SingleSignerValidationModule.encodeOnUninstallData: (args: {
entityId: number;
}) => HexencodeOnUninstallData({
entityId: numberentityId: let sessionKeyEntityId: numbersessionKeyEntityId,
}),
hookUninstallDatas: `0x${string}`[]hookUninstallDatas: [
const AllowlistModule: {
abi: readonly [{
readonly type: "function";
readonly name: "addressAllowlist";
readonly inputs: readonly [{
readonly name: "entityId";
readonly type: "uint32";
readonly internalType: "uint32";
}, {
readonly name: "target";
readonly type: "address";
readonly internalType: "address";
}, {
readonly name: "account";
readonly type: "address";
readonly internalType: "address";
}];
readonly outputs: readonly [{
readonly name: "allowed";
readonly type: "bool";
readonly internalType: "bool";
}, {
readonly name: "hasSelectorAllowlist";
readonly type: "bool";
readonly internalType: "bool";
}, {
readonly name: "hasERC20SpendLimit";
readonly type: "bool";
readonly internalType: "bool";
}];
readonly stateMutability: "view";
}, {
readonly type: "function";
readonly name: "checkAllowlistCalldata";
readonly inputs: readonly [{
readonly name: "entityId";
readonly type: "uint32";
readonly internalType: "uint32";
}, {
readonly name: "callData";
readonly type: "bytes";
readonly internalType: "bytes";
}];
readonly outputs: readonly [];
readonly stateMutability: "view";
}, {
readonly type: "function";
readonly name: "deleteAllowlist";
readonly ...AllowlistModule.encodeOnUninstallData: (args: {
entityId: number;
inputs: Array<{
target: Address;
hasSelectorAllowlist: boolean;
hasERC20SpendLimit: boolean;
erc20SpendLimit: bigint;
selectors: Array<Hex>;
}>;
}) => HexencodeOnUninstallData({
entityId: numberentityId: const hookEntityId: 1hookEntityId,
inputs: {
target: Address;
hasSelectorAllowlist: boolean;
hasERC20SpendLimit: boolean;
erc20SpendLimit: bigint;
selectors: Array<Hex>;
}[]inputs: [
{
target: `0x${string}`target,
hasSelectorAllowlist: booleanhasSelectorAllowlist: false,
hasERC20SpendLimit: booleanhasERC20SpendLimit: false,
erc20SpendLimit: biginterc20SpendLimit: 0n,
selectors: `0x${string}`[]selectors: [],
},
],
}),
],
});