Skip to content

Instantly share code, notes, and snippets.

@silentgeckoaudit3801
Created June 8, 2026 04:38
Show Gist options
  • Select an option

  • Save silentgeckoaudit3801/8dd78d43835cfa07db9504fc4d8e2733 to your computer and use it in GitHub Desktop.

Select an option

Save silentgeckoaudit3801/8dd78d43835cfa07db9504fc4d8e2733 to your computer and use it in GitHub Desktop.
Static analysis: ALEX AMM pool v2-01

Static Analysis: ALEX AMM Pool v2-01

Contract: SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01
Audit date: 2026-06-08
Scope: amm-pool-v2-01, with directly relevant authorization and mutation paths in amm-registry-v2-01 and amm-vault-v2-01.

Executive summary

The AMM pool contract is a routing and calculation layer over a separate registry, vault, and LP-token contract. It retains only the global pause flag. The most important issue is an authorization pattern repeated across every pool-owner setter: it trusts tx-sender rather than requiring the immediate contract-caller to be the pool owner. A malicious contract called by a pool owner can therefore exercise the owner's configuration authority.

No high or critical issue was identified. The review found one medium, two low, and two informational findings.

Severity Count
Critical 0
High 0
Medium 1
Low 2
Informational 2

1. State model

Local persistent state

Store Line Initial value Writers Authority
paused data-var 16 true pause DAO or registered DAO extension

The contract declares no local data maps. Pool balances, supply, ownership, fees, oracle configuration, block windows, thresholds, ratios, and blocklist state live in amm-registry-v2-01. Token custody and reserves live in amm-vault-v2-01. LP balances live in token-amm-pool-v2-01.

Constants

Lines Constants Purpose
2-15 ERR-* u1000/u2000-u2012 Authorization, pool, liquidity, slippage, oracle, pause, and ratio errors
481-482 ONE_8, MAX_POW_RELATIVE_ERROR Fixed-point scale and power-error bound
506-509 exponent bounds Fixed-point exponent safety limits
510-522 precomputed logarithm factors Fixed-point logarithm approximation
523-527 math errors Fixed-point bounds and exponent errors

External mutation model

External contract State affected Called by
amm-registry-v2-01 pools, balances, supply, owners, fee/oracle/ratio settings, reserves metadata, blocklist All setters, create, add/reduce liquidity, swaps
amm-vault-v2-01 token custody and reserves reduce liquidity and swaps
token-amm-pool-v2-01 LP token supply and balances add/reduce liquidity
Caller-supplied SIP-010 traits User token balances add liquidity and swaps

2. Function inventory

Read-only functions

All read-only functions are open. They do not mutate state.

Group Functions Behavior / external calls
Authorization and policy is-dao-or-extension, is-blocklisted-or-default, check-pool-status Reads DAO extension status, registry blocklist, and pool block window
Pool/config getters get-pool-details-by-id, get-pool-details, get-pool-exists, is-paused, get-balances, get-start-block, get-end-block, get-max-in-ratio, get-max-out-ratio, get-oracle-enabled, get-oracle-average, get-threshold-x, get-threshold-y, get-fee-rebate, get-fee-rate-x, get-fee-rate-y, get-pool-owner, get-max-ratio-limit Reads local pause or registry state
Oracle/price get-oracle-resilient, get-oracle-instant, get-price Calculates prices from registry balances/config
Swap quotes get-y-given-x, get-x-given-y, get-y-in-given-x-out, get-x-in-given-y-out, get-x-given-price, get-y-given-price Fixed-point quote calculations with ratio limits
Liquidity quotes get-token-given-position, get-position-given-mint, get-position-given-burn Calculates required tokens or LP shares
Route helpers get-helper, get-helper-a, get-helper-b, get-helper-c, fee-helper, fee-helper-a, fee-helper-b, fee-helper-c Selects pool direction and composes multi-hop quotes/fees
Math get-invariant Calculates the selected AMM invariant

Public functions

Function(s) Lines Authority / preconditions Mutations / transfers
pause 198-201 DAO or DAO extension Writes local paused
set-start-block, set-end-block 202-211 Pool owner by tx-sender, or DAO/extension Registry block window
set-max-in-ratio, set-max-out-ratio 212-221 Pool owner by tx-sender, or DAO/extension; registry caps ratios Registry ratio limits
set-oracle-enabled, set-oracle-average 222-231 Pool owner by tx-sender, or DAO/extension; registry validates average Registry oracle config
set-threshold-x, set-threshold-y 232-241 Pool owner by tx-sender, or DAO/extension Registry thresholds
set-fee-rate-x, set-fee-rate-y 242-251 Pool owner by tx-sender, or DAO/extension Registry directional fees
create-pool 252-256 Open; sender not blocklisted; add-liquidity path must be unpaused Creates registry pool, transfers initial tokens, mints LP
add-to-position 257-278 Open; unpaused; positive liquidity; max Y bound Transfers X/Y to vault, updates registry, mints LP
reduce-position 279-301 Open; sender not blocklisted; unpaused; percent <= 1e8 Vault sends X/Y, registry updates, LP burns
swap-x-for-y, swap-y-for-x 302-359 Open; sender not blocklisted; unpaused; pool active; positive input; optional minimum User input to vault, vault output to user, reserve and registry updates
swap-helper, swap-helper-a/b/c 360-375 Same checks as selected nested swaps Executes one to four swaps; only final hop receives caller minimum

3. Post-condition coverage matrix

Public function Token movement Recommended caller post-conditions
Setters and pause None Bind exact contract/function and arguments; pool owners should reject unexpected intermediary calls
create-pool X and Y from sender to vault; LP tokens to sender Cap X/Y sent; require expected LP token minimum
add-to-position X and calculated Y from sender to vault; LP tokens to sender Cap X and Y sent; require LP token minimum; do not omit a meaningful max-dy
reduce-position LP tokens burned; X/Y from vault to sender Cap LP burn; require minimum X and minimum Y received
swap-x-for-y X from sender to vault; Y from vault to sender Cap X sent and require minimum Y received
swap-y-for-x Y from sender to vault; X from vault to sender Cap Y sent and require minimum X received
Multi-hop helpers Input token from sender; intermediate vault movements; final output to sender Cap initial input and require final output token minimum; bind all token principals and factors

The optional minimum-output arguments default to zero, so transaction post-conditions are an important independent protection when callers omit them.

4. Authority / access-control matrix

Capability Authorized principal Check
Pause/unpause all pool operations DAO or DAO extension is-dao-or-extension
Change a pool's block window, ratios, oracle settings, thresholds, or fees Pool owner by tx-sender, DAO, or DAO extension Repeated setter checks at lines 205-250
Create pool Any non-blocklisted transaction sender Line 254 plus registry uniqueness
Add liquidity Any transaction sender, including blocklisted senders No blocklist check in add-to-position
Remove liquidity Any non-blocklisted LP holder Lines 286-299
Swap Any non-blocklisted sender while unpaused and pool active Lines 319-328 / 348-357
Transfer from vault Approved DAO extension calling vault Vault authorization
Update registry pool accounting Approved DAO extension calling registry Registry authorization

The pool contract is expected to be an approved DAO extension, which is why it wraps registry/vault calls in as-contract.

5. Clarity best-practice review

Check Result
tx-sender vs contract-caller Pool-owner setters rely on tx-sender, exposing owner authority to intermediary-contract phishing; see ALEX-01
Panic unwraps reduce-position uses unwrap-panic on the LP balance read at line 286; see ALEX-04
Arithmetic overflow / underflow Dedicated fixed-point bounds exist. No exploitable arithmetic failure was proven. Registry permits arbitrary fee rates, creating a privileged configuration DoS risk; see ALEX-03
as-contract / principal escalation Used extensively and intentionally to make authorized registry/vault/LP calls. Caller-supplied traits remain constrained by registry pool identity and vault approved-token policy
Trait conformance Caller-supplied tokens must implement the local SIP-010 trait. Registry and vault authorization provide additional containment
Slippage Add liquidity has optional max Y; swaps have optional minimum output. Omitted values remove caller-level economic protection
Blocklist consistency Create, remove, and swap paths enforce blocklist; direct add-liquidity does not

6. Findings

ID Severity Function Line Finding Recommended fix
ALEX-01 Medium Ten pool-owner setters 202-251 Pool-owner authority is phishable through tx-sender. A malicious intermediary contract called by a pool owner retains that owner as tx-sender and can call any setter. It can change block windows, directional fees, ratios, thresholds, and oracle configuration. Registry fee setters are not capped, so the resulting configuration can disable or materially distort a pool. Require (is-eq contract-caller pool-owner) for the owner branch. If direct wallet calls must remain supported, require both tx-sender and contract-caller to equal the owner. Keep DAO-extension authorization as a separate explicit branch.
ALEX-02 Low add-to-position 257-278 Blocklisted principals can still add liquidity and receive LP tokens. create-pool, reduce-position, and both direct swap paths check is-blocklisted-or-default; add-to-position does not. This leaves a direct policy bypass and permits a blocked address to increase protocol exposure even though it cannot exit through this contract while blocked. Apply the same blocklist assertion at the start of add-to-position, or document why blocked liquidity additions are intentionally allowed.
ALEX-03 Low set-fee-rate-x/y and registry setters 242-251; registry 181-190 Directional fee rates have no upper bound. A pool owner or DAO can set fee rates at or above ONE_8, making net input zero and causing swaps in that direction to fail or behave unexpectedly. This amplifies ALEX-01 and makes accidental misconfiguration a pool availability risk. Enforce fee-rate-x < ONE_8 and fee-rate-y < ONE_8 in the registry, ideally with a stricter governance maximum.
ALEX-04 Informational reduce-position 286 A user-facing path uses unwrap-panic. Any error from the LP balance read aborts without the contract's structured error vocabulary, making integration failures harder to diagnose. Funds remain atomic and no loss was identified. Replace with unwrap! and a dedicated error code.
ALEX-05 Informational Swap and liquidity entrypoints 257, 302, 331, 360-375 Economic bounds are optional and default to permissive values. none means unlimited Y on add-liquidity or zero minimum output on swaps; helpers intentionally protect only the final hop. Integrators that omit bounds are exposed to price movement and sandwiching. Offer strict wrapper entrypoints requiring explicit bounds, and prominently document transaction post-conditions for all integrations.

ALEX-01 impact detail

In Clarity, tx-sender remains the original transaction initiator across contract calls, while contract-caller identifies the immediate caller. If a pool-owner wallet calls an untrusted contract, that contract can call set-end-block, set-fee-rate-x/y, or another setter while the AMM still sees the owner as tx-sender. The subsequent registry mutation executes through as-contract, so the registry correctly trusts the AMM extension and applies the attacker's chosen value.

The issue requires the pool owner to call an attacker-controlled contract in the same transaction. It does not grant arbitrary callers owner authority, so it is rated medium rather than high.

ALEX-02 impact detail

The omission is specific and observable: blocklist checks exist at lines 254, 294, 319, and 348, but not in add-to-position. A blocked address can call the function directly, transfer assets into the vault, and receive LP shares. Its ability to withdraw remains blocked by reduce-position, but the inconsistent policy can undermine compliance expectations and complicate remediation.

Positive security properties

  • Pool accounting and custody are separated into registry and vault contracts.
  • Vault transfers require approved-token and DAO-extension checks.
  • Swap paths enforce pause state, pool block windows, positive inputs, max in/out ratios, and optional minimum outputs.
  • Registry caps max input/output ratios and oracle average.
  • Every state-changing multi-contract workflow is atomic on error.
  • Fixed-point math includes exponent and product bounds.
  • No high or critical issue was found, so no private responsible-disclosure action was required before publishing this report.

Suggested tests

  1. Have a pool-owner wallet call a mock malicious contract that invokes every setter; verify current behavior succeeds, then verify the proposed contract-caller fix rejects it.
  2. Blocklist an address and prove direct add-to-position currently succeeds while create, reduce, and swap reject it.
  3. Set each directional fee to ONE_8 and above; document and test the desired governance bound.
  4. Force LP balance-read errors and verify reduce-position returns a structured error after replacing unwrap-panic.
  5. Property-test swap and liquidity post-conditions across all helper routes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment