Vault Manager โ Stablecoin Collateral/Debt Management
| Contract | SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-freddie-v1-1 |
|---|---|
| Source | Stacks Explorer ยท Hiro API (on-chain) |
| Lines of Code | 1,060 |
| Clarity Version | 1 (pre-Clarity 4) โ ๏ธ |
| Auditor | cocoa007.btc |
| Date | 2026-02-24 |
| Confidence | Medium โ complex multi-contract system, some cross-contract interactions unverifiable without full DAO source |
Arkadiko Freddie is the vault manager for the Arkadiko protocol โ a MakerDAO-style stablecoin system on Stacks. Users deposit STX (or SIP-010 tokens) as collateral to mint USDA stablecoins. The contract manages vault lifecycle: creation, collateral deposits/withdrawals, debt minting/burning, stability fee accrual, liquidation, and post-liquidation settlement.
The contract interacts extensively with the Arkadiko DAO registry for access control, external reserves for custody, an oracle for pricing, and collateral-type configuration. This multi-contract architecture provides modularity but expands the trust surface considerably.
| Metric | Score | Weighted |
|---|---|---|
| Financial risk | 3 (DeFi lending/staking) | 9 |
| Deployment likelihood | 3 (deployed mainnet) | 6 |
| Code complexity | 3 (1060 lines, multi-contract) | 6 |
| User exposure | 3 (well-known Stacks DeFi protocol) | 4.5 |
| Novelty | 1 (DeFi vault โ similar category) | 1.5 |
| Total | 2.7 (โ0.5 Clarity 1 penalty = 2.2) โ | |
Location: close-vault (line ~520)
Description: The close-vault function burns the vault's debt and returns collateral but does not call pay-stability-fee. By contrast, the burn function (line ~470) explicitly calls (try! (pay-stability-fee vault-id coll-type)) before burning debt. A vault owner can avoid all accrued stability fees by calling close-vault instead of burn followed by collateral withdrawal.
;; close-vault โ burns debt directly, no fee payment
(if (is-eq (get debt vault) u0)
true
(try! (contract-call? .arkadiko-dao burn-token .usda-token (get debt vault) (get owner vault)))
)
(try! (contract-call? reserve burn ft (get owner vault) (get collateral vault)))
;; ^^^ stability fee never collected!
Impact: Loss of protocol revenue. Vault owners can systematically avoid stability fees by closing and re-opening vaults, undermining the protocol's economic model. For high-debt vaults, this could represent significant uncollected fees.
Recommendation: Add (try! (pay-stability-fee vault-id coll-type)) before burning debt in close-vault, matching the pattern in burn.
Location: redeem-tokens (line ~940)
Description: The redeem-tokens function transfers USDA and DIKO held by the contract to the DAO payout address. The only guard is a 31-day cooldown check โ anyone can call it. While funds go to the DAO payout address (not the caller), this gives any user the power to trigger treasury operations.
(define-public (redeem-tokens (usda-amount uint) (diko-amount uint))
(begin
;; Only checks time, not caller identity
(asserts! (> (- block-height (var-get block-height-last-paid)) (* BLOCKS-PER-DAY u31)) (err ERR-NOT-AUTHORIZED))
(var-set block-height-last-paid block-height)
;; ... transfers to payout address
Impact: A griefing attack could repeatedly trigger small payouts (e.g., 1 micro-USDA) to reset the 31-day timer, preventing the DAO from making a meaningful withdrawal. The attacker specifies the usda-amount and diko-amount parameters.
Recommendation: Add (asserts! (is-eq tx-sender (contract-call? .arkadiko-dao get-dao-owner)) (err ERR-NOT-AUTHORIZED)) or restrict to guardian address.
Location: stability-fee-helper (line ~750)
Description: Stability fees are calculated as simple linear multiplication: (* number-of-blocks interest). Standard DeFi protocols use compound interest. This means fees grow linearly with time rather than exponentially, resulting in systematically lower-than-expected fee collection.
(let (
(number-of-blocks (- block-height stability-fee-last-accrued))
(fee (get stability-fee collateral-type))
(decimals (get stability-fee-decimals collateral-type))
(interest (/ (* debt fee) (pow u10 decimals)))
)
(ok (* number-of-blocks interest)) ;; linear, not compound
)
Impact: Protocol collects less revenue than expected. For long-lived vaults (months/years), the difference between linear and compound interest grows substantially. This is arguably a design decision but deviates from DeFi norms and could mislead users expecting standard compounding behavior.
Recommendation: Document this behavior clearly. If compounding is desired, implement per-block compounding or periodic accrual checkpoints.
as-contract Authority (Pre-Clarity 4)Location: Multiple โ lines ~310, ~940, ~960, ~975, ~990
Description: The contract uses as-contract (Clarity 1) which grants blanket authority over all assets the contract holds. Any function that runs code under as-contract can transfer any token the contract owns. In Clarity 4, as-contract? with explicit asset allowances (with-ft, with-stx, etc.) enforces at the language level which assets can move.
Impact: If any of the external contracts called under as-contract (like .usda-token transfer, .arkadiko-token transfer) were compromised or if the DAO registry were manipulated to point to malicious implementations, the attacker could drain all assets held by this contract.
Recommendation: Migrate to Clarity 4 and use as-contract? with explicit with-ft / with-stx allowances for each as-contract block. This is the single most impactful safety upgrade available.
Location: release-stacked-stx (line ~225)
Description: Despite the comment "method that can only be called by deployer (contract owner)", the function has no caller check. Anyone can call it for any liquidated xSTX vault once the burn height is reached. The function adds the stacked tokens to the redeemable pool.
;; method that can only be called by deployer (contract owner)
;; ^^^ comment is incorrect โ no tx-sender check exists
(define-public (release-stacked-stx (vault-id uint))
(let ((vault (get-vault-by-id vault-id)))
;; checks: shutdown, xSTX token, is-liquidated, stacked > 0, burn-height reached
;; NO caller check
(try! (add-stx-redeemable (get stacked-tokens vault)))
...
Impact: Low โ the function is likely designed as a public good (anyone can trigger the release once conditions are met). However, the misleading comment suggests the developer intended access control. If early release timing matters for the protocol, this could be exploited.
Recommendation: Either add the deployer check implied by the comment, or correct the comment to document the intentional public access.
Location: subtract-stx-redeemable (line ~60)
Description: The function performs unchecked subtraction: (- (var-get stx-redeemable) token-amount). In Clarity, unsigned integer underflow causes a runtime panic that aborts the transaction. The caller (redeem-stx) uses min-of to cap the amount, which currently prevents underflow. However, if any future caller omits this guard, transactions will fail silently.
Impact: Low โ currently mitigated by min-of in redeem-stx. A defensive check would be more robust.
Recommendation: Add (asserts! (>= (var-get stx-redeemable) token-amount) (err ERR-INSUFFICIENT-COLLATERAL)) before the subtraction.
Location: Throughout (30+ instances)
Description: The contract uses unwrap-panic extensively for DAO registry lookups, vault data reads, and collateral type queries. Each unwrap-panic on a none result aborts the entire transaction with no descriptive error. If any external contract returns unexpected none (e.g., due to migration, data corruption, or misconfiguration), affected functions become uncallable.
Recommendation: Replace critical unwrap-panic calls with unwrap! and descriptive error codes to aid debugging and provide graceful failure.
Location: set-stacking-unlock-burn-height (line ~72) and initialization (line ~1050)
Description: The contract hardcodes exactly 4 stacker names ("stacker", "stacker-2", "stacker-3", "stacker-4") in both the authorization check and the initialization block. Adding more stackers requires deploying a new version of the contract.
Recommendation: Use a map-based allowlist that can be updated via governance, or accept this as a known limitation of v1.
Location: calculate-current-collateral-to-debt-ratio (line ~110)
Description: The ratio calculation uses nested integer division which can lose precision:
(/ (/ (* collateral price) debt) (/ decimals u100)). Integer division truncates, and performing it twice compounds the precision loss. For vaults near the liquidation threshold, this could cause the ratio to appear slightly lower than actual, potentially triggering premature liquidation or preventing valid operations.
Recommendation: Restructure to minimize division steps: (/ (* collateral price 100) (* debt decimals)).
arkadiko-dao via get-qualified-name-by-name. This is powerful (contracts are upgradeable) but creates a single point of trust โ if the DAO owner is compromised, all registered contracts can be swapped for malicious versions.vault-trait, ft-trait, oracle-trait, etc.) allows flexible composition but requires careful validation of each trait implementor. The contract correctly validates trait contracts against the DAO registry in most functions.freddie-shutdown-activated independently.migrate-funds and migrate-state functions enable smooth upgrades but are powerful โ restricted to DAO owner only.Audit by cocoa007.btc ยท Full audit portfolio ยท Source verified on-chain via Hiro API