Skip to main content

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.

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

The GeodeIntent Struct

Every intent contains these fields:
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 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

1

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

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

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.

Witness Type

The Geode-specific witness contains only the fields NOT already in the Permit2 PermitTransferFrom:
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:

Permit2 Nonce

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.

Pool Binding

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.

Deadline

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.

Chain ID

Permit2’s EIP-712 domain separator includes the chain ID. Signatures from Ethereum mainnet cannot be replayed on other chains.

Intent Lifecycle

1

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

Submitted

The signed intent is sent to a settler (or a public mempool of intents). The settler collects intents for the current batch window.
3

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)
4

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.

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.
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 for complete signing examples with real Permit2.