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

# Intents

> The GeodeIntent struct, Permit2 signing, replay protection, and the intent lifecycle.

## What is an Intent?

An intent is a signed message that says: *"I want to swap X tokens for at least Y tokens, by this deadline."* It's not an on-chain transaction — it's a gasless signature that authorizes a settler to execute the trade on your behalf during batch settlement.

<Info>
  **Key difference from a swap**: A swap executes immediately against the AMM. An intent is collected, batched with others, and settled at a uniform clearing price. You trade against the batch, not against the pool.
</Info>

## The GeodeIntent Struct

Every intent contains these fields:

```solidity theme={null}
struct GeodeIntent {
    PoolId poolId;        // Which pool this intent targets
    address owner;        // Signer's address (who owns the tokens)
    address tokenIn;      // Token being sold
    address tokenOut;     // Token being received
    uint256 amountIn;     // Amount to sell
    uint256 minAmountOut; // Minimum acceptable output (slippage protection)
    uint256 deadline;     // Expiry timestamp (intent invalid after this)
    uint256 nonce;        // Permit2 replay protection
}
```

### Implicit Limit Price

The intent doesn't have an explicit price field. Instead, the limit price is derived from `amountIn` and `minAmountOut`:

* **Buy limit price** = `amountIn × Q128 / minAmountOut` (maximum price willing to pay)
* **Sell limit price** = `minAmountOut × Q128 / amountIn` (minimum price willing to accept)

A buy intent fills if its limit price ≥ the clearing price. A sell intent fills if its limit price ≤ the clearing price. Intents that don't meet the clearing price are skipped entirely — the user's tokens are never moved.

## Permit2 Integration

Geode uses [Permit2](https://github.com/Uniswap/permit2) for gasless token approvals and transfers. This is the mechanism that lets users sign intents off-chain while settlers execute them on-chain.

### How It Works

<Steps>
  <Step title="One-Time Approval">
    The user approves the Permit2 contract to spend their tokens (standard ERC20 `approve`). This is a one-time transaction per token — Permit2 is shared across all protocols.
  </Step>

  <Step title="Sign the Intent">
    The user signs a Permit2 `PermitWitnessTransferFrom` message that includes:

    * **Permitted**: token address, amount, nonce, deadline (standard Permit2 fields)
    * **Witness**: poolId, tokenOut, minAmountOut (Geode-specific fields)

    This single signature authorizes both the token transfer AND the trade parameters.
  </Step>

  <Step title="Settler Executes">
    During `geodeSettleBatch()`, the hook calls `permit2.permitWitnessTransferFrom()` for each filled intent. Permit2 verifies the signature, checks the nonce, and transfers tokens from the user to the PoolManager.
  </Step>
</Steps>

### Witness Type

The Geode-specific witness contains only the fields NOT already in the Permit2 `PermitTransferFrom`:

```solidity theme={null}
bytes32 constant GEODE_WITNESS_TYPEHASH = keccak256(
    "GeodeWitness(bytes32 poolId,address tokenOut,uint256 minAmountOut)"
);
```

The full signed type becomes:

```
PermitWitnessTransferFrom(
  TokenPermissions permitted,
  address spender,
  uint256 nonce,
  uint256 deadline,
  GeodeWitness witness
)
GeodeWitness(bytes32 poolId,address tokenOut,uint256 minAmountOut)
TokenPermissions(address token,uint256 amount)
```

## Replay Protection

Intents are protected against replay attacks at multiple levels:

<CardGroup cols={2}>
  <Card title="Permit2 Nonce" icon="hashtag">
    Each intent uses a unique Permit2 nonce. Once used, the nonce is consumed and the signature becomes invalid. The same intent can never be executed twice.
  </Card>

  <Card title="Pool Binding" icon="link">
    The `poolId` is part of the signed witness. An intent signed for Pool A cannot be replayed on Pool B — the signature verification will fail.
  </Card>

  <Card title="Deadline" icon="clock">
    Every intent has an expiry timestamp. After the deadline, the Permit2 signature is invalid and the tokens cannot be moved, regardless of whether the nonce was used.
  </Card>

  <Card title="Chain ID" icon="shield">
    Permit2's EIP-712 domain separator includes the chain ID. Signatures from Ethereum mainnet cannot be replayed on other chains.
  </Card>
</CardGroup>

## Intent Lifecycle

<Steps>
  <Step title="Created">
    User configures the trade in the UI (or programmatically) and signs the Permit2 message in their wallet. The signature is produced locally — no on-chain transaction.
  </Step>

  <Step title="Submitted">
    The signed intent is sent to a settler (or a public mempool of intents). The settler collects intents for the current batch window.
  </Step>

  <Step title="Evaluated">
    When the settler is ready to settle, the clearing price algorithm evaluates each intent:

    * **Fills** if the intent's limit price is compatible with the clearing price
    * **Skipped** if the limit price is not met (user keeps their tokens)
  </Step>

  <Step title="Settled">
    For filled intents, Permit2 pulls the user's tokens, the settlement routes them through internal matching and/or the AMM, and output tokens are distributed to the user's wallet.
  </Step>
</Steps>

## What Happens If My Intent Doesn't Fill?

If the clearing price doesn't meet your `minAmountOut`, your intent is simply skipped. Your tokens are never moved and your Permit2 nonce is NOT consumed — you can reuse the same signed intent in a future batch.

If your intent isn't included in any batch before its deadline, the signature expires naturally and becomes unusable.

## Settler Competition

Settlement is **permissionless** — anyone can call `geodeSettleBatch()`. Settlers compete to submit the most complete batches because:

* More filled intents = more settlement fees earned
* Omitting valid intents leaves money on the table for a competing settler
* The first valid batch for a given window wins

This competitive dynamic ensures intents are included and settled promptly.

## Security Model

Permit2 has two modes — Geode deliberately uses the safer one.

### SignatureTransfer, Not AllowanceTransfer

Permit2 provides two token transfer mechanisms:

|                   | AllowanceTransfer                                         | SignatureTransfer                                             |
| ----------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
| **Approval type** | Standing allowance (amount + expiry)                      | Single-use permit per signature                               |
| **Reuse**         | Can be spent multiple times up to the allowance           | Each nonce is consumed on first use                           |
| **Risk surface**  | A compromised spender can drain up to the approved amount | A compromised signature can only move exactly `amountIn` once |
| **Used by**       | Uniswap frontend (swap router)                            | **Geode intents**                                             |

Geode uses **`permitWitnessTransferFrom`** from the `SignatureTransfer` interface. Every intent signature is a single-use authorization — once the Permit2 nonce is consumed, that signature is permanently dead.

<Warning>
  **Permit2 itself still requires a one-time ERC20 `approve()`** — you approve Permit2 (not Geode) to spend your tokens. This is a shared approval across all protocols that use Permit2. Individual intents then scope what Permit2 is allowed to do with each signature.
</Warning>

### Bounded Exposure

Every intent signature is scoped to exactly five constraints:

| Constraint      | Field                    | Effect                                              |
| --------------- | ------------------------ | --------------------------------------------------- |
| **Amount**      | `amountIn`               | Maximum tokens that can be moved                    |
| **Time**        | `deadline`               | Signature expires and becomes unusable              |
| **Destination** | `spender` (hook address) | Only the Geode hook contract can use this signature |
| **Pool**        | `poolId` (witness)       | Cannot be replayed on a different pool              |
| **One-shot**    | `nonce`                  | Consumed on first use, permanently invalidated      |

Your maximum exposure at any moment is: **exactly `amountIn` tokens, for at most `deadline - now` seconds, usable only by the specific hook contract, on the specific pool, exactly once.**

If the intent fills, you receive at least `minAmountOut` tokens in return. If it doesn't fill, your tokens are never touched and the nonce remains available for reuse.

### What a Settler Cannot Do

The settler submits your signed intent to the hook, but the hook enforces all safety checks on-chain:

* **Cannot change the amount** — Permit2 verifies `amountIn` matches the signature
* **Cannot change the recipient** — output tokens are sent to `intent.owner`, hardcoded in the settlement logic
* **Cannot underpay you** — if the clearing price doesn't meet `minAmountOut`, the intent is skipped entirely
* **Cannot replay the signature** — Permit2 nonces are consumed atomically
* **Cannot front-run** — all intents in a batch get the same uniform clearing price

The settler's only power is deciding *which* valid intents to include. And omitting valid intents just leaves settlement fees for a competing settler to claim.

### No Residual Risk

Unlike protocols that hold standing Permit2 allowances, Geode intents leave no persistent authorization:

* **Before signing**: Permit2 has your ERC20 approval, but no signature exists to use it
* **After signing**: A time-limited, single-use, amount-capped signature exists
* **After settlement**: The nonce is consumed — the signature is dead
* **After deadline**: Even if never settled, the signature expires and becomes cryptographically unusable

There is no window where a compromised contract or key can drain more than what you explicitly signed for.

<Note>
  **For integrators**: The intent can be constructed and signed using any EIP-712 compatible library. The witness type string and typehash are defined in `GeodeTypes.sol`. See the [production fork tests](https://github.com/Geode-vAMM/geodex) for complete signing examples with real Permit2.
</Note>
