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

# Token Approvals

> Grant the allowances that let settlement pull your input tokens - once, or per trade.

Settlement can only pull the tokens you've allowed it to. That permission is the ordinary ERC-20 allowance mechanism: an `approve()` call on the token itself, granting a named spender the right to move up to a chosen amount on your behalf.

The same escrow settlement system serves both the RFQ API and the Router API, so the approval flow is identical across products.

## Finding the Right Spender

Run preflight on your selected quote - the returned `nextActions` include any approval step, with the exact spender address and amount. Always use those values rather than hardcoding.

<Warning>
  **Approval addresses are not constants.** They shift with chain and lock type, so treat the preflight `nextActions` of your selected candidate as the only authority on where an approval goes.

  Some tokens need no approval at all: USDC-style tokens settle via EIP-3009 `transferWithAuthorization`, authorized entirely inside your signature.
</Warning>

## Reading Current Allowance

Read the existing allowance first - if it already covers the trade, an extra approval transaction is pure wasted gas.

```python theme={null} theme={null}
from web3 import Web3

ERC20_ABI = [
    {
        "constant": True,
        "inputs": [
            {"name": "_owner", "type": "address"},
            {"name": "_spender", "type": "address"},
        ],
        "name": "allowance",
        "outputs": [{"name": "", "type": "uint256"}],
        "type": "function",
    },
    {
        "constant": False,
        "inputs": [
            {"name": "_spender", "type": "address"},
            {"name": "_value", "type": "uint256"},
        ],
        "name": "approve",
        "outputs": [{"name": "", "type": "bool"}],
        "type": "function",
    },
]

w3 = Web3(Web3.HTTPProvider("https://mainnet.optimism.io"))

def check_allowance(owner: str, token_address: str, spender: str) -> int:
    token = w3.eth.contract(
        address=Web3.to_checksum_address(token_address),
        abi=ERC20_ABI,
    )
    return token.functions.allowance(
        Web3.to_checksum_address(owner),
        Web3.to_checksum_address(spender),
    ).call()
```

## Granting Allowance

When the allowance falls short, top it up with an approval transaction. The common pattern is a one-time maximum grant (`2^256 - 1`) per token, after which the question never comes up again.

```python theme={null} theme={null}
from eth_account import Account

PRIVATE_KEY = "0x<your_private_key>"

def ensure_allowance(account, token_address: str, spender: str, required: int):
    current = check_allowance(account.address, token_address, spender)
    if current >= required:
        return  # already approved

    token = w3.eth.contract(
        address=Web3.to_checksum_address(token_address),
        abi=ERC20_ABI,
    )

    tx = token.functions.approve(
        Web3.to_checksum_address(spender),
        2**256 - 1,  # max approval
    ).build_transaction({
        "from": account.address,
        "nonce": w3.eth.get_transaction_count(account.address),
        "gasPrice": w3.eth.gas_price,
    })

    signed = w3.eth.account.sign_transaction(tx, private_key=PRIVATE_KEY)
    tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
    w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
```

## Wiring It into the Trade Flow

After selecting a quote, run preflight and execute any approval action it returns before signing. Approval actions arrive as ready-to-broadcast transactions - `type: "evmTransaction"` with `purpose: "tokenApproval"` and the prepared calldata in `tx`:

```python theme={null} theme={null}
import httpx

preflight = httpx.post(
    "https://api.tetrafi.io/api/v1/router/orders/preflight",
    json={"quoteResponse": selected_quote},
    headers={"X-API-Key": YOUR_API_KEY},
).json()

for action in preflight["nextActions"]:
    if action["purpose"] == "tokenApproval" and action["actor"] == "userWallet":
        tx = action["tx"]  # {to, data, value} - broadcast as returned
        signed = w3.eth.account.sign_transaction(
            {**tx, "from": account.address,
             "nonce": w3.eth.get_transaction_count(account.address),
             "gasPrice": w3.eth.gas_price},
            private_key=PRIVATE_KEY,
        )
        w3.eth.wait_for_transaction_receipt(
            w3.eth.send_raw_transaction(signed.raw_transaction), timeout=120
        )

# Actions with frequency "reusableSetup" (e.g. a bounded Permit2 approval)
# persist across trades - you won't see them again until they're spent.
# Now proceed to sign and submit...
```

## Approval Strategies

**Maximum grant** - the usual choice for programmatic flow: approve `2^256 - 1` per token once and never pay approval gas again. Solvers and aggregators default to this.

**Per-trade grant** - approve precisely what each trade needs. Tighter risk posture, at the cost of an approval transaction every time; favoured by some compliance-sensitive setups.

## Funding Locks Beyond `approve()`

The escrow supports three ways of funding an order; preflight selects the one available for your corridor and token:

* **Permit2:** Approve the [Permit2 contract](https://github.com/Uniswap/permit2) once per token; each order's escrow pull is then authorized by an off-chain EIP-712 signature - no per-settler approvals. See the [Router API quickstart](/router-api/quickstart).
* **EIP-3009:** Tokens like USDC support `transferWithAuthorization` - the escrow pull is authorized entirely inside your signed message, with no prior approval transaction. See the [RFQ Order Submission guide](/rfq-api/guides/gasless-execution).
* **Resource lock:** Pre-deposit into the compact settler and trade against that balance - suited to high-frequency integrators.

<Note>
  The lock type is a property of the corridor and token, not something you choose per request - read it from the preflight `nextActions` for your selected candidate.
</Note>
