Smart Sessions
Smart Sessions allow accounts to delegate specific permissions to other signers with fine-grained control over what actions they can perform.
Enabling and using a permission with Smart Sessions is a two-step process involving setup and actual usage:
1. Enable Permission
The initial setup phase involves several key operations to prepare your account for session-based permissions:
- Deploy the SCA (if it does not exist yet).
- Fund the SCA to ensure gas costs or funding for your DeFi needs.
- Bridge Funds, Unwind Positions or etc as part of initial setup by passing extra instructions via the
instructions array.
- Install the Smart Sessions Module into the SCA, ensuring the account is capable of session management.
- Enable one or more permissions: Define specific actions and their allowed signers.
2. Use Permission
Once a permission is enabled, a valid session signer is now authorized to perform the allowed actions.
- The session signer can initiate actions within the allowed scope (such as contract calls, swaps, mints, etc.).
- Calls made by the session signer are validated against the permission rules applied in the previous step.
Prepare and enable permissions
Deploy accounts, fund accounts, install Smart Sessions module, and enable permissions.
const quote = await meeClient.getSessionQuote({ mode: 'PREPARE', ...options });
Parameters
| Parameter | Type | Required | Description |
|---|
mode | "PREPARE" | Yes | Prepare or use a permission. |
enableSession | EnableSession | No | Options to enable a session. |
smartSessionValidatorAddress | Address | No | Address of the Smart Session validator to use. |
instructions | Instruction[] | No | Array of composable instructions to be executed. |
feeToken | FeeTokenInfo | Yes* | Token used for gas payment. |
sponsorship | boolean | No | Enable or disable gas sponsorship. |
delegate | boolean | No | Enable EIP-7702 delegate mode. |
authorization | Authorization | No | EIP-7702 authorization configuration. |
lowerBoundTimestamp | number | No | Earliest allowed execution timestamp. |
upperBoundTimestamp | number | No | Latest allowed execution timestamp. |
cleanUps | CleanUp[] | No | List of cleanup instructions. |
simulation | SimulationOptions | No | Simulation config. |
EnableSession
| Property | Type | Required | Description |
|---|
redeemer | Address | Yes | Address of the session signer. |
actions | SessionAction[] | Yes | Allowed actions within this session. |
batchActions | boolean | No | Whether to allow batching of actions. |
maxPaymentAmount | bigint | No | Maximum payment amount allowed for the session. |
Payment policy and fee token: When the session token (e.g. the token used in spending limits or transfer actions) is the same as feeToken, the SDK merges those policies with the payment policy—the effective spending limit is increased by maxPaymentAmount and no duplicate payment action is added.
SessionAction
| Property | Type | Required | Description |
|---|
actions | ActionData[] | Yes | Specific action configurations (see ActionData). |
chainId | number | Yes | Chain ID where the permission is valid. |
ActionData
| Property | Type | Required | Description |
|---|
actionTarget | Address | Yes | Target contract address for this action. |
actionTargetSelector | Hex | Yes | Function selector (4-byte signature) for the contract method. |
actionPolicies | Policy[] | No | List of policies defining rules and restrictions on this action. |
Example
const quote = await meeClient.getSessionQuote({
mode: "PREPARE",
enableSession: {
redeemer: sessionSigner.address,
actions,
},
simulation: { simulate: true },
feeToken: { address: USDC, chainId: 8453 },
trigger: {
tokenAddress: USDC,
chainId: 8453,
amount: parseUnits("2", 6),
},
});
// Store the sessions details for later use.
let sessionDetails: SessionDetail[] = [];
if (quote) {
const { hash } = await meeClient.executeSessionQuote(quote);
await meeClient.waitForSupertransactionReceipt({ hash });
sessionDetails = quote.sessionDetails;
}
Use Permission
Execute instructions using granted permissions.
const quote = await meeClient.getSessionQuote({ mode: 'USE', ...options });
Parameters
| Parameter | Type | Required | Description |
|---|
mode | "USE" | Yes | Prepare or use a permission. |
sessionDetails | SessionDetails | Yes | Session information for enabled permissions |
instructions | Instruction[] | No | Array of instructions to be executed. |
feeToken | FeeTokenInfo | Yes* | Token used for gas payment. |
sponsorship | boolean | No | Enable or disable gas sponsorship. |
delegate | boolean | No | Enable EIP-7702 delegate mode. |
authorization | Authorization | No | EIP-7702 authorization configuration. |
lowerBoundTimestamp | number | No | Earliest allowed execution timestamp. |
upperBoundTimestamp | number | No | Latest allowed execution timestamp. |
simulation | SimulationOptions | No | Simulation config. |
Example
const quote = await meeClient.getSessionQuote({
mode: "USE",
sessionDetails,
simulation: { simulate: true },
feeToken: { address: USDC, chainId: 8453 },
instructions: [
{
chainId: 8453,
calls: [{ to: CounterContract, data: "0x..." }]
}
]
});
const { hash } = await meeClient.executeSessionQuote(quote);
await meeClient.waitForSupertransactionReceipt({ hash });
Policies
Sudo Policy
Grants unlimited permissions for specified functions.
const sudoPolicy = mcNexus.buildActionPolicy({ type: "sudo" })
Use with caution, provides highest level of access.
Universal Policy
Fine-grained parameter-level control.
const universalPolicy = mcNexus.buildActionPolicy({
type: "universal",
rules: [
{
condition: "equal",
calldataOffset: calldataArgument(2),
comparisonValue: parseUnits("10", 6),
isLimited: false,
usage: { limit: 0n, used: 0n }
}
],
// Configure spending limit for native token
valueLimitPerUse: 1n
});
Use calldataArgument(n) to specify the calldata offset for the n-th (1-based) function argument—e.g. calldataArgument(2) is the second parameter. Import from @biconomy/abstractjs.
Timeframe policy
Add fine-grained expirations to the session actions.
const now = Math.floor(Date.now() / 1000)
const validAfter = now
const validUntil = now + 3600
const timeframePolicy = mcNexus.buildActionPolicy({
type: "timeframe",
validAfter, // unix-timestamp when policy becomes active
validUntil // unix-timestamp when policy expires
});
Spending limit policy
Add spending limits for the token spendings.
const spendingLimitsPolicy = mcNexus.buildActionPolicy({
type: "spendingLimits",
tokenLimits: [{ token: USDC, limit: parseUnits("10", 6) }]
});
Usage limit policy
Add usage limits to control the session usage count.
const usagePolicy = mcNexus.buildActionPolicy({
type: "usageLimit",
limit: 5n
});
Actions
Frequently Used Actions
Build approve, transfer, and transferFrom actions for your session
const actions = mcNexus.buildSessionAction({
type: "approve" | "transferFrom" | "transfer",
data: {
chainIds: [8453, 10],
contractAddress: USDC,
recipientAddress: "0x..." // Optional
amountLimitPerAction: parseUnits("10", 6), // Optional
maxAmountLimit: parseUnits("10", 6), // Optional
usageLimit: 10n, // Optional
validAfter: Math.floor(Date.now() / 1000), // Optional
validUntil: Math.floor(Date.now() / 1000) + 60 * 60 * 24 // Optional
// or
// policies: [] // Define policies on your own. Defaults to sudo policy
},
});
By default, sudo policy will be configured. Configure policies on your own by defining policies
Custom Actions
Build custom actions for your session
const functionSignature = toFunctionSelector(
getAbiItem({ abi: CounterAbi, name: "incrementCount" })
);
const customActions = mcNexus.buildSessionAction({
type: "custom",
data: {
chainIds: [8453, 10],
contractAddress: CounterContract,
functionSignature,
policies: [{ type: "sudo" }] // Optional
}
});
Custom action batching
By default, all actions are submitted as a single atomic batch when enabling session permissions.
If you encounter issues with block gas limits on certain chains, you can break the actions into multiple smaller batches.
const actionOne = mcNexus.buildSessionAction({...});
const actionTwo = mcNexus.buildSessionAction({...});
const batchedActions = mcNexus.buildSessionAction({
type: "batch",
data: { actions: [...actionOne, ...actionTwo] }
});
const actionThree = mcNexus.buildSessionAction({...});
// Actions can be partially batched
const actions = [...batchedActions, ...actionThree];
To enable custom batching, make sure to configure batchActions: false in enableSession while preparing the permissions.
Check Permission Status
meeSessionActions
Extend the MEE client with session-related methods.
import { meeSessionActions } from "@biconomy/abstractjs";
const sessionClient = meeClient.extend(meeSessionActions);
The following methods will be extended
isPermissionEnabled
checkEnabledPermissions
isPermissionEnabled
const isEnabled = await sessionClient.isPermissionEnabled({
permissionId: permission.permissionId,
chainId: 8453
});
checkEnabledPermissions
const enabledMap = await sessionClient.checkEnabledPermissions(sessionDetails);
// Returns: { [permissionId]: { [chainId]: boolean } }
Complete Flow
// 1. User account setup
const userSigner = privateKeyToAccount("0x...");
const mcNexus = await toMultichainNexusAccount(options):
const meeClient = await createMeeClient(options);
// 2. Session signer setup
const sessionSigner = privateKeyToAccount("0x...");
const sessionMcNexus = await toMultichainNexusAccount({
chainConfigurations: [
{
// ... other config
// User McNexus address needs to be overriden here
accountAddress: mcNexus.addressOn(chainId, true),
},
{
// ... other config
// User McNexus address needs to be overriden here
accountAddress: mcNexus.addressOn(chainId, true),
},
],
signer: sessionSigner,
});
const sessionMeeClient = await createMeeClient(options);
// 3. Build actions for permissions
const functionSignature = toFunctionSelector(
getAbiItem({ abi: CounterAbi, name: "incrementCount" })
);
const actions = mcNexus.buildSessionAction({
type: "custom",
data: {
chainIds: [8453, 10],
contractAddress: CounterContract,
functionSignature
}
});
// 4. Prepare and enable sessions
const quote = await meeClient.getSessionQuote({
mode: "PREPARE",
enableSession: {
redeemer: sessionSigner.address,
actions,
},
simulation: { simulate: true },
feeToken: { address: USDC, chainId: 8453 },
trigger: {
tokenAddress: USDC,
chainId: 8453,
amount: parseUnits("2", 6),
},
});
// Store the sessions details for later use.
let sessionDetails: SessionDetail[] = [];
if (quote) {
const { hash } = await meeClient.executeSessionQuote(quote);
await meeClient.waitForSupertransactionReceipt({ hash });
if (quote.sessionDetails) sessionDetails = quote.sessionDetails;
}
// 5. Execute transaction on behalf of user via session signer
const quote = await sessionMeeClient.getSessionQuote({
mode: "USE",
sessionDetails,
simulation: { simulate: true },
feeToken: { address: USDC, chainId: 8453 },
instructions: [
{
chainId: 8453,
calls: [{ to: CounterContract, data: "0x..." }]
}
]
});
const { hash } = await sessionMeeClient.executeSessionQuote(quote);
await sessionMeeClient.waitForSupertransactionReceipt({ hash });