stakied-pt-yt-amm
๐ Security Audit: Stakied PT/YT AMM
Overview
Stakied is a yield tokenization protocol for Stacks, inspired by Pendle Finance. The AMM contract enables trading Principal Tokens (PT) against Standardized Yield (SY) tokens. The protocol splits yield-bearing assets into PT (fixed yield) and YT (variable yield), with this AMM providing PT/SY trading liquidity.
Findings Summary
| Severity | Count |
|---|---|
| Critical | 0 |
| High | 1 |
| Medium | 3 |
| Low | 3 |
| Informational | 3 |
Findings
H-01: Time-Decay Curve Not Applied to Swap Pricing
HIGH | swap-pt-for-sy, swap-sy-for-pt
The contract advertises a "Time-Decay Curve" AMM but swap functions use a standard constant-product (xยทy=k) formula with no time-weighting. The calculate-time-factor function exists as read-only but is never referenced in any swap calculation.
In a yield tokenization AMM (like Pendle), the pricing curve must incorporate time-to-maturity so that PT price converges to 1:1 with SY at maturity. Without this, the AMM fails at its core purpose.
;; Swap formula used (standard constant-product, no time factor):
(sy-out (/ (* sy-reserve pt-after-fee) (+ pt-reserve pt-after-fee)))
Impact: PT will not converge to face value at maturity. The AMM functions as a generic constant-product DEX, not a yield tokenization AMM. Users trading near maturity get incorrect prices.
Recommendation: Implement a time-weighted invariant curve. Apply calculate-time-factor to bias pricing toward 1:1 as maturity approaches (e.g., Pendle-style x^(1-t) + y^(1-t) = k).
M-01: No Maturity Validation in Pool Initialization
MEDIUM | initialize-pool
The function does not validate that maturity > block-height. Pools can be created for already-passed maturities.
;; Missing check:
;; (asserts! (> maturity block-height) err-invalid-maturity)
Impact: Expired-maturity pools waste gas and clutter state. If PT tokens for that maturity exist, trades occur that shouldn't.
Recommendation: Add (asserts! (> maturity block-height) err-invalid-maturity).
M-02: Dead Pool After Full Liquidity Removal
MEDIUM | remove-liquidity, initialize-pool
When all liquidity is removed, the pool entry persists with zero reserves/LP. Re-initialization is blocked by is-none check. add-liquidity would divide by zero (total-lp = 0), making the pool permanently unusable.
Impact: Maturity slots permanently locked after full withdrawal.
Recommendation: Delete pool entry when total-lp-supply reaches 0, or allow re-initialization when reserves are zero.
M-03: Uses as-contract Instead of Clarity 4 as-contract?
MEDIUM | Throughout contract
The contract uses pre-Clarity 4 as-contract which grants blanket asset access. Clarity 4's as-contract? with explicit with-ft/with-stx allowances is strictly safer.
Impact: A malicious token contract called under as-contract scope could drain any assets held by the AMM.
Recommendation: Migrate to Clarity 4 and use as-contract? with explicit asset allowances.
L-01: LP Tokens Not Transferable
LOW | lp-balances map
LP positions stored in a map with no transfer function. Holders cannot sell, transfer, or compose positions.
Recommendation: Add a transfer-lp function.
L-02: No Post-Maturity Pool Settlement
LOW | Entire contract
After maturity, swaps continue with constant-product pricing. No mechanism converts PT reserves to SY or settles the pool.
Recommendation: Add a settle-pool function that redeems PTโSY after maturity.
L-03: Oracle Contract Not Integrated
LOW | Entire contract
The stakied-oracle contract exists but the AMM never references it. No price bounds or manipulation protection.
Recommendation: Integrate oracle price bounds as a circuit breaker for swaps.
I-01: Time Factor Uses Genesis-Relative Calculation
INFORMATIONAL | calculate-time-factor
Computes block-height / maturity โ progress from genesis rather than pool creation time.
I-02: sqrt-i Limited to 8 Iterations
INFORMATIONAL | sqrt-i
Newton's method with 8 unrolled iterations. Adequate for realistic AMM values but may not converge for values near uint max (~2^128).
I-03: No Access Control on Pool Initialization
INFORMATIONAL | initialize-pool
Anyone can create pools for any maturity and set initial price ratios. A malicious actor could front-run legitimate pool creation with a skewed ratio.
Top Recommendations
- Implement time-weighted swap curve (H-01) โ Core value proposition of a yield tokenization AMM.
- Add maturity validation and dead pool recovery (M-01 + M-02) โ Prevent expired pools and handle edge cases.
- Migrate to Clarity 4 (M-03) โ Use
as-contract?for defense-in-depth.