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

# Core Concepts

> This section explains the primitives that make x402Escrow work: the escrow lifecycle, the roles involved, and the EIP-3009 signed authorization mechanism.

<Badge color="gray">x402Escrow</Badge>

## Escrow Lifecycle

Every escrow ultimately reaches one of two terminal states:

```
                settle()                    release()
  [empty] ------------------> [active] ------------------> [released]
                                 |
                                 |
                                 |  refundAfterTimeout()
                                 +-----------------------> [refunded]
```

### States

| State        | Storage                | Meaning                                                            |
| ------------ | ---------------------- | ------------------------------------------------------------------ |
| **Empty**    | `client == address(0)` | No escrow exists for this ID.                                      |
| **Active**   | `client != address(0)` | USDC is locked. Waiting for settlement or timeout.                 |
| **Released** | Deleted                | Facilitator received payment; client received remainder. Terminal. |
| **Refunded** | Deleted                | Client received full refund after timeout. Terminal.               |

Once an escrow reaches a terminal state, its storage slot is cleared. The same `escrowId` cannot be reused — it is derived from `keccak256(client, nonce)`, where the nonce is a one-time EIP-3009 value.

## Roles

x402Escrow uses OpenZeppelin's AccessControl with three distinct roles:

| Role                                 | Permissions                                                   | Typical holder                    |
| ------------------------------------ | ------------------------------------------------------------- | --------------------------------- |
| **Owner**                            | Authorize UUPS upgrades. Transfer ownership (two-step).       | Multisig or governance contract.  |
| **Admin** (`DEFAULT_ADMIN_ROLE`)     | Grant/revoke roles. Adjust timeout window.                    | Operations team or multisig.      |
| **Facilitator** (`FACILITATOR_ROLE`) | Call `settle()` and `release()`. Receives payment on release. | Service operator or relay server. |

### Why Roles Are Separated

* The **Owner** has the most dangerous capability (upgrading contract logic) but cannot touch escrowed funds.
* The **Admin** manages day-to-day configuration but cannot upgrade the contract.
* The **Facilitator** handles funds but cannot change configuration or upgrade logic.

This follows the principle of least privilege. The compromise of any single role does not grant full control.

## EIP-3009: Signed Authorization

x402Escrow does not use the standard ERC-20 `approve` + `transferFrom` pattern. Instead, it uses [EIP-3009 <Icon icon="arrow-up-right" />](https://eips.ethereum.org/EIPS/eip-3009) — a signed authorization scheme supported natively by USDC token.

### How It Works

1. The client constructs and signs a `ReceiveWithAuthorization` message off-chain. This message specifies:
   * **from**: the client address
   * **to**: the escrow contract address (critical — prevents signature reuse)
   * **value**: the maximum USDC to lock
   * **validAfter / validBefore**: time window for the authorization
   * **nonce**: unique per-authorization (prevents replay)

2. The facilitator submits this signature to `settle()`. The contract calls `receiveWithAuthorization()` on the USDC token, which verifies the signature and transfers funds atomically.

### Why EIP-3009 Over `approve`/`transferFrom`

| Property                    | approve + transferFrom              | EIP-3009                             |
| --------------------------- | ----------------------------------- | ------------------------------------ |
| Client transaction required | Yes (approve tx)                    | No                                   |
| MEV frontrunning risk       | High (anyone can call transferFrom) | None (only `to` address can execute) |
| Gas cost for the client     | \~46k gas for approve               | 0 (off-chain signature)              |
| Nonce management            | Token-level nonce                   | Per-authorization nonce              |
| Replay protection           | Allowance-based                     | Nonce-based, one-time use            |

The `to` field in `ReceiveWithAuthorization` is set to the escrow contract address. Only the escrow contract (as `msg.sender == to`) can execute the transfer. This is a critical anti-MEV property — even if the signed data leaks, no other contract or EOA can use it.

## Escrow ID

Each escrow is identified by a deterministic ID:

```
escrowId = keccak256(abi.encodePacked(client, nonce))
```

This means:

* The same client with different nonces produces different escrow IDs.
* The escrow ID is known before the transaction is submitted (useful for off-chain tracking).
* Collision requires the same client to reuse a nonce, which EIP-3009 prevents at the token level.

## Storage Efficiency

Each active escrow occupies exactly one 32-byte storage slot:

```
+----------------------+-----------+-------------+
|  client (20 bytes)   | refundAt  |   amount    |
|      address         |  uint40   |   uint56    |
+----------------------+-----------+-------------+
                  32 bytes total                    
```

* **uint40 refundAt**: Unix timestamp up to year 36,812. Sufficient for any practical timeout.
* **uint56 amount**: Supports up to approximately 72 billion USDC (72,057,594,037,927,935 micro-units). More than the entire USDC supply.

When an escrow is released or refunded, the slot is zeroed — reclaiming the storage gas refund.

## Timeout Mechanism

Each escrow stores its own `refundAt` timestamp, calculated at settlement time:

```
refundAt = block.timestamp + timeoutSecs
```

Key properties:

* **Per-escrow deadline**: Changing the global `timeoutSecs` via `setTimeout()` does not affect existing escrows. Only new escrows use the updated value.
* **Permissionless refund**: `refundAfterTimeout()` can be called by anyone — not just the client. Funds always go to the stored `client` address. This enables relayer-based gasless refunds.
* **Bounded range**: The admin can set `timeoutSecs` between 5 minutes and 24 hours.

## Payment Routing

When `release()` is called, the facilitator payment goes to `msg.sender` — not to a stored address. This is intentional:

* Multiple addresses can hold `FACILITATOR_ROLE`.
* The address that calls `release()` receives the payment.
* This enables facilitator rotation, load balancing, and hot-wallet separation without contract changes.

## Payment Flow

```
 Client                Facilitator            x402Escrow            USDC Token
   |                       |                      |                      |
   | 1. Sign EIP-3009 auth |                      |                      |
   |---------------------->|                      |                      |
   |                       |                      |                      |
   |                       | 2. settle(signature) |                      |
   |                       |--------------------->|                      |
   |                       |                      |                      |
   |                       |                      | 3. receiveWithAuth   |
   |                       |                      |--------------------->|
   |                       |                      |                      |
   |                       |                      | 4. USDC transferred  |
   |                       |                      |<---------------------|
   |                       |                      |                      |
   |                       | 5. escrowId returned |                      |
   |                       |<---------------------|                      |
   |                       |                      |                      |
   |          ... service executes ...            |                      |
   |                       |                      |                      |
   |                       | 6. release(cost)     |                      |
   |                       |--------------------->|                      |
   |                       |                      |                      |
   |                       | 7. Payment to        |                      |
   |                       |    facilitator       |                      |
   |                       |<---------------------|                      |
   |                       |                      |                      |
   |       8. Remainder refunded to client        |                      |
   |<----------------------+----------------------|                      |
   |                       |                      |                      |
```

<Steps>
  <Step title="Client signs authorization (off-chain)">
    Constructs and signs an EIP-3009 `ReceiveWithAuthorization` message. No gas required. The signed payload specifies the from, to (escrow address), value (max USDC), validAfter/validBefore, and a unique nonce. The client sends this signature to the facilitator, typically via an `X-PAYMENT` HTTP header per the x402 protocol.
  </Step>

  <Step title="Facilitator calls settle()">
    Submits the client's signature to the escrow contract with the EIP-3009 parameters.
  </Step>

  <Step title="Escrow calls receiveWithAuthorization()">
    The contract calls the USDC token to verify the client's signature and pull funds.
  </Step>

  <Step title="USDC transferred">
    The token verifies the signature, checks that the nonce hasn't been used, and transfers funds from the client to the escrow contract. Atomic — if any check fails, the entire transaction reverts.
  </Step>

  <Step title="Escrow ID returned">
    The contract validates the amount, computes `escrowId = keccak256(client, nonce)`, stores the escrow with `refundAt = block.timestamp + timeoutSecs`, and returns the ID to the facilitator.
  </Step>
</Steps>

*...service executes — the facilitator performs the metered work and tracks the actual cost...*

<Steps>
  <Step title="Facilitator calls release()" stepNumber="6">
    Provides the actual cost (`facilitatorAmount ≤ maxAmount`). The contract clears the escrow from storage.
  </Step>

  <Step title="Payment to facilitator" stepNumber="7">
    The contract transfers `facilitatorAmount` to `msg.sender` (the calling facilitator).
  </Step>

  <Step title="Remainder to client" stepNumber="8">
    The contract refunds `maxAmount - facilitatorAmount` to the stored client address.
  </Step>
</Steps>

### Timeout Refund

If the facilitator never calls `release()`:

```
 Anyone                       x402Escrow
   │                              │
   │ refundAfterTimeout(escrowId) │
   │----------------------------->│
   │                              │
   │                              │--> Checks block.timestamp ≥ refundAt
   │                              │--> Transfers full amount to client
   │                              │
   │       Refunded event         │
   │<-----------------------------│
```

* **Permissionless**: any address can trigger it. The client does not need to be online.
* **Funds always go to client**: the stored `client` address receives the refund regardless of who triggers it.
* **Default timeout**: 90 minutes (configurable by admin between 5 min and 24 hours).

### Typical x402 HTTP Integration

1. Agent sends HTTP request to a metered API
2. Server responds with HTTP 402 + payment requirements (price, token, escrow address)
3. Agent signs EIP-3009 authorization for the max amount
4. Agent re-sends the request with `X-PAYMENT` header containing the signature
5. Server (as facilitator) calls settle() to lock funds
6. Server executes the request
7. Server calls release() with the actual cost
8. Agent receives the HTTP response + any USDC remainder
