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

# Contract argument limits

> Compare decoded unsigned ABI arguments and scope per-call limits to the exact contract, function and asset.

Use an unsigned argument condition to constrain a decoded number inside an EVM contract call. This guide shows how to identify the exact call, choose its amount argument, and limit the Aave `supply` and Morpho `vaultBundlesV1Deposit` calls used by Para's yield builders with complete policy JSON.

The same rules apply when the signing request comes through REST or an SDK. They compare the value declared in the call, not a simulated or executed balance change, and do not add a daily spending budget.

## Choose the fact and units

`request.uintArgs[n]` exposes a decoded top-level `uint8` through `uint256` argument, indexed from zero. Its value is a decimal digit string; use `eq`, `ne`, `lt`, `lte`, `gt` or `gte`. Para compares the integers without converting token decimals, shares or prices.

For a six-decimal USDC amount, `"100000000"` means 100 USDC. A numeric argument may instead represent shares, a deadline, an identifier or a price bound: confirm the function's meaning before treating it as money.

| Existing operation | Fact to constrain | Meaning |
| - | - | - |
| Native transfer, or native value attached to an EVM contract call | `request.value.baseUnits` | Declared value in the chain's native base units, such as wei on EVM. Does not include gas. |
| Supported token transfers, and EVM token approvals | `request.token.value.baseUnits`, with `request.token.asset` | Existing declared transfer amount or approval allowance. An approval is not a deposit. See the [wallet-type fact tables](/v3/references/permissions/facts.md) for supported actions. |
| Readable Safe native or token payments | `request.safe.value.baseUnits` or `request.safe.token.value.baseUnits`, with the corresponding asset fact | Existing amount inside a [readable Safe payment](/v3/references/permissions/facts.md#safe-payments), distinct from the outer call's value. |
| NFT calls, such as ERC-721 `safeTransferFrom(address,address,uint256)` | `request.uintArgs[2]` | The token ID, not a fungible amount or number of NFTs. |
| Aave `supply(address,uint256,address,uint16)` | `request.uintArgs[1]` | USDC amount in six-decimal base units. Bind the asset in `request.args[0]` and beneficiary in `request.args[2]`. |
| Morpho `vaultBundlesV1Deposit` | `request.uintArgs[1]` | Gross USDC amount in six-decimal base units. Bind the selected USDC vault in `request.args[0]` and the official wrapper as target. |
| Fixed Aave `withdraw(address,uint256,address)` | `request.uintArgs[1]` | Requested USDC amount, with asset and recipient bound separately. |
| Fixed Morpho `withdraw(uint256,address,address)` | `request.uintArgs[0]` | Requested USDC amount, with receiver and owner bound separately. |
| Aave withdrawal with `amount: "all"` | `request.uintArgs[1]` | The maximum `uint256` sentinel, not the position's USDC value. A finite amount cap rejects it. |
| Morpho withdrawal with `amount: "all"` | `request.uintArgs[0]` on `redeem(uint256,address,address)` | Captured share amount, not a USDC payout. A share limit does not establish a USDC withdrawal cap. |
| Other reliably decoded EVM contract calls | `request.uintArgs[n]` | The selected top-level unsigned integer, interpreted using that exact function's ABI and semantics. |

Signed integers, numeric strings, bytes, array elements and tuple members do not expose `request.uintArgs[n]`. A top-level unsigned argument can still be read when another argument is a tuple. Existing `request.args[n]` stays a string fact with `eq`, `ne` and `in`.

Para classifies the three-argument ERC-721 `safeTransferFrom` as `transfer_nfts`. Plain ERC-721 `transferFrom(address,address,uint256)` shares its selector with ERC-20 `transferFrom` and is currently classified as `transfer_tokens`; its `request.uintArgs[2]` still represents a token ID. Bind the exact contract and function instead of assuming the action classification makes an argument a fungible amount.

On a decoded Safe `execTransaction`, `request.uintArgs[n]` describes the outer function's arguments, not inferred spending inside its calldata. Use the existing `request.safe.*` facts for a readable inner payment; the unsigned argument facts do not traverse nested calls.

## Bind the whole call

Use `selector.chainId`, `request.target` and `request.abi.selector` together. The selector identifies the exact function signature, including argument types, rather than just its name. Then bind the asset or trusted asset-specific vault, beneficiary where the call takes one, and any native value attached to the call.

Values for `request.abi.selector` must be `0x` followed by exactly eight hex characters. Selector comparisons ignore hex letter case; malformed values fail policy validation.

The Guardrails below target an exact contract and function, then deny `not(all expected deposit conditions)`. Both policies can apply to the same wallet.

> [!WARNING]
>
> These limits apply to decoded deposit arguments. A standard rule that cannot be judged solely because of a decode gap is skipped, including a Guardrail `deny`. Other rules and the Para baseline decide, so an app-owned wallet may sign and a user-owned wallet may need review.
>
> These rules do not constrain Aave `supplyWithPermit` or direct Morpho vault `deposit`. Those calls follow their other applicable policies and the Para baseline; a broad contract-call allow can permit them. Other functions and approvals also retain their existing rules. These policies are not contract allowlists or cumulative spending limits, and do not cap gas costs or revoke existing allowances.

See [Facts Para cannot read](/v3/references/permissions/policy-json.md#facts-para-cannot-read) for which failures qualify for decode-gap fallback.

## Aave: limit decoded supply calls to 100 USDC

For decoded Aave V3 `supply` calls to the configured Base pool, this policy requires native USDC, no attached ETH and the signing wallet as beneficiary. The function selector is `0x617ba037`; argument 1 is the amount, argument 0 the asset and argument 2 the beneficiary.

Replace `YOUR_WALLET_ID` with the wallet's Para ID and `0x1111111111111111111111111111111111111111` with that same wallet's address. The deposit cap applies whether the flow first needs an approval or reuses an existing allowance. Configure approval controls separately with the existing token facts.

```json aave-deposit-limit.json theme={null}
{
  "id": "base-aave-deposit-limit",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM",
    "chainId": "8453",
    "walletId": "YOUR_WALLET_ID"
  },
  "rules": [
    {
      "id": "deny-out-of-policy-deposit",
      "effect": "deny",
      "actions": [
        "call_contract"
      ],
      "when": {
        "kind": "all",
        "of": [
          { "kind": "compare", "fact": "request.target", "op": "eq", "value": "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5" },
          { "kind": "compare", "fact": "request.abi.selector", "op": "eq", "value": "0x617ba037" },
          {
            "kind": "not",
            "of": {
              "kind": "all",
              "of": [
                { "kind": "compare", "fact": "request.args[0]", "op": "eq", "value": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
                { "kind": "compare", "fact": "request.args[2]", "op": "eq", "value": "0x1111111111111111111111111111111111111111" },
                { "kind": "compare", "fact": "request.uintArgs[1]", "op": "lte", "value": "100000000" },
                { "kind": "compare", "fact": "request.value.baseUnits", "op": "eq", "value": "0" }
              ]
            }
          }
        ]
      }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Limit decoded Aave supply calls on Base to 100 USDC per call"
  }
}
```

## Morpho: limit decoded vaultBundlesV1Deposit calls to 100 USDC

For decoded `vaultBundlesV1Deposit(address,uint256,uint256,(uint8,bytes),uint256,address,uint256)` calls to the official Base wrapper, this policy requires the supported Steakhouse Prime V2 USDC vault. The selector is `0x6bbba4e0`; argument 1 is the gross asset amount, not the share-price bound in argument 2. The zero referral-fee condition keeps that gross amount equal to the assets deposited.

The [official wrapper deposits shares for its caller](https://github.com/morpho-org/bundles/blob/main/src/vault/VaultBundlesV1.sol), so there is no beneficiary argument. The wallet ID scopes this direct call to the signing wallet. USDC is bound through the fixed supported vault, whose underlying asset is USDC, rather than through `request.token.asset`, which is unavailable on generic contract calls.

Replace `YOUR_WALLET_ID` with the wallet's Para ID. Approvals target the wrapper, not the vault; fixed withdrawals and full redemptions target the vault instead and retain their existing policy behavior.

```json morpho-deposit-limit.json theme={null}
{
  "id": "base-morpho-deposit-limit",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM",
    "chainId": "8453",
    "walletId": "YOUR_WALLET_ID"
  },
  "rules": [
    {
      "id": "deny-out-of-policy-deposit",
      "effect": "deny",
      "actions": [
        "call_contract"
      ],
      "when": {
        "kind": "all",
        "of": [
          { "kind": "compare", "fact": "request.target", "op": "eq", "value": "0x2B08A911f48dE25A7e305D910Afb5597aBE8ea7B" },
          { "kind": "compare", "fact": "request.abi.selector", "op": "eq", "value": "0x6bbba4e0" },
          {
            "kind": "not",
            "of": {
              "kind": "all",
              "of": [
                { "kind": "compare", "fact": "request.args[0]", "op": "eq", "value": "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9" },
                { "kind": "compare", "fact": "request.uintArgs[1]", "op": "lte", "value": "100000000" },
                { "kind": "compare", "fact": "request.uintArgs[4]", "op": "eq", "value": "0" },
                { "kind": "compare", "fact": "request.value.baseUnits", "op": "eq", "value": "0" }
              ]
            }
          }
        ]
      }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Limit decoded Morpho vaultBundlesV1Deposit calls on Base to 100 USDC per call"
  }
}
```

## Validate and test the policy

Use the existing [JSON authoring flow](/v3/rest/permissions/json.md) to validate and save a policy. For each provider, test decoded deposits at the limit and above it, plus the wrong asset or vault and unexpected native value. For Aave, also change the beneficiary; for Morpho, change the referral fee. Test approval and deposit separately, including a deposit that reuses an existing allowance.

Activate both limits together in your test policy set. Confirm that both providers' decoded deposits are constrained, while another contract or function follows its existing rules. For explorer-backed contract rules, test a genuinely missing ABI, such as an unverified contract, separately from [temporary ABI lookup failures](/v3/references/permissions/policy-json.md#facts-para-cannot-read). A genuine decode gap can skip the numeric rule; other rules and the Para baseline decide. Test malformed transactions separately too.

A limit scoped to one exact function does not cover alternate functions, nested calls or other ways of moving the same asset. Review the complete policy set when your application needs a wider boundary.

Two deposits of 60 USDC each satisfy a 100-USDC per-call cap. These argument facts do not meter deposits across requests and do not turn a captured share amount or a withdraw-all sentinel into a USDC value.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.