Vauban Docs
Architecture

Swap & Intent System

Swap & Intent System

Technical architecture of VaubanSwap — Starknet's CoW (Coincidence of Wants) intent settlement system.

System Overview

                    Off-chain                          On-chain
┌──────────┐     ┌────────────┐     ┌──────────────┐     ┌──────────────┐
│  User     │────>│  Backend   │────>│ CoW Matcher  │────>│  Settlement  │
│  Wallet   │     │  (API)     │     │ (bipartite)  │     │  Contract    │
│           │<────│            │<────│              │     │              │
│  SNIP-12  │     │ swap_intents│    │ cow-matcher   │     │ VaubanSettle │
│  Signing  │     │  table     │     │  .ts (318L)  │     │  ment.cairo  │
└──────────┘     └────────────┘     └──────┬───────┘     └──────────────┘

                                    ┌──────┴───────┐
                                    │ AVNU Fallback │
                                    │ (unmatched)   │
                                    └──────────────┘

Smart Contracts

VaubanSettlement

File: contracts/src/VaubanSettlement.cairo (571 LOC)

The atomic batch settlement contract. All intents in a batch either succeed together or revert together.

Constructor:

VaubanSettlement(owner, deposit_token, min_solver_deposit)

Core functionsettle_batch():

settle_batch(
    users:           Span<ContractAddress>,   // Intent signers
    sell_tokens:     Span<ContractAddress>,   // Tokens being sold
    buy_tokens:      Span<ContractAddress>,   // Tokens being bought
    sell_amounts:    Span<u256>,              // Amounts sold
    min_buy_amounts: Span<u256>,             // Minimum acceptable output
    deadlines:       Span<u64>,              // Expiration timestamps
    nonces:          Span<u256>,             // Replay protection
    signatures:      Span<Span<felt252>>,    // SNIP-12 signatures
    fill_buy_amounts: Span<u256>,            // Actual fill amounts (>= min)
)

Invariant: For every intent i, fill_buy_amounts[i] >= min_buy_amounts[i]. The contract hard-reverts the entire batch if any intent receives less than its minimum.

Security features:

  • OpenZeppelin: Ownable, Pausable, ReentrancyGuard
  • Nonce-based replay protection (per-user nonce tracking)
  • On-chain intent cancellation (cancel_intent, cancel_intents)
  • Deadline enforcement (expired intents revert)

Solver Registration

Two paths to become a solver:

PathMechanismRequirements
Admin-authorizedauthorize_solver(address)Owner approval
Permissionlessregister_solver()Deposit bond (100 STRK default)

Permissionless solvers deposit a configurable bond (min_solver_deposit) in the deposit token. The bond can be slashed by the admin for misbehavior via slash_solver(). Deregistration returns the remaining bond.

IntentVault

File: contracts/src/IntentVault.cairo (476 LOC)

Optional pre-deposit vault for faster execution. Users deposit tokens in advance; the solver pulls directly from the vault during settlement.

Key features:

  • Supports multiple ERC20 tokens
  • Auto-staking: output tokens can be auto-staked into VaubanVault via supported_vaults mapping
  • Instant withdrawal (no unbonding period)
  • Per-user balance tracking

YieldIntentVault

File: contracts/src/YieldIntentVault.cairo (612 LOC)

Extended IntentVault that earns yield on idle DCA funds via Nostra lending integration.

Architecture:

User deposits ──> YieldIntentVault ──> Nostra ILendingPool
                       │                      │
                       │ 10% liquid buffer     │ supply()
                       │ for execution         │ withdraw()
                       │                      │
                  DCA executor ◄───── yield + principal

Key parameters:

ParameterValuePurpose
Liquid buffer10%Ensures execution reliability
Per-user cap100K STRKRisk management
Min stake10 STRKGas efficiency threshold

Functions:

  • stake_idle_funds() — Move idle balance to lending pool
  • unstake_for_execution() — Pull funds back for DCA execution
  • harvest_yield() — Collect and redistribute earned yield

Off-Chain Components

Solver Service

File: apps/defi/bastion/api/src/services/solver.service.ts (523 LOC)

Orchestrates the batch settlement cycle:

  1. Collect: Accumulate signed intents from API submissions
  2. Match: Run CoW bipartite matching (cow-matcher.ts)
  3. Settle: Submit matched batch to VaubanSettlement
  4. Fallback: Route unmatched intents through AVNU aggregator

The batch window is adaptive — higher volume triggers more frequent settlements.

CoW Matcher

File: apps/defi/bastion/api/src/services/cow-matcher.ts (318 LOC)

Bipartite matching engine that finds opposing intent pairs:

Intent A: Sell 1000 STRK, Buy ETH
Intent B: Sell 0.5 ETH, Buy STRK
──────────────────────────────────
Match: A ←→ B (Coincidence of Wants)
Surplus: Any price improvement beyond both minimums

The matcher maximizes total matched volume across all token pairs in a single pass.

SNIP-12 Intent Signing

Intents are signed off-chain using the SNIP-12 typed data standard (Starknet's equivalent of EIP-712).

Type hash structure:

SwapIntent {
    inputToken:     ContractAddress,
    outputToken:    ContractAddress,
    inputAmount:    u256,
    minOutputAmount: u256,
    deadline:       u64,
    nonce:          felt252,
}

Signing spec: docs/specs/SNIP-12-SWAP-INTENT.md

Settlement Flow

1. User signs SNIP-12 intent off-chain (zero gas)

2. Backend stores intent in swap_intents table

3. Solver triggers batch window

4. CoW matcher finds opposing pairs

5a. Matched pairs ──> VaubanSettlement.settle_batch()
    │                     │
    │                     ├── Verify all signatures
    │                     ├── Check all nonces unused
    │                     ├── Check all deadlines valid
    │                     ├── Transfer sell tokens from users
    │                     ├── Transfer buy tokens to users
    │                     ├── Assert fill >= minBuyAmount
    │                     └── Emit IntentSettled + BatchSettled

5b. Unmatched intents ──> AVNU aggregator
    │                     │
    │                     └── Price floor: minOutputAmount

6. WebSocket notifies user of settlement status

Events

EventIndexed KeysData
IntentSettleduser, noncesell_token, buy_token, sell_amount, buy_amount, settlement_id
BatchSettledsettlement_idnum_intents, solver, timestamp
IntentCancelleduser, nonce
SolverRegisteredsolverdeposit
SolverDeregisteredsolverrefund
SolverSlashedsolveramount, remaining

Deployed Contracts

ContractSepoliaMainnet
VaubanSettlement0x003ea562...0x070347e0...
IntentVaultSee .sepolia_addresses0x03450709...
YieldIntentVaultPending

Security Considerations

  • No custody: Users sign intents; tokens are transferred atomically during settlement
  • Atomic batches: All-or-nothing execution prevents partial fills
  • Solver bonds: Permissionless solvers stake collateral that can be slashed
  • AVNU price floor: Unmatched intents always get at least minOutputAmount
  • Nonce replay protection: Each nonce can only be used once per user
  • Pausable: Admin can halt settlements during incidents