> ## Documentation Index
> Fetch the complete documentation index at: https://docs.biconomy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Universal Action Policy

> Fine-grained parameter-level control for DeFi agents

The Universal Action Policy provides granular control over function parameters. Use it when your agent handles funds and you need spending limits, recipient whitelisting, or parameter validation.

## When to Use Universal Action

<Check>Agent handles user funds</Check>
<Check>Need per-action spending limits</Check>
<Check>Need cumulative spending caps</Check>
<Check>Want to restrict recipients</Check>
<Check>Need parameter validation</Check>

## Core Concepts

### Parameter Rules

Each rule validates one parameter in the function call and maximum of 16 rules can be added per policy.

```typescript theme={null}
const universalPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    {
      condition: "equal", // Comparison type
      calldataOffset: calldataArgument(1), // Argument position
      comparisonValue: 10n || "0x..", // Value to compare against
      isLimited: true, // Track cumulative usage
      usage: { limit: parseUnits("5000", 6), used: 0n }  // Cumulative limit
    }
  ],
  // Configure spending limit for native token
  valueLimitPerUse: 1n
});
```

### Conditions

| Condition            | Meaning            | Use Case                |
| -------------------- | ------------------ | ----------------------- |
| `equal`              | Must equal ref     | Whitelist addresses     |
| `lessThan`           | Must be \< ref     | Amount caps             |
| `lessThanOrEqual`    | Must be ≤ ref      | Amount caps (inclusive) |
| `greaterThan`        | Must be > ref      | Minimum amounts         |
| `greaterThanOrEqual` | Must be ≥ ref      | Minimum amounts         |
| `notEqual`           | Must not equal ref | Blacklist               |

## Trading Agent Example

Limit per-trade and max spending limit per session:

```typescript theme={null}
const UNISWAP_ROUTER = "0x2626664c2603336E57B271c5C0b26F421741e481";

const tradingPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    // Rule 1: Recipient must be user's account
    {
      condition: "equal",
      calldataOffset: calldataArgument(4),
      comparisonValue: userAccountAddress,
    },
    // Rule 2: Max $500 per trade, $5000 cumulative
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(5),
      comparisonValue: parseUnits("500", 6),
      isLimited: true,  // Track cumulative
      usage: { 
        limit: parseUnits("5000", 6),  // $5000 total limit
        used: 0n 
      }
    }
  ],
  valueLimitPerUse: 0n,  // No ETH value
});

const now = Math.floor(Date.now() / 1000)
const validAfter = now
const validUntil = now + 7 * 24 * 60 * 60

const timeframePolicy = mcNexus.buildActionPolicy({
  type: "timeframe",
  validAfter, // unix-timestamp when policy becomes active
  validUntil  // unix-timestamp when policy expires
});

const UNISWAP_ROUTER = "0x2626664c2603336E57B271c5C0b26F421741e481";

const customActions = mcNexus.buildSessionAction({
  type: "custom",
  data: {
    chainIds: [8453, 10],
    contractAddress: UNISWAP_ROUTER,
    functionSignature: '0x...',
    policies: [tradingPolicy, timeframePolicy]
  }
});

// Finally get the session quote and execute the quote to enable permissions
```

## Payment Agent Example

Fixed recipient and amount:

```typescript theme={null}
const MERCHANT = "0x1234...";
const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";

const paymentPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    // Only send to merchant
    {
      condition: "equal",
      calldataOffset: calldataArgument(1),
      comparisonValue: MERCHANT
    },
    // Max 100 USDC per payment
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("100", 6),
    }
  ],
  // Configure spending limit for native token
  valueLimitPerUse: 0n,
});

const usagePolicy = mcNexus.buildActionPolicy({
  type: "usageLimit",
  limit: 12n,  // 12 payments max
});

const now = Math.floor(Date.now() / 1000)
const validAfter = now
const validUntil = now + 365 * 24 * 60 * 60

const timeframePolicy = mcNexus.buildActionPolicy({
  type: "timeframe",
  validAfter, // unix-timestamp when policy becomes active
  validUntil  // unix-timestamp when policy expires
});

// Finally use these policies in the action and enable permission for agents.
const policies = [paymentPolicy, usageLimit, timeframePolicy];
```

## Rebalancing Agent Example

Limit rebalance size to % of portfolio:

```typescript theme={null}
const rebalancePolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    // Max 10% of portfolio per rebalance ($10,000 cap)
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(4),
      comparisonValue: parseUnits("10000", 6), // $10k
      isLimited: true,
      usage: { 
        limit: parseUnits("50000", 6),  // $50k total per period
        used: 0n 
      }
    }
  ],
  valueLimitPerUse: 0n,
});
```

## Per-Action vs Cumulative Limits

### Per-Action Only

Each individual action must be ≤ limit, but no cumulative tracking:

```typescript theme={null}
const universalPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("500", 6), // Max $500 per trade
      // No cumulative tracking
    }
  ]
});
```

### Cumulative Tracking

Track total spent across all actions:

```typescript theme={null}
const universalPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("500", 6), // Max $500 per trade
      isLimited: true,  // Track cumulative
      usage: { 
        limit: parseUnits("5000", 6),  // $5000 total
        used: 0n 
      }
    }
  ]
});
```

## Multiple Rules Example

Validate multiple parameters:

```typescript theme={null}
// For transfer(address recipient, uint256 amount)
const universalPolicyOne = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    // Rule 1: Recipient in whitelist (address A)
    {
      condition: "equal",
      calldataOffset: calldataArgument(1),
      comparisonValue: ALLOWED_RECIPIENT_A
    },
    // Rule 2: Amount limit
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("1000", 6),
      isLimited: true,
      usage: { limit: parseUnits("10000", 6), used: 0n }
    }
  ]
});

const universalPolicyTwo = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    // Rule 1: recipient B (rules are OR'd for same offset)
    {
      condition: "equal",
      calldataOffset: calldataArgument(1),
      comparisonValue: ALLOWED_RECIPIENT_B
    },
    // Rule 3: Amount limit
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("1000", 6),
      isLimited: true,
      usage: { limit: parseUnits("10000", 6), used: 0n }
    }
  ]
});

// This allows the recipient to be either A or B
const policies = [universalPolicyOne, universalPolicyTwo];
```

## Finding Parameter Positions:

For a function like `swap(address tokenIn, address tokenOut, uint256 amount)`:

```
calldataArgument(1):  tokenIn (address)
calldataArgument(2):  tokenOut (address)  
calldataArgument(3):  amount (uint256)
```

For structs, calldataArguments are more complex. Check the ABI encoding.

## Common Patterns

### Whitelist Single Recipient

```typescript theme={null}
const universalPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    {
      condition: "equal",
      calldataOffset: calldataArgument(1),
      comparisonValue: ALLOWED_ADDRESS
    },
  ]
});
```

### Cap Per Transaction Amount

```typescript theme={null}
const universalPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("500", 6)
    },
  ]
});
```

### Total Max Spending Limit

```typescript theme={null}
const universalPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    {
      condition: "lessThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("500", 6), // Per-tx max
      isLimited: true,
      usage: { 
        limit: parseUnits("5000", 6),  // Cumulative max
        used: 0n 
      }
    },
  ]
});
```

### Minimum Amount

```typescript theme={null}
const universalPolicy = mcNexus.buildActionPolicy({
  type: "universal",
  rules: [
    {
      condition: "greaterThanOrEqual",
      calldataOffset: calldataArgument(2),
      comparisonValue: parseUnits("10", 6), // At least $10
    },
  ]
});
```

## Debugging Tips

1. **Check offsets**: Use `console.log(encodeFunctionData(...))` to see actual calldata
2. **Match decimals**: USDC = 6, ETH = 18, etc.
3. **Test on testnet**: Verify rules work before mainnet
