Protocol Architecture
taurusSwap follows a compute-off-chain, verify-on-chain pattern. This is essential for scalability: the heavy computation (solving the torus invariant for trade output) happens in the SDK, while the contract performs O(1) verification.
The Pattern
On-chain trade computation would require solving a quartic equation (Newton's method iteration), which is too expensive for the AVM opcode budget. Instead:
- Frontend — User inputs swap parameters (token in, token out, amount)
- SDK — Computes the trade output by solving the torus invariant, returns a quote with claimedOut
- Transaction Group — User signs ASA transfer (input) + app call (with claimedOut)
- Contract — Verifies the torus invariant still holds with the new state, checks claimedOut ≥ minOut, emits inner transaction payout
The key insight: verification is always O(1), regardless of how many tokens or ticks are involved. The contract never solves equations — it only checks that the proposed solution is valid.
Data Flow
┌─────────────┐
│ Frontend │
│ (React UI) │
└──────┬──────┘
│ user input
▼
┌─────────────────────────┐
│ SDK (compute quote) │
│ - readPoolState │
│ - getSwapQuote │
│ - solve torus invariant│
└──────────┬──────────────┘
│ quote: { amountOut, priceImpact }
▼
┌─────────────────────────┐
│ Transaction Group │
│ - ASA transfer (in) │
│ - App call (swap) │
│ - Signed by user │
└──────────┬──────────────┘
│ atomic group
▼
┌─────────────────────────┐
│ Contract (verify) │
│ - read old state │
│ - compute new sumX, │
│ sumXSq from trade │
│ - evaluate invariant │
│ - check tolerance │
│ - inner tx payout │
└─────────────────────────┘Smart Contract Methods
The contract exposes these ARC-4 methods:
| Method | Description |
|---|---|
swap | Simple swap without tick crossings |
swap_with_crossings | Swap that crosses one or more tick boundaries |
add_tick | Add a new tick (LP provides liquidity) |
remove_liquidity | Remove liquidity from a tick position |
claim_fees | Claim accrued fees from a position |
get_pool_state | Read-only: return global state |
get_tick_state | Read-only: return tick state by ID |
Atomic Composition
Algorand's atomic transaction groups enable safe composition:
- Add liquidity — n ASA transfers (one per token) + app call, all in one group
- Swap — ASA transfer (input) + app call + inner ASA transfer (output), all atomic
- Remove liquidity — App call + inner ASA transfers (output), all atomic
Either all transactions succeed or all fail. There's no intermediate state where the user has transferred input but not received output.
Why Not Compute On-Chain?
Solving the torus invariant for Δx_out given Δx_in requires:
- Substituting the trade into the invariant
- Expanding to get a quartic equation
- Using Newton's method to find the root
Newton's method typically requires 10-20 iterations for convergence. Each iteration involves multiple 512-bit multiplications and a division. On the AVM, this would exceed the opcode budget for a single transaction.
By moving computation off-chain, the contract only needs to:
- Read 2 state values (sumX, sumXSq)
- Compute new values from the trade (O(1))
- Evaluate the invariant (O(1))
- Check tolerance (O(1))
This fits comfortably within the opcode budget, even for large n.
Note:The opcode budget pooling pattern (multiple app calls in one group sharing a budget) could enable on-chain computation for small n, but it's unnecessary complexity. Off-chain compute is simpler and cheaper.