StacksYield Elite — Multi-Tier Staking Protocol

sienna-enzo/stacks-yield-elite · Staking & Yield Vault · February 21, 2026

10
Findings
2
Critical
3
High
3
Medium
2
Low

Overview

StacksYield Elite is a multi-tier liquid staking protocol where users deposit STX and earn YIELD-ANALYTICS-TOKEN rewards. It features three tiers (Explorer, Pioneer, Titan) with escalating yield multipliers, time-lock bonuses up to 3x, a cooldown-based withdrawal mechanism, and an on-chain governance system with weighted voting.

The contract is ~400 lines of Clarity in a single file. It handles real STX transfers, making the bugs especially consequential. The protocol is non-functional in its current state — staked funds cannot be withdrawn and rewards cannot be minted.

Findings

CRITICAL C-1: Complete Withdrawal Sends STX to Itself

The withdrawal function attempts to return STX to the user but sends it back to the contract:

(define-public (complete-withdrawal)
  (let (
      (stake-position (unwrap! (map-get? StakingPositions tx-sender) ERR-NO-STAKE))
      (cooldown-start (unwrap! (get cooldown-initiated stake-position) ERR-NOT-AUTHORIZED))
      (withdrawal-amount (get staked-amount stake-position))
    )
    ;; ...cooldown check...
    (try! (as-contract (stx-transfer? withdrawal-amount tx-sender tx-sender)))
    ;; ...cleanup...
  )
)

Inside as-contract, both occurrences of tx-sender resolve to the contract's own principal. The STX transfer goes from contract → contract. The user's position is deleted but they receive nothing.

Impact: All staked STX is permanently locked. No user can ever withdraw.

Fix: Capture the user's principal before entering as-contract:

(let ((user tx-sender))
  ;; ...
  (try! (as-contract (stx-transfer? withdrawal-amount tx-sender user)))
)

CRITICAL C-2: Stake Overwrites Instead of Accumulating

When a user stakes additional tokens, the staking position is overwritten rather than accumulated:

(map-set StakingPositions tx-sender {
  staked-amount: amount,  ;; ← should be updated-stake-total
  stake-start-block: stacks-block-height,
  ...
})

The UserPositions map correctly tracks updated-stake-total, but StakingPositions stores only the latest deposit. Previous deposits remain locked in the contract but are no longer tracked against the user.

Impact: Repeated staking silently orphans previously staked STX. A user staking 1M then 500K loses visibility of the first 1M — it's still in the contract but withdrawal can only recover 500K.

HIGH H-1: Fungible Token Has Zero Max Supply

(define-fungible-token YIELD-ANALYTICS-TOKEN u0)

The second argument to define-fungible-token is the maximum supply. With u0, the token has a max supply of zero. Every call to ft-mint? will fail because minting any amount exceeds the cap.

Impact: claim-staking-rewards always reverts. No rewards can ever be distributed. The entire yield mechanism is broken.

Fix: Either remove the supply cap or set a meaningful maximum: (define-fungible-token YIELD-ANALYTICS-TOKEN)

HIGH H-2: Yield Multiplier Overflow (Up to 90,000x)

(let (
    (final-yield-multiplier (* (get yield-multiplier tier-details) time-lock-multiplier))
  )

Both tier yield-multiplier (up to 300 for Titan) and time-lock-multiplier (up to 300 for 120-day lock) are expressed in basis points where 100 = 1x. Multiplying them gives up to 90,000 — which is 900x, not 9x as intended.

Impact: Titan-tier users with 120-day locks get 900x yield multiplier instead of 9x. Reward calculation produces absurdly inflated values. (Moot currently due to H-1, but would be exploitable if the FT bug were fixed.)

Fix: (/ (* tier-mult time-lock-mult) u100)

HIGH H-3: Lock Duration Not Enforced on Withdrawal

Neither initiate-withdrawal nor complete-withdrawal checks whether the lock period has elapsed:

;; initiate-withdrawal checks:
;; ✓ sufficient balance
;; ✓ no existing cooldown
;; ✓ not emergency mode
;; ✗ lock-duration elapsed? — NOT CHECKED

;; complete-withdrawal checks:
;; ✓ cooldown period complete
;; ✗ lock-duration elapsed? — NOT CHECKED

Impact: A user can stake with a 120-day lock (receiving the maximum 3x yield multiplier) then immediately initiate withdrawal. They get the enhanced rewards without honoring the lock commitment. The time-lock incentive structure is meaningless.

MEDIUM M-1: Governance Power Persists Through Cooldown

initiate-withdrawal starts the cooldown but doesn't reduce governance power. During the entire cooldown period (2,160 blocks / ~36 hours), the user retains full voting weight despite having signaled their intent to exit. They can vote on proposals with power derived from stake they're withdrawing.

Impact: Exiting stakers retain governance influence during cooldown, allowing last-minute voting before withdrawal completes.

MEDIUM M-2: Deposit-Only Economics — No Token Redemption

Users deposit real STX but earn YIELD-ANALYTICS-TOKEN. There is no mechanism to:

The yield token has no utility beyond governance voting weight (which is also calculated from STX stake, not token balance). The "yield" is entirely illusory — users get a number in a map but no recoverable value.

MEDIUM M-3: Partial Withdrawal Amount Discarded

initiate-withdrawal accepts a withdrawal-amount parameter and validates it against the staked balance, but never stores it. complete-withdrawal always withdraws the full staked-amount:

;; initiate-withdrawal: validates withdrawal-amount but only sets cooldown flag
;; complete-withdrawal: reads staked-amount from position (ignores original request)

Impact: Users cannot partially withdraw. The parameter creates a false expectation of partial withdrawal support. All-or-nothing withdrawals force users to fully exit and re-stake to reduce position size.

LOW L-1: Tier Configuration Requires Manual Init

TierConfiguration entries don't exist until the owner calls initialize-protocol. Staking works before initialization because calculate-tier-assignment uses hardcoded values — but the TierConfiguration map entries that governance or UI might query will return none.

Additionally, initialize-protocol can be called multiple times, allowing the owner to silently change tier parameters.

LOW L-2: Centralized Admin with No Timelock

The contract owner can:

None of these actions require governance approval or have a timelock delay. The governance system exists but has no power over protocol parameters — it's purely advisory.

Architecture Assessment

AspectAssessment
STX CustodyBROKEN Funds go in but can't come out (C-1)
Reward DistributionBROKEN Zero-supply FT blocks all minting (H-1)
Staking AccountingBROKEN Overwrites lose track of deposits (C-2)
Time-Lock EnforcementBROKEN Never checked on withdrawal (H-3)
Governance SystemWEAK Functional voting but no execution power
Tier SystemOK Hardcoded fallback works but map is decorative
Access ControlCENTRALIZED Owner has full unilateral control

Verdict

StacksYield Elite is non-functional as deployed. The two critical bugs (self-transfer withdrawal, stake overwrite) mean funds deposited into this contract cannot be recovered. The zero-supply fungible token means no rewards can be distributed. The time-lock system provides yield bonuses but doesn't enforce the lock.

The governance system is the most complete subsystem — proposal creation, weighted voting, and double-vote prevention all work correctly. But governance has no teeth: it can't modify protocol parameters or execute any on-chain actions.

The core pattern here is a contract that looks sophisticated on the surface (tiers, multipliers, governance, cooldowns) but fails at the fundamental operations: depositing correctly, withdrawing at all, and distributing rewards.