Skip to main content

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.

Security Model

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

SignatureTransfer, Not AllowanceTransfer

Permit2 provides two token transfer mechanisms:
AllowanceTransferSignatureTransfer
Approval typeStanding allowance (amount + expiry)Single-use permit per signature
ReuseCan be spent multiple times up to the allowanceEach nonce is consumed on first use
Risk surfaceA compromised spender can drain up to the approved amountA compromised signature can only move exactly amountIn once
Used byUniswap 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.
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.

Bounded Exposure

Every intent signature is scoped to exactly five constraints:
ConstraintFieldEffect
AmountamountInMaximum tokens that can be moved
TimedeadlineSignature expires and becomes unusable
Destinationspender (hook address)Only the Geode hook contract can use this signature
PoolpoolId (witness)Cannot be replayed on a different pool
One-shotnonceConsumed 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.
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.