Prerequisites
You need a deployed smart account with session key module support.Create Smart Account
Create Session Keys
- ZeroDev
- Biconomy
- Safe
Copy
Ask AI
import { toECDSASigner } from "@zerodev/permissions/signers";
import { toPermissionValidator } from "@zerodev/permissions";
import { toCallPolicy, toSudoPolicy } from "@zerodev/permissions/policies";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
async function createSessionKey(
kernelClient: any,
permissions: {
target?: string;
functionSelector?: string;
valueLimit?: bigint;
validAfter?: number;
validUntil?: number;
}
) {
const sessionPrivateKey = generatePrivateKey();
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);
console.log("Session Key Address:", sessionKeySigner.address);
const ecdsaSigner = toECDSASigner({ signer: sessionKeySigner });
const policies = [];
if (permissions.target) {
policies.push(
toCallPolicy({
target: permissions.target,
selector: permissions.functionSelector,
valueLimit: permissions.valueLimit || 0n
})
);
}
const permissionPlugin = await toPermissionValidator(kernelClient.publicClient, {
signer: ecdsaSigner,
policies,
validAfter: permissions.validAfter || Math.floor(Date.now() / 1000),
validUntil: permissions.validUntil || Math.floor(Date.now() / 1000) + 86400
});
const userOpHash = await kernelClient.installPlugin({
plugin: permissionPlugin
});
await kernelClient.waitForUserOperationReceipt({ hash: userOpHash });
console.log("Session key created!");
return {
sessionKey: sessionPrivateKey,
sessionKeySigner,
permissionPlugin
};
}
Copy
Ask AI
import { createSessionKeyManagerModule } from "@biconomy/modules";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
async function createSessionKey(
smartAccount: any,
sessionConfig: {
contractAddress: string;
functionSelector: string;
rules?: Array<{
offset: number;
condition: number;
referenceValue: string;
}>;
validAfter?: number;
validUntil?: number;
}
) {
const sessionPrivateKey = generatePrivateKey();
const sessionSigner = privateKeyToAccount(sessionPrivateKey);
const sessionModule = await createSessionKeyManagerModule({
smartAccountAddress: await smartAccount.getAccountAddress()
});
const sessionData = {
validUntil: sessionConfig.validUntil || Math.floor(Date.now() / 1000) + 86400,
validAfter: sessionConfig.validAfter || 0,
sessionValidationModule: sessionModule.address,
sessionPublicKey: sessionSigner.address,
sessionKeyData: sessionModule.interface.encodeFunctionData(
"setSessionKey",
[
sessionConfig.contractAddress,
sessionConfig.functionSelector,
sessionConfig.rules || [],
sessionConfig.validAfter || 0,
sessionConfig.validUntil || Math.floor(Date.now() / 1000) + 86400
]
)
};
const { transactionHash } = await smartAccount.createSessionKey(
sessionData,
{ paymasterServiceData: { mode: "SPONSORED" } }
);
console.log("Session key created:", transactionHash);
console.log("Session Key Address:", sessionSigner.address);
return {
sessionKey: sessionPrivateKey,
sessionSigner,
sessionModule,
transactionHash
};
}
Copy
Ask AI
import { Safe4337Pack } from "@safe-global/relay-kit";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
async function createSessionKey(
safe4337Pack: any,
sessionConfig: {
delegate: string;
validAfter?: number;
validUntil?: number;
allowedMethods?: string[];
}
) {
const sessionPrivateKey = generatePrivateKey();
const sessionSigner = privateKeyToAccount(sessionPrivateKey);
const addDelegateTx = await safe4337Pack.createTransaction({
transactions: [{
to: await safe4337Pack.getSafeAddress(),
value: "0",
data: safe4337Pack.encodeFunctionData("addDelegate", [
sessionSigner.address,
sessionConfig.validAfter || Math.floor(Date.now() / 1000),
sessionConfig.validUntil || Math.floor(Date.now() / 1000) + 86400,
sessionConfig.allowedMethods || []
])
}]
});
const signedTx = await safe4337Pack.signTransaction(addDelegateTx);
const userOpHash = await safe4337Pack.executeTransaction({
executable: signedTx
});
console.log("Session delegate added:", userOpHash);
console.log("Session Key Address:", sessionSigner.address);
return {
sessionKey: sessionPrivateKey,
sessionSigner,
userOpHash
};
}
Use Session Keys
- ZeroDev
- Biconomy
- Safe
Copy
Ask AI
import { createKernelAccountClient } from "@zerodev/sdk";
import { toECDSASigner } from "@zerodev/permissions/signers";
import { privateKeyToAccount } from "viem/accounts";
async function useSessionKey(
sessionPrivateKey: string,
account: any,
bundlerUrl: string,
publicClient: any
) {
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);
const ecdsaSigner = toECDSASigner({ signer: sessionKeySigner });
const sessionKeyClient = createKernelAccountClient({
account,
chain: account.client.chain,
bundlerTransport: http(bundlerUrl),
client: publicClient,
middleware: {
sponsorUserOperation: async (args) => {
console.log("Using session key for:", args);
return args;
}
}
});
console.log("Session key client ready");
console.log("Using session key:", sessionKeySigner.address);
return sessionKeyClient;
}
Copy
Ask AI
import { createSmartAccountClient } from "@biconomy/account";
import { privateKeyToAccount } from "viem/accounts";
async function useSessionKey(
sessionPrivateKey: string,
sessionModule: any,
chainId: number,
bundlerUrl: string
) {
const sessionSigner = privateKeyToAccount(sessionPrivateKey);
const biconomySmartAccount = await createSmartAccountClient({
signer: sessionSigner,
chainId,
bundler: { bundlerUrl },
defaultValidationModule: sessionModule
});
console.log("Session account ready");
console.log("Using session key:", sessionSigner.address);
return biconomySmartAccount;
}
Copy
Ask AI
import { ethers } from "ethers";
async function useSessionKey(
sessionPrivateKey: string,
safeAddress: string,
provider: ethers.Provider
) {
const sessionWallet = new ethers.Wallet(sessionPrivateKey, provider);
console.log("Using session key:", sessionWallet.address);
console.log("For Safe:", safeAddress);
return {
sessionWallet,
executeWithSession: async (transaction: any) => {
const delegatedTx = {
...transaction,
from: sessionWallet.address,
delegateCall: true
};
return await sessionWallet.sendTransaction(delegatedTx);
}
};
}
Scoped Permissions
- ZeroDev
- Biconomy
Copy
Ask AI
import { toCallPolicy, toGasPolicy, toRateLimitPolicy } from "@zerodev/permissions/policies";
import { encodeFunctionData, parseAbi } from "viem";
async function createScopedSessionKey(
kernelClient: any,
config: {
erc20Token: string;
spendLimit: bigint;
recipient: string;
rateLimit: number;
}
) {
const erc20Abi = parseAbi([
"function transfer(address to, uint256 amount) returns (bool)"
]);
const policies = [
toCallPolicy({
target: config.erc20Token,
selector: "0xa9059cbb",
valueLimit: 0n,
constraints: [
{
condition: "EQUAL",
offset: 0,
value: config.recipient
},
{
condition: "LESS_THAN_OR_EQUAL",
offset: 32,
value: config.spendLimit
}
]
}),
toGasPolicy({
allowed: parseEther("0.01")
}),
toRateLimitPolicy({
count: config.rateLimit,
interval: 86400
})
];
const sessionPrivateKey = generatePrivateKey();
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);
const ecdsaSigner = toECDSASigner({ signer: sessionKeySigner });
const permissionPlugin = await toPermissionValidator(kernelClient.publicClient, {
signer: ecdsaSigner,
policies,
validUntil: Math.floor(Date.now() / 1000) + 86400
});
const userOpHash = await kernelClient.installPlugin({
plugin: permissionPlugin
});
await kernelClient.waitForUserOperationReceipt({ hash: userOpHash });
console.log("Scoped session key created");
console.log("- Token:", config.erc20Token);
console.log("- Recipient:", config.recipient);
console.log("- Spend Limit:", formatEther(config.spendLimit));
console.log("- Rate Limit:", config.rateLimit, "per day");
return {
sessionKey: sessionPrivateKey,
sessionKeySigner,
permissionPlugin
};
}
Copy
Ask AI
async function createScopedSessionKey(
smartAccount: any,
config: {
contractAddress: string;
methodNames: string[];
valueLimit: string;
usageLimit: number;
}
) {
const sessionModule = await createSessionKeyManagerModule({
smartAccountAddress: await smartAccount.getAccountAddress()
});
const sessionPrivateKey = generatePrivateKey();
const sessionSigner = privateKeyToAccount(sessionPrivateKey);
const sessionKeyData = [];
for (const methodName of config.methodNames) {
const methodSig = ethers.utils.id(methodName).slice(0, 10);
sessionKeyData.push({
validUntil: Math.floor(Date.now() / 1000) + 86400,
validAfter: 0,
sessionValidationModule: sessionModule.address,
sessionPublicKey: sessionSigner.address,
contractAddress: config.contractAddress,
functionSelector: methodSig,
rules: [
{
offset: 0,
condition: 2,
referenceValue: config.valueLimit
}
],
usageLimit: config.usageLimit
});
}
const { transactionHash } = await smartAccount.createBatchSessionKey(
sessionKeyData,
{ paymasterServiceData: { mode: "SPONSORED" } }
);
console.log("Scoped session key created:", transactionHash);
console.log("- Methods:", config.methodNames);
console.log("- Value Limit:", config.valueLimit);
console.log("- Usage Limit:", config.usageLimit);
return {
sessionKey: sessionPrivateKey,
sessionSigner,
sessionModule,
transactionHash
};
}
Revoke Session Keys
- ZeroDev
- Biconomy
- Safe
Copy
Ask AI
async function revokeSessionKey(
kernelClient: any,
permissionPlugin: any
) {
try {
const userOpHash = await kernelClient.uninstallPlugin({
plugin: permissionPlugin
});
console.log("Revoking session key...");
const receipt = await kernelClient.waitForUserOperationReceipt({
hash: userOpHash
});
console.log("Session key revoked:", receipt.receipt.transactionHash);
return receipt;
} catch (error) {
console.error("Failed to revoke session key:", error);
throw error;
}
}
Copy
Ask AI
async function revokeSessionKey(
smartAccount: any,
sessionPublicKey: string
) {
try {
const { transactionHash } = await smartAccount.revokeSessionKey(
sessionPublicKey,
{ paymasterServiceData: { mode: "SPONSORED" } }
);
console.log("Session key revoked:", transactionHash);
console.log("Revoked key:", sessionPublicKey);
return transactionHash;
} catch (error) {
console.error("Failed to revoke session key:", error);
throw error;
}
}
Copy
Ask AI
async function revokeSessionKey(
safe4337Pack: any,
delegateAddress: string
) {
try {
const removeDelegateTx = await safe4337Pack.createTransaction({
transactions: [{
to: await safe4337Pack.getSafeAddress(),
value: "0",
data: safe4337Pack.encodeFunctionData("removeDelegate", [
delegateAddress
])
}]
});
const signedTx = await safe4337Pack.signTransaction(removeDelegateTx);
const userOpHash = await safe4337Pack.executeTransaction({
executable: signedTx
});
console.log("Session delegate removed:", userOpHash);
console.log("Revoked delegate:", delegateAddress);
return userOpHash;
} catch (error) {
console.error("Failed to revoke session key:", error);
throw error;
}
}