The Usage Limit Policy restricts how many times your agent can execute a specific action. Simple but effective rate limiting.
When to Use Usage Limit
Subscription agents (12 payments per year)
Trial periods (limited actions)
Preventing runaway agents
Safety cap for any automation
Basic Usage
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
redeemer: agentSigner.address,
feeToken: { address: USDC, chainId: base.id },
actions: [{
chainId: base.id,
actionTarget: CONTRACT,
actionTargetSelector: selector,
usageLimit: 100n, // Max 100 executions ever
actionPolicies: [getSudoPolicy()]
}]
});
Agent Examples
Subscription Agent (12/Year)
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
redeemer: agentSigner.address,
feeToken: { address: USDC, chainId: base.id },
// 1 year validity
sessionValidUntil: Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60,
actions: [{
chainId: base.id,
actionTarget: USDC,
actionTargetSelector: toFunctionSelector("transfer(address,uint256)"),
usageLimit: 12n, // 12 monthly payments
actionPolicies: [paymentPolicy]
}]
});
Trial Agent (10 Actions)
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
redeemer: agentSigner.address,
feeToken: { address: USDC, chainId: base.id },
// 7-day trial
sessionValidUntil: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60,
actions: [{
chainId: base.id,
actionTarget: SERVICE_CONTRACT,
actionTargetSelector: serviceSelector,
usageLimit: 10n, // Only 10 trial actions
actionPolicies: [getSudoPolicy()]
}]
});
Trading Agent (Daily Limit)
Reset-style limits aren’t natively supported, but you can:
// Option 1: Create new session daily (recommended for strict resets)
const createDailySession = async () => {
return await sessionClient.grantPermissionTypedDataSign({
sessionValidUntil: endOfDay(),
actions: [{
usageLimit: 50n, // 50 trades per day
actionPolicies: [tradingPolicy]
}]
});
};
// Option 2: Set total limit for period
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
sessionValidUntil: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60,
actions: [{
usageLimit: 350n, // ~50/day for 7 days
actionPolicies: [tradingPolicy]
}]
});
Rebalancing Agent (Quarterly)
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
redeemer: agentSigner.address,
feeToken: { address: USDC, chainId: base.id },
// 1 year
sessionValidUntil: Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60,
actions: [{
chainId: base.id,
actionTarget: REBALANCER,
actionTargetSelector: rebalanceSelector,
usageLimit: 4n, // 4 quarterly rebalances
actionPolicies: [getSudoPolicy()]
}]
});
Gaming Agent (Action Budget)
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
redeemer: agentSigner.address,
feeToken: { address: USDC, chainId: base.id },
// Weekly session
sessionValidUntil: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60,
actions: [
{
actionTarget: GAME,
actionTargetSelector: toFunctionSelector("attack(uint256)"),
usageLimit: 100n, // 100 attacks per week
actionPolicies: [getSudoPolicy()]
},
{
actionTarget: GAME,
actionTargetSelector: toFunctionSelector("heal()"),
usageLimit: 50n, // 50 heals per week
actionPolicies: [getSudoPolicy()]
},
{
actionTarget: GAME,
actionTargetSelector: toFunctionSelector("claimRewards()"),
usageLimit: 7n, // Daily claim
actionPolicies: [getSudoPolicy()]
}
]
});
Per-Action Limits
Each action can have its own limit:
actions: [
{
actionTargetSelector: toFunctionSelector("deposit(...)"),
usageLimit: 50n, // 50 deposits
actionPolicies: [getSudoPolicy()]
},
{
actionTargetSelector: toFunctionSelector("withdraw(...)"),
usageLimit: 50n, // 50 withdrawals (separate counter)
actionPolicies: [getSudoPolicy()]
}
]
Combining with Other Policies
Usage + Time (Most Common)
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
// 30-day window
sessionValidUntil: now + 30 * DAY,
actions: [{
usageLimit: 100n, // Max 100 in those 30 days
actionPolicies: [getSudoPolicy()]
}]
});
Usage + Universal Action
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
actions: [{
usageLimit: 50n, // 50 trades max
actionPolicies: [
getUniversalActionPolicy({
// Also limit per-trade and total spending
paramRules: { /* ... */ }
})
]
}]
});
Usage + Sudo (Safe Sudo)
const sessionDetails = await sessionClient.grantPermissionTypedDataSign({
sessionValidUntil: now + 7 * DAY, // Time limit
actions: [{
usageLimit: 20n, // Action limit
actionPolicies: [getSudoPolicy()] // Full access per action
}]
});
Tracking Usage
The usage counter is tracked on-chain. You can check remaining uses:
// Check if permission is still usable
const isEnabled = await sessionClient.isPermissionEnabled({
permissionId: sessionDetails.permissionId,
chainId: base.id
});
// The counter decrements with each successful use
// When it hits 0, further calls revert
When Limit is Reached
When usageLimit is exhausted:
- Agent’s
usePermission() call reverts
- Error indicates limit exceeded
- User must grant new permission to continue
try {
await agentClient.usePermission({ ... });
} catch (error) {
if (error.code === "LIMIT_EXCEEDED") {
// Notify user to renew permissions
await notifyUser("Your agent has used all allocated actions");
}
}
Common Patterns
Renewal Flow
When limits are reached, prompt for renewal:
async function executeWithRenewal(userId, action) {
try {
return await executeAgentAction(userId, action);
} catch (error) {
if (error.code === "LIMIT_EXCEEDED") {
// Request new permission from user
await requestPermissionRenewal(userId);
// After user approves, retry
return await executeAgentAction(userId, action);
}
throw error;
}
}
Metered Access
Sell action packs:
const PACK_SIZES = {
starter: 10n,
pro: 100n,
unlimited: 10000n // Effectively unlimited
};
async function createPackSession(userId, pack) {
return await sessionClient.grantPermissionTypedDataSign({
actions: [{
usageLimit: PACK_SIZES[pack],
actionPolicies: [agentPolicy]
}]
});
}
Limits
usageLimit is a bigint
- Maximum value:
2^256 - 1 (effectively unlimited)
- Counter decrements atomically per successful execution
- Failed executions don’t count against limit