Skip to main content
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
Metered access models
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:
  1. Agent’s usePermission() call reverts
  2. Error indicates limit exceeded
  3. 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