> ## 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.

# Recipient and contract allowlists

> Limit app-owned wallets to known recipients, contracts and functions.

export const PermissionsCards = ({basePath, pages, cols = 2, horizontal = false}) => {
  const ImageCard = Card;
  const hrefOverrides = {
    "/v3/react/guides/permissions": {
      "conditional-approvals": "/v3/react/guides/conditional-approvals"
    },
    "/v3/rest/permissions": {
      "backend": "/v3/rest/permissions-administration"
    }
  };
  const guardrailBases = ["/v3/rest/permissions", "/v3/server/guides/permissions"];
  const ownership = guardrailBases.includes(basePath) ? 'guardrail' : 'request';
  const overviewImages = {
    "/v3/rest/permissions": {
      "imgUrl": "/images/v3/cards/rest-guardrails.png",
      "horizontalImgUrl": "/images/v3/cards/rest-guardrails-horizontal.png"
    },
    "/v3/server/guides/permissions": {
      "imgUrl": "/images/v3/cards/sdk-server-guardrail-signing.png",
      "horizontalImgUrl": "/images/v3/cards/sdk-server-guardrail-signing-horizontal.png"
    },
    "/v3/react/guides/permissions": {
      "imgUrl": "/images/v3/cards/sdk-get-policy-consent-url.png",
      "horizontalImgUrl": "/images/v3/cards/sdk-get-policy-consent-url-horizontal.png"
    },
    "/v3/react-native/guides/permissions": {
      "imgUrl": "/images/v3/cards/sdk-get-policy-consent-url.png",
      "horizontalImgUrl": "/images/v3/cards/sdk-get-policy-consent-url-horizontal.png"
    }
  };
  const registry = {
    overview: {
      title: "Permissions overview",
      description: "Return to the start of the Permissions path.",
      ...overviewImages[basePath]
    },
    "developer-portal": {
      title: "Create policies in the Developer Portal",
      description: "Build a policy with the Portal forms and activate it on a beta key.",
      imgUrl: "/images/v3/cards/developer-portal-policy-form.png",
      horizontalImgUrl: "/images/v3/cards/developer-portal-policy-form-horizontal.png"
    },
    "json": {
      title: "Write policy JSON",
      description: "Move to JSON for policies the forms cannot express.",
      imgUrl: "/images/v3/cards/developer-portal-policy-json.png",
      horizontalImgUrl: "/images/v3/cards/developer-portal-policy-json-horizontal.png"
    },
    "testing": {
      title: "Test your policies",
      description: "Exercise every rule on beta before you ship.",
      imgUrl: "/images/v3/cards/concept-policy-testing.png",
      horizontalImgUrl: "/images/v3/cards/concept-policy-testing-horizontal.png"
    },
    "production": {
      title: "Go to production",
      description: "Request a production review and publish your policy.",
      imgUrl: "/images/v3/cards/concept-policy-publication.png",
      horizontalImgUrl: "/images/v3/cards/concept-policy-publication-horizontal.png"
    },
    "conditional-approvals": {
      title: "Conditional approvals",
      description: "Require people to approve a request before it signs.",
      imgUrl: "/images/v3/cards/portal-conditional-approval-case.png",
      horizontalImgUrl: "/images/v3/cards/portal-conditional-approval-case-horizontal.png"
    },
    "approval-signatures": {
      title: "Collect approval signatures",
      description: "Collect signatures from approvers for multisig workflows.",
      imgUrl: "/images/v3/cards/sdk-get-approval-signatures.png",
      horizontalImgUrl: "/images/v3/cards/sdk-get-approval-signatures-horizontal.png"
    },
    "adjustable-parameters": {
      title: "Adjustable parameters",
      description: "Let users set their own limits and managers adjust approval settings.",
      imgUrl: "/images/v3/cards/portal-user-policy-limits.png",
      horizontalImgUrl: "/images/v3/cards/portal-user-policy-limits-horizontal.png"
    },
    "lifecycle": {
      title: "Manage the policy lifecycle",
      description: "Version, activate, replace and retire policies.",
      imgUrl: "/images/v3/cards/developer-portal-policy-versions.png",
      horizontalImgUrl: "/images/v3/cards/developer-portal-policy-versions-horizontal.png"
    },
    "backend": {
      title: "Manage permissions from your backend",
      description: "Assign approvers and read policy state with your server secret.",
      imgUrl: "/images/v3/cards/rest-permissions-administration.png",
      horizontalImgUrl: "/images/v3/cards/rest-permissions-administration-horizontal.png"
    },
    templates: ownership === "guardrail" ? {
      title: "Guardrail templates",
      description: "Start from a tested Guardrail policy.",
      imgUrl: "/images/v3/cards/concept-guardrail-templates.png",
      horizontalImgUrl: "/images/v3/cards/concept-guardrail-templates-horizontal.png"
    } : {
      title: "Request templates",
      description: "Start from a tested Request policy.",
      imgUrl: "/images/v3/cards/concept-request-templates.png",
      horizontalImgUrl: "/images/v3/cards/concept-request-templates-horizontal.png"
    },
    "guardrail:templates/spending-caps": {
      title: "Spending caps",
      description: "Cap native and token transfers per transaction or per day.",
      imgUrl: "/images/v3/cards/concept-spending-limits.png",
      horizontalImgUrl: "/images/v3/cards/concept-spending-limits-horizontal.png"
    },
    "guardrail:templates/allowlists": {
      title: "Recipient and contract allowlists",
      description: "Limit transfers and contract calls to addresses you know.",
      imgUrl: "/images/v3/cards/concept-recipient-allowlists.png",
      horizontalImgUrl: "/images/v3/cards/concept-recipient-allowlists-horizontal.png"
    },
    "guardrail:templates/approval-tiers": {
      title: "Approval thresholds and tiers",
      description: "Hold large transfers for approval, or deny the largest.",
      imgUrl: "/images/v3/cards/concept-approval-tiers.png",
      horizontalImgUrl: "/images/v3/cards/concept-approval-tiers-horizontal.png"
    },
    "guardrail:templates/treasury": {
      title: "Treasury approvals",
      description: "Deny unplanned actions and approve wallet or Safe payouts in stages.",
      imgUrl: "/images/v3/cards/concept-treasury-approvals.png",
      horizontalImgUrl: "/images/v3/cards/concept-treasury-approvals-horizontal.png"
    },
    "guardrail:templates/message-signing": {
      title: "Message and raw signing",
      description: "Block or hold raw signatures, and block permits.",
      imgUrl: "/images/v3/cards/concept-signing-actions.png",
      horizontalImgUrl: "/images/v3/cards/concept-signing-actions-horizontal.png"
    },
    "request:templates/allowances": {
      title: "Transfer allowances",
      description: "Let small transfers and a daily budget sign without a prompt.",
      imgUrl: "/images/v3/cards/concept-spending-limits.png",
      horizontalImgUrl: "/images/v3/cards/concept-spending-limits-horizontal.png"
    },
    "request:templates/recipients-and-contracts": {
      title: "Known recipients and app contracts",
      description: "Cover transfers to known addresses and your contract's calls.",
      imgUrl: "/images/v3/cards/concept-recipient-allowlists.png",
      horizontalImgUrl: "/images/v3/cards/concept-recipient-allowlists-horizontal.png"
    },
    "request:templates/cosigner-approval": {
      title: "Co-signer approval",
      description: "Send large transfers to a co-signer for approval.",
      imgUrl: "/images/v3/cards/portal-conditional-approval-case.png",
      horizontalImgUrl: "/images/v3/cards/portal-conditional-approval-case-horizontal.png"
    },
    "request:templates/adjustable-limits": {
      title: "User-adjustable limits",
      description: "Let each user choose their own spending limits.",
      imgUrl: "/images/v3/cards/portal-user-policy-limits.png",
      horizontalImgUrl: "/images/v3/cards/portal-user-policy-limits-horizontal.png"
    },
    "request:templates/message-signing": {
      title: "Message signing",
      description: "Sign messages from users' wallets without a prompt.",
      imgUrl: "/images/v3/cards/concept-signing-actions.png",
      horizontalImgUrl: "/images/v3/cards/concept-signing-actions-horizontal.png"
    }
  };
  const entryFor = slug => registry[slug] || registry[`${ownership}:${slug}`];
  const hrefFor = slug => {
    if (slug === 'overview') return basePath;
    const overrides = hrefOverrides[basePath] || ({});
    return overrides[slug] || `${basePath}/${slug}`;
  };
  return <CardGroup cols={cols}>
      {pages.map(slug => {
    const entry = entryFor(slug);
    return <ImageCard key={slug} title={entry.title} description={entry.description} href={hrefFor(slug)} imgUrl={horizontal ? entry.horizontalImgUrl : entry.imgUrl} horizontal={horizontal} />;
  })}
    </CardGroup>;
};

These templates keep an app-owned wallet to addresses you already know: the recipients it pays and the contracts it calls. Replace the placeholder addresses with your own before you publish.

## Allow only known recipients

Use this when the wallet should only pay addresses you control or have vetted. The policy denies an ETH transfer on Base to any address outside the list.

<Tabs>
  <Tab title="Developer Portal">
    1. On your **Development** key, open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**. In **Networks**, clear **Ethereum (1)**, select **Base (8453)**, and select **Continue**.
    3. Select **Transfer ETH**, then **Add Condition**. Keep **Recipient address**, choose **is one of**, and enter the addresses separated by commas.
    4. Select **Review**, then **Save Draft**, then **Activate** on the draft.
  </Tab>

  <Tab title="JSON">
    ```json guardrail-allowed-recipients.json theme={null}
    {
      "id": "base-allowed-recipients",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "allowed-recipients",
          "effect": "deny",
          "actions": ["transfer_native"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
              {
                "kind": "not",
                "of": {
                  "kind": "compare",
                  "fact": "request.to",
                  "op": "in",
                  "value": [
                    "0x1111111111111111111111111111111111111111",
                    "0x2222222222222222222222222222222222222222"
                  ]
                }
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Send ETH on Base only to approved recipients"
      }
    }
    ```
  </Tab>
</Tabs>

| Signing request | Result |
| - | - |
| Send ETH to `0x1111…1111` | Signs |
| Send ETH to an address outside the list | Denied by `allowed-recipients` |
| Token transfers and contract calls | Not limited by this policy |

<Check>
  Test it on your Development key: sign a transfer to a listed address and one to an unlisted address. Only the second is denied.
</Check>

The rule covers native ETH transfers only. On Solana, Cosmos, Stellar or Sui, choose the **Native transfer** action in the form and add the same **Recipient address** condition with addresses in that network's format.

## Allow one function on one contract

Use this when the wallet should only ever call a single protocol function, such as a deposit. The policy denies a contract call on Base unless it calls `deposit` on the listed contract.

<Tabs>
  <Tab title="Developer Portal">
    1. On your **Development** key, open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**. In **Networks**, clear **Ethereum (1)**, select **Base (8453)**, and select **Continue**.
    3. Select **Call a contract**. Enter the contract in **Contract Address** and `deposit` in **Function Name**. No condition is needed.
    4. Select **Review**. The Review step warns that no conditions are set, but the contract and function still limit the rule, as the **Normalized policy JSON** shows. Select **Save Draft**, then **Activate** on the draft.
  </Tab>

  <Tab title="JSON">
    ```json guardrail-allowed-contracts.json theme={null}
    {
      "id": "base-allowed-contract",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "allowed-contract-call",
          "effect": "deny",
          "actions": ["call_contract"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "decoded_contract_call" },
              {
                "kind": "any",
                "of": [
                  {
                    "kind": "compare",
                    "fact": "request.contract.address",
                    "op": "ne",
                    "value": "0x3333333333333333333333333333333333333333"
                  },
                  { "kind": "compare", "fact": "request.method", "op": "ne", "value": "deposit" }
                ]
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Call only deposit on the approved Base contract"
      }
    }
    ```
  </Tab>
</Tabs>

| Signing request | Result |
| - | - |
| Call `deposit` on `0x3333…3333` | Signs |
| Call another function, or another contract, that Para can decode | Denied by `allowed-contract-call` |
| A call Para cannot decode | Not limited by this policy |

<Warning>
  Para reads the contract and function only from calls it can decode with the contract's ABI. The rule starts with a `request.classification` check, so a call Para cannot decode does not match it and stays allowed. To deny those calls too, use the next template.
</Warning>

<Check>
  Test it on your Development key: sign a `deposit` call on the listed contract, then a call to another function. Only the second is denied.
</Check>

<Warning>
  Adding a second **Call a contract** action in the form does not widen the list. Each action becomes its own rule that denies every call outside its own contract and function, so two actions together deny every decoded call. To list several contracts or functions, use the next template.
</Warning>

## Allow listed contracts and functions

Use this when the wallet works with a few contracts and calls Para cannot decode must not slip through. The Developer Portal forms cannot express it, so publish it as JSON.

```json guardrail-contract-allowlist.json theme={null}
{
  "id": "base-contract-allowlist",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM",
    "chainId": "8453"
  },
  "rules": [
    {
      "id": "listed-contracts-only",
      "effect": "deny",
      "actions": ["call_contract"],
      "when": {
        "kind": "not",
        "of": {
          "kind": "compare",
          "fact": "request.target",
          "op": "in",
          "value": ["0x3333333333333333333333333333333333333333", "0x4444444444444444444444444444444444444444"]
        }
      }
    },
    {
      "id": "listed-functions-only",
      "effect": "deny",
      "actions": ["call_contract"],
      "when": {
        "kind": "all",
        "of": [
          { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "decoded_contract_call" },
          {
            "kind": "not",
            "of": { "kind": "compare", "fact": "request.method", "op": "in", "value": ["deposit", "withdraw"] }
          }
        ]
      }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Call only listed contracts and functions on Base"
  }
}
```

The policy has two rules:

* `listed-contracts-only` checks `request.target`, the contract the transaction calls. Para reads it from every contract call, decoded or not, so a call to any unlisted contract is denied.
* `listed-functions-only` checks the function on calls Para can decode, and denies anything other than `deposit` or `withdraw`.

| Signing request | Result |
| - | - |
| Call `deposit` or `withdraw` on a listed contract | Signs |
| Call any function on an unlisted contract, decoded or not | Denied by `listed-contracts-only` |
| Call another function that Para can decode on a listed contract | Denied by `listed-functions-only` |
| A call to a listed contract that Para cannot decode | Signs |

<Check>
  Test it on your Development key: sign a call to a listed contract, a call to an unlisted contract, and a call to another function on a listed contract. Only the first signs.
</Check>

Contract calls are the `call_contract` action. ERC-20 transfers and approvals, NFT transfers, Safe transactions and deployments are separate actions, so this policy does not limit them. On Solana, Cosmos, Stellar or Sui, the form's **Execution target** action pins the program or contract a transaction calls. A Solana transaction that creates the recipient's associated token account and then sends one `TransferChecked` to it is a `transfer_tokens` request, so this policy does not limit it either.

## Publish your allowlists

Test recipients and contracts are often not the ones you use in production. Para reviews the policies active on your Development key and applies them to production, so make sure the lists hold your production addresses before you request a production review.

<PermissionsCards basePath="/v3/server/guides/permissions" pages={["templates/spending-caps", "testing"]} cols={2} />


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