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

# Template variables

> Compare a policy condition against the address of the wallet signing the request.

# Overview

Template variables let a policy condition compare against a value that Privy resolves per request. The
policy does not fix that value when it is written. A condition holds a placeholder such as
`{{wallet.address}}`, and Privy substitutes the value each time it evaluates the condition.

Together, template variables let one set of rules adapt to each request, instead of a separate rule per
case.

This approach offers several benefits:

* **Reusability**: Encode a rule once and reuse the same policy wherever it applies
* **Maintainability**: Keep rules stable as the set of resources a policy governs changes
* **Scalability**: Cover any number of cases with one rule instead of one rule per case
* **Safety**: Privy resolves the value from the request context, so a caller cannot supply its own

## Concepts

A **template variable** is a placeholder, written as `{{namespace.field}}`, that a condition value
holds in place of a literal. Before evaluating the condition, the policy engine replaces it with a
secure value from the request context. The value a rule compares against can therefore differ from one
request to the next.

Privy supports one template variable today:

* **`{{wallet.address}}`** resolves to the address of the wallet signing the request. Privy resolves it
  per request, so one policy can hold a rule about each wallet's own address.

## Where it can be used

`{{wallet.address}}` can be used anywhere a condition compares an address, and inside the content of a
message. These operators accept it:

| Condition compares | Operators                                          | Notes                                                                                            |
| ------------------ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| An address         | `eq`, `in`                                         | The variable must be the entire value. With `in`, it can share the array with literal addresses. |
| Message content    | `eq`, `contains`, `starts_with`, `ends_with`, `in` | The variable can sit inside surrounding text.                                                    |

The `in_condition_set` operator does not accept a template variable, because its value names a
condition set rather than an address.

<Info>
  Aggregation conditions do not accept a template variable yet. See [stateful
  policies](/controls/policies/stateful-policies) for what aggregations support.
</Info>

## Examples

### Restrict an ERC-20 transfer recipient to the signing wallet

The recipient argument must decode to the signing wallet's own address, which keeps funds from
leaving the wallet that holds them.

```ts {skip-check} theme={"system"}
{
    version: '1.0',
    name: 'Only transfer to self',
    chain_type: 'ethereum',
    rules: [
        {
            name: 'Allow ERC20 transfers back to the signing wallet',
            method: 'eth_sendTransaction',
            action: 'ALLOW',
            conditions: [
                {
                    field_source: 'ethereum_calldata',
                    field: 'transfer.recipient',
                    abi: [{
                        "name": "transfer",
                        "type": "function",
                        "inputs": [
                            {"name": "recipient", "type": "address"},
                            {"name": "amount", "type": "uint256"}
                        ]
                    }],
                    operator: 'eq',
                    value: '{{wallet.address}}'
                }
            ]
        }
    ]
}
```

### Allow the signing wallet alongside fixed addresses

The `in` operator accepts template variables and literal addresses in the same array. This rule
allows a transaction to the signing wallet or to a treasury address.

```ts {skip-check} theme={"system"}
{
    version: '1.0',
    name: 'Transfer to self or treasury',
    chain_type: 'ethereum',
    rules: [
        {
            name: 'Allow the signing wallet or the treasury',
            method: 'eth_sendTransaction',
            action: 'ALLOW',
            conditions: [
                {
                    field_source: 'ethereum_transaction',
                    field: 'to',
                    operator: 'in',
                    value: [
                        '{{wallet.address}}',
                        '<insert-treasury-address>'
                    ]
                }
            ]
        }
    ]
}
```

### Restrict a transfer action recipient to the signing wallet

The [transfer](/wallets/actions/transfer/policies) wallet action evaluates against the request body,
so the same constraint applies to `destination.address`.

```ts {skip-check} theme={"system"}
{
    version: '1.0',
    name: 'Transfer action to self or treasury',
    chain_type: 'ethereum',
    rules: [
        {
            name: 'Allow transfers to the wallet or the treasury',
            method: 'transfer',
            action: 'ALLOW',
            conditions: [
                {
                    field_source: 'action_request_body',
                    field: 'destination.address',
                    operator: 'in',
                    value: [
                        '{{wallet.address}}',
                        '<insert-treasury-address>'
                    ]
                },
                {
                    field_source: 'action_request_body',
                    field: 'source.chain',
                    operator: 'eq',
                    value: 'tempo'
                }
            ]
        }
    ]
}
```

### Only sign typed data that names the signing wallet

A condition can compare an `address` leaf of an EIP-712 message against the signing wallet. This rule
stops a wallet from signing an order on another account's behalf.

```ts {skip-check} theme={"system"}
{
    version: '1.0',
    name: 'Only sign orders for the signing wallet',
    chain_type: 'ethereum',
    rules: [
        {
            name: 'Require the order maker to be the signing wallet',
            method: 'eth_signTypedData_v4',
            action: 'ALLOW',
            conditions: [
                {
                    field_source: 'ethereum_typed_data_message',
                    typed_data: {
                        types: {
                            EIP712Domain: [
                                {name: 'name', type: 'string'},
                                {name: 'version', type: 'string'},
                                {name: 'chainId', type: 'uint256'},
                                {name: 'verifyingContract', type: 'address'},
                            ],
                            Order: [
                                {name: 'maker', type: 'address'},
                                {name: 'amount', type: 'uint256'},
                            ],
                        },
                        primary_type: 'Order',
                    },
                    field: 'maker',
                    operator: 'eq',
                    value: '{{wallet.address}}'
                }
            ]
        }
    ]
}
```

<Warning>
  An `ethereum_typed_data_message` condition only evaluates when the `types` map in the policy
  matches the `types` map in the signing request exactly. See [Restrict parameters of a typed data
  message](/controls/policies/example-policies/ethereum#restrict-parameters-of-a-typed-data-message)
  for the full requirement.
</Warning>

### Only sign login messages that name the signing wallet

The `message` field source compares free-form text, so a template variable can sit inside a longer
value. This rule allows a [Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361) message
only when it names the signing wallet.

```ts {skip-check} theme={"system"}
{
    version: '1.0',
    name: 'Sign-in messages only',
    chain_type: 'ethereum',
    rules: [
        {
            name: 'Allow login messages that name the wallet',
            method: 'personal_sign',
            action: 'ALLOW',
            conditions: [
                {
                    field_source: 'message',
                    field: 'content',
                    operator: 'starts_with',
                    value: 'example.com wants you to sign in with your Ethereum account:\n{{wallet.address}}'
                }
            ]
        }
    ]
}
```

<Warning>
  String comparisons are case-sensitive. For Ethereum wallets, `{{wallet.address}}` resolves to the EIP-55
  checksummed address that the [wallets API](/api-reference/wallets/get) returns, so a message holding
  a lowercase address will not match. Address fields compare EVM addresses without regard to case, so they are
  unaffected.
</Warning>

### Only debit the signing wallet on Solana

A Solana transaction can carry instructions that move funds from other signers. A condition on
`Transfer.from` limits a transaction to debiting the signing wallet.

```ts {skip-check} theme={"system"}
{
    version: '1.0',
    name: 'Debit the signing wallet only',
    chain_type: 'solana',
    rules: [
        {
            name: 'Allow SOL transfers that debit the wallet',
            method: 'signAndSendTransaction',
            action: 'ALLOW',
            conditions: [
                {
                    field_source: 'solana_system_program_instruction',
                    field: 'Transfer.from',
                    operator: 'eq',
                    value: '{{wallet.address}}'
                }
            ]
        }
    ]
}
```

## Next steps

<CardGroup cols={2}>
  <Card title="Create a policy" icon="shield" href="/controls/policies/create-a-policy" arrow>
    Create and attach a policy with the Node SDK or the REST API.
  </Card>

  <Card title="Conditional policies per signer" icon="key" href="/recipes/wallets/conditional-signer-policies" arrow>
    Apply different permissions to different signers on the same wallet.
  </Card>
</CardGroup>
