Status: Draft
Type: Standards Track
Category: Core
Created: 2026-01-18
Requires: CIP-20
Abstract
CIP-21 defines Cowboy’s standard for decentralized exchanges and liquidity pools. The design is hybrid: pools are actors (maximum flexibility) with platform-level primitives for efficiency (math helpers, routing, LP tokens). Key features:- Two pool types: Constant product (V2-style) and concentrated liquidity (V3-style)
- Platform LP tokens: Fungible LP shares for V2 pools (CIP-20 tokens)
- Actor-managed positions: Non-fungible liquidity positions for V3 pools
- Validation hooks:
can_swap/on_swapfor compliance and MEV protection - Dual routing: Actor-based router for flexibility, platform primitive for efficient multi-hop
Motivation
DEXes are critical infrastructure for any blockchain ecosystem. Cowboy’s unique features—actors, timers, platform tokens—enable DEX designs not possible on Ethereum:- Native TWAP oracles via timers (no external keeper)
- On-chain limit orders via height-timer polling (a state-triggered timer is a future CIP-5 extension; see §Limit Orders)
- Efficient batch swaps via platform routing
- Compliance pools via validation hooks
Specification
Overview
Part 1: Platform Primitives
The runtime provides efficient helpers for common AMM operations.1.1 Math Primitives
1.2 Concentrated Liquidity Math
1.3 Platform Routing
Part 2: V2 Pool Standard (Constant Product)
V2 pools use the classicx * y = k formula with fungible LP tokens.
2.1 Interface
2.2 Validation Hooks
V2 pools MAY specify a validation hook for compliance or MEV protection:2.3 Reference Implementation
2.4 Worked Example: CIP-20 token ↔ V2 pool
This walks the full path end to end — deploy CIP-20 tokens → seed a V2 pool → swap → harvest LP fees — with concrete addresses, amounts, and expected balances. Every figure below is produced by the spec formulas in §1.1 and §2.3 (integer floor division); a runnable snippet that reproduces them is at the end. Cast (illustrative addresses):Step 1 — Deploy two CIP-20 tokens
decimals=0 keeps the arithmetic whole-numbered for readability; production
tokens typically use 6–18 decimals.
Step 2 — Seed the V2 pool (first liquidity)
The pool pulls both legs withToken.transfer_from (Alice must approve the pool
first), then mints LP tokens. For the first provider the spec mints
sqrt(amount_a * amount_b) - 1000 and locks 1000 to address(0):
Emits
AddLiquidity(alice, 100_000, 100_000, 99_000, alice) and Sync(100_000, 100_000).
1_000 LP is permanently locked at address(0) (anti-first-depositor-griefing),
so Alice owns 99_000 / 100_000 = 99% of the pool.
Step 3 — Swap (round trip)
Bob swaps10_000 ACME for USDC. With fee_bps = 30:
9_066 USDC back for ACME, returning the price to ~1:1 so
the harvest in step 4 is purely accrued fees (no impermanent-loss noise):
Each swap emits
Swap(sender, token_in, amount_in, amount_out, recipient) and a
Sync. Note k strictly grows (10.000B → 10.0055B): the 30 bps fee on every
swap stays in the reserves. That growth is the LP fee.
Step 4 — Harvest LP fees
V2 has no separate “claim fees” call — fees accrue into the reserves, so each LP token becomes redeemable for more than it was at seed. Measuring redemption value per LP token isolates the fee cleanly (it is unaffected by the locked 1_000):
+0.0275% per LP token — entirely harvested swap fees. Alice realizes her share
by burning her LP:
RemoveLiquidity(alice, 99_000, 99_054, 99_000, alice)). Her LP redeemed at the grown per-LP value (2.00055 vs
2.00000 at seed): that uplift is her cut of the 30 bps fees Bob and Carol paid.
(Her redemption tracks 99% of reserves, not 100%, because of the locked minimum
liquidity.)
Reproduce the numbers
Part 3: V3 Pool Standard (Concentrated Liquidity)
V3 pools allow LPs to concentrate liquidity in price ranges for higher capital efficiency.3.1 Core Concepts
Ticks: Price space is divided into discrete ticks. Each tick represents a 0.01% price change. Positions: LPs provide liquidity between two ticks (a price range). Positions are non-fungible. Liquidity: A position’s liquidity value determines its share of fees when price is in range.3.2 Interface
3.3 Position Data
Positions are stored in actor state (not as NFTs):3.4 Validation Hooks
V3 pools follow the same hook pattern as V2 (validation-only: returnTrue/False, cannot modify amounts), but with V3-specific signatures — can_swap takes sqrt_price_limit in place of V2’s min_amount_out, and position hooks (can_mint_position / can_burn_position) replace V2’s can_add_liquidity / can_remove_liquidity:
3.5 Position Manager (Optional)
For better UX, a position manager actor can wrap positions as transferable:Part 4: Factory
The factory creates and indexes pools:Standard Fee Tiers
Part 5: Router
5.1 Actor Router (Flexible)
5.2 Platform Router (Efficient)
The platform router (amm_swap_exact_in / amm_swap_exact_out) provides:
- Lower gas cost (no actor call overhead per hop)
- Atomic multi-hop execution
- Standardized interface
- Standard path-based swap
- No custom logic needed
- Maximum efficiency required
- Complex operations (add liquidity + swap)
- Custom fee handling
- Flash swaps
Part 6: Advanced Features
6.1 Native TWAP Oracle
V2 pools include a built-in TWAP oracle updated via timers:6.2 On-Chain Limit Orders
⚠️ Note (timer API): CIP-5 timers are height-triggered one-shot only — there is no state/condition trigger (Using a limit-order pattern layered on CIP-5 timers:trigger_type="state",watch_address,condition,handler=) in the deployed timer API (schedule_timer(height, payload)/schedule_timer_ex). The example below is illustrative of a desired pattern; against the current API a limit order is emulated by re-arming a short-interval height timer each block that polls the pool price (the handler re-schedules itself, CIP-5 §4.2 / Appendix B). A true state-triggered timer would be a future CIP-5 extension.
6.3 MEV Protection via Hooks
6.4 Compliance Pools
Events
V2 Pool Events
V3 Pool Events
Error Conditions
Actor pools are illustrative reference implementations and revert viarequire(cond, "<message>") rather than numeric codes. The canonical revert messages are:
Security Considerations
Reentrancy
The actor model provides natural reentrancy protection—actors process one message at a time. However, cross-actor calls during swaps should follow checks-effects-interactions pattern.Price Manipulation
- TWAP oracles mitigate flash loan attacks
- Concentrated liquidity pools are more sensitive to manipulation at range boundaries
- Hooks can implement additional protections (MEV detection, circuit breakers)
Hook Security
- Hooks are capped at 50,000 Cycles and 50,000 Cells (same as CIP-20 token hooks)
- Malicious hooks can block all swaps—pool deployers must be trusted
- Consider timelock for hook updates on major pools
Integer Precision
- Use Q128 format for prices to maintain precision
- Concentrated liquidity math uses Q64.96 (matching Uniswap V3)
- Platform primitives handle precision; actor implementations should use them
Rationale
Why Hybrid (Actor + Platform)?
Pure actor-based: Maximum flexibility but higher gas costs Pure platform-based: Maximum efficiency but inflexible Hybrid gives:- Flexibility for pool logic (actors)
- Efficiency for common operations (platform primitives)
- Best of both worlds
Why Both V2 and V3?
V2 (constant product):- Simpler for LPs
- Fungible LP tokens (composable with DeFi)
- Lower gas cost
- Good for stable pairs
- Higher capital efficiency
- Better for professional LPs
- Required for competitive pricing on major pairs
Why Validation-Only Hooks?
Full hooks (modifying amounts) add complexity:- Unpredictable outputs
- Gas estimation difficulty
- Hidden fees
- Swap succeeds or fails
- Predictable gas
- Covers compliance use cases
Backwards Compatibility
This is a new standard. No backwards compatibility concerns.Reference Implementation
Seecowboy-core/src/runtime/amm.rs for platform primitive implementations.
See examples/cowswap/ for reference V2 and V3 pool implementations.
