Skip to content

Instantly share code, notes, and snippets.

@sato820
Created June 23, 2026 12:17
Show Gist options
  • Select an option

  • Save sato820/cbedbb077e65696994ddfe1993b08647 to your computer and use it in GitHub Desktop.

Select an option

Save sato820/cbedbb077e65696994ddfe1993b08647 to your computer and use it in GitHub Desktop.
AIBTC audit: Arkadiko Freddie v1-1 static analysis

Arkadiko Freddie v1-1 static-analysis audit

Target bounty: mqf84kgs5a5b6c995a80

Target contract: SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-freddie-v1-1

Source URL: https://api.hiro.so/v2/contracts/source/SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR/arkadiko-freddie-v1-1

Snapshot time: 2026-06-23

Source metadata:

  • Published at Stacks height: 34239
  • Source length: 46156 bytes, 1060 lines
  • Source SHA256: 9ab03ff419d4d527518ff3e443bc77727a545e4dfb45e70be98ca1d7eaccc689
  • Current DAO registry resolves:
    • collateral-types -> SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-collateral-types-v3-1
    • oracle -> SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-oracle-v2-3
    • stx-reserve -> SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-stx-reserve-v1-1
    • sip10-reserve -> SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-sip10-reserve-v2-1
    • auction-engine / liquidator -> SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-auction-engine-v4-5
  • Live read-only context:
    • arkadiko-vault-data-v1-1.get-last-vault-id returned 2869.
    • freddie.get-stx-redeemable returned 400000000000.
    • freddie.get-usda-balance returned 0.
    • freddie.get-diko-balance returned 0.
    • get-stability-fee-for-vault using the live collateral-types contract returned non-zero fees for active recent vaults:
      • vault 2852: 117427618
      • vault 2856: 44912640
      • vault 2864: 713227464
      • vault 2869: 79156176

Responsible disclosure note: I did not identify a high or critical severity issue in this public report. No private disclosure was triggered.

1. State model

arkadiko-freddie-v1-1 is an orchestration contract. Most vault state lives in arkadiko-vault-data-v1-1; collateral parameters, debt caps and stability fees live in the current DAO-selected collateral-types contract.

Constants

  • Lines 14-31 define error constants ERR-NOT-AUTHORIZED, transfer/mint/burn/deposit/withdraw/mint/liquidation failures, collateral/debt/stacking/liquidation/auction errors.
  • Line 34 defines BLOCKS-PER-DAY as u144.

Data vars and maps in Freddie

  • Line 36: stx-redeemable, amount of STX redeemable for xSTX after liquidated stacked collateral is released.
  • Line 37: block-height-last-paid, last foundation payout block used by redeem-tokens.
  • Line 38: maximum-debt-surplus, initialized to u10000000000000.
  • Lines 39-44: stacking-unlock-burn-height, maps stacker names to burnchain unlock heights.
  • Line 45: freddie-shutdown-activated, local emergency flag.

Vault state in arkadiko-vault-data-v1-1

The vault map contains:

  • id, owner
  • collateral, collateral-type, collateral-token
  • stacked-tokens, stacker-name, revoked-stacking, auto-payoff
  • debt
  • created-at-block-height, updated-at-block-height
  • stability-fee-accrued, stability-fee-last-accrued
  • is-liquidated, auction-ended, leftover-collateral

Important supporting state:

  • vault-entries maps a user principal to a list of up to 20 vault IDs.
  • closing-vault is a helper map keyed by user.
  • last-vault-id is the global ID counter.

Mutation authority

  • Freddie mutates vault records through arkadiko-vault-data-v1-1.update-vault, which allows the DAO-registered freddie, stacker-payer, or stacker contracts.
  • Freddie creates and closes entries through update-vault-entries, set-last-vault-id, and close-vault.
  • Collateral/debt totals are mutated through the DAO-selected collateral-types contract.
  • USDA mint/burn authority is delegated through arkadiko-dao.mint-token and arkadiko-dao.burn-token; DAO checks whether contract-caller has mint/burn permission.
  • Reserve custody is delegated to the current DAO-selected STX or SIP10 reserve.

2. Function inventory

Read-only functions

get-stx-redeemable lines 48-50

  • Authority: none.
  • Returns stx-redeemable.

get-stacking-unlock-burn-height lines 70-72

  • Authority: none.
  • Reads stacking-unlock-burn-height.
  • Uses unwrap-panic for missing stacker names.

get-vault-by-id lines 94-96

  • Authority: none.
  • Delegates to arkadiko-vault-data-v1-1.

get-vault-entries lines 98-100

  • Authority: none.
  • Delegates to arkadiko-vault-data-v1-1.

get-collateral-type-for-vault lines 102-106

  • Authority: none.
  • Reads a vault and returns collateral-type.

get-collateral-token-for-vault lines 108-112

  • Authority: none.
  • Reads a vault and returns collateral-token.

get-usda-balance lines 961-963

  • Authority: none.
  • Reads Freddie's USDA balance via as-contract.

get-diko-balance lines 965-967

  • Authority: none.
  • Reads Freddie's DIKO balance via as-contract.

Public user and protocol functions

set-stacking-unlock-burn-height lines 78-92

  • Authority: contract-caller must be one of DAO-registered stacker, stacker-2, stacker-3, stacker-4.
  • Mutates stacking-unlock-burn-height.

calculate-current-collateral-to-debt-ratio lines 120-152

  • Authority: oracle must match DAO registry; coll-type is only checked inside stability-fee-helper when fees are included.
  • Reads vault, oracle price, debt and optional fee.
  • No direct state mutation.

toggle-stacking lines 165-187

  • Authority: global shutdown must be false; tx-sender must be vault owner.
  • Requires STX vault, not liquidated.
  • Calls STX reserve and updates vault stacking flags.

stack-collateral lines 191-215

  • Authority: global shutdown false; tx-sender must be vault owner.
  • Requires STX vault, not liquidated, no existing stacked tokens.
  • Calls STX reserve and updates stacked-token fields.

release-stacked-stx lines 219-247

  • Intended authority per comment: deployer/contract owner.
  • Actual authority: no owner/deployer assertion.
  • Requires xSTX, liquidated vault, non-zero stacked tokens and burn-height >= configured unlock height.
  • Adds to stx-redeemable and zeroes stacked-tokens.

redeem-stx lines 255-267

  • Authority: any caller with xSTX to burn.
  • Burns xSTX and redeems STX up to min(stx-redeemable, ustx-amount).
  • Decrements stx-redeemable.

toggle-freddie-shutdown lines 269-275

  • Authority: tx-sender must be DAO guardian.
  • Toggles local shutdown flag.

collateralize-and-mint lines 288-371

  • Authority: shutdown false; coll-type, oracle, and reserve must match DAO registry; collateral token must match collateral-type.
  • Calculates initial ratio, verifies min collateral ratio and max-debt cap.
  • Calls reserve custody, mints USDA via DAO, creates vault, updates entries, rewards and collateral-type total debt.

deposit lines 380-427

  • Authority: shutdown false; reserve/collateral-types must match DAO registry; tx-sender must be owner.
  • Requires not liquidated and token match.
  • Calls reserve deposit, updates collateral and rewards.

withdraw lines 437-501

  • Authority: shutdown false; reserve/collateral-types/oracle must match DAO registry; tx-sender must be owner.
  • Requires not liquidated, positive amount, enough collateral, no stacked tokens and token match.
  • Verifies resulting collateral ratio, calls reserve withdraw, updates vault and rewards.

mint lines 510-581

  • Authority: shutdown false; reserve/collateral-types/oracle must match DAO registry; tx-sender must be owner.
  • Requires not liquidated, healthy ratio, max-debt cap.
  • Calls accrue-stability-fee, reserve mint, then updates vault debt and collateral-type debt.

burn lines 590-626

  • Authority: shutdown false; reserve/collateral-types must match DAO registry.
  • No owner check.
  • Pays stability fee from tx-sender, then burns min(debt, vault.debt) USDA from tx-sender through burn-partial-debt.
  • Does not transfer collateral.

close-vault lines 628-670

  • Authority: no tx-sender == owner check.
  • Requires no stacked tokens, collateral-types/reserve match and not liquidated.
  • Burns principal debt from the vault owner via DAO, withdraws all collateral to owner, subtracts debt, updates vault, removes rewards, then calls vault-data close.
  • Does not call pay-stability-fee.

get-stability-fee-for-vault lines 694-703

  • Authority: checks collateral-types inside helper.
  • Calculates fee from the vault's last-accrual height, debt, and collateral type.

accrue-stability-fee lines 729-745

  • Authority: collateral-types must match DAO registry.
  • Updates stability-fee-accrued and stability-fee-last-accrued.

pay-stability-fee lines 751-774

  • Authority: collateral-types must match DAO registry.
  • Transfers fee amount from tx-sender to Freddie's contract principal and resets fee fields.

liquidate lines 782-840

  • Authority: contract-caller must be DAO-registered liquidator.
  • Removes rewards/collateral accounting, computes penalty and auction discount, updates vault to liquidated state.
  • For stacked STX, changes collateral token to xSTX and mints xSTX.

finalize-liquidation lines 848-874

  • Authority: contract-caller must be DAO-registered auction engine.
  • Requires liquidated vault.
  • Sets auction-ended, stores leftover collateral and subtracts debt total.

redeem-auction-collateral lines 884-896

  • Authority: reserve must match DAO registry; contract-caller must be auction engine.
  • Calls reserve to transfer auction collateral to winning lot owner.

withdraw-leftover-collateral lines 904-954

  • Authority: shutdown false; reserve/collateral-types must match; tx-sender must be owner.
  • Requires liquidated, auction ended, no stacked tokens, token match.
  • Calls pay-stability-fee, then withdraws leftover collateral to owner and zeros leftover.

Admin functions

redeem-tokens lines 975-992

  • Authority: no owner/governance check; only requires 31 days since block-height-last-paid.
  • Updates block-height-last-paid before performing caller-specified USDA/DIKO transfers to DAO payout.

migrate-funds lines 1000-1010

  • Authority: contract-caller must be DAO owner.
  • Transfers Freddie's whole balance of a supplied token to the new vault-manager contract.

set-stx-redeemable lines 1015-1022

  • Authority: DAO owner.
  • Sets stx-redeemable.

set-block-height-last-paid lines 1024-1031

  • Authority: DAO owner.
  • Sets block-height-last-paid.

set-maximum-debt-surplus lines 1033-1040

  • Authority: DAO owner.
  • Sets maximum-debt-surplus.

migrate-state lines 1044-1053

  • Authority: DAO owner.
  • Calls setter functions on a supplied new vault manager.

3. Post-condition coverage matrix

Post-conditions cannot cover every cross-contract state write, but callers should attach them for token movement and vault-facing workflows.

Function Expected movements Recommended caller post-conditions
collateralize-and-mint Collateral leaves user/reserve custody path; USDA minted to user Assert exact collateral transfer from user, exact USDA increase to user, no unexpected token sends.
deposit Additional collateral leaves owner Assert exact collateral transfer from owner to approved reserve; no USDA/DIKO movement.
withdraw Collateral moves from reserve to owner Assert exact collateral received by owner; no USDA/DIKO movement.
mint USDA minted to owner Assert exact USDA increase by extra-debt; no collateral movement.
burn USDA and fee can be transferred/burned from tx-sender Assert USDA decrease does not exceed debt + expected fee; for non-owner callers, avoid calling unless intentionally paying another vault's debt.
close-vault Owner's principal debt is burned; all collateral returns to owner Owner should assert full collateral returned and USDA burned equals principal debt plus expected fees. Current code does not charge fees, so this is also a finding.
redeem-stx xSTX burned from caller; STX redeemed to caller Assert xSTX decrease and STX increase bounded by min(stx-redeemable, ustx-amount).
release-stacked-stx No direct caller token movement; stx-redeemable increases Protocol/keeper call should assert vault stacked-tokens becomes zero.
liquidate Rewards removed; collateral converted to auction accounting; possible xSTX mint Auction engine should enforce expected collateral and debt tuple before proceeding.
finalize-liquidation No direct token movement Auction engine should assert leftover value matches auction settlement.
redeem-auction-collateral Collateral transferred from reserve to auction winner Assert exact collateral to winning sender.
withdraw-leftover-collateral Fee paid by owner; leftover collateral moves to owner Assert fee paid and exact leftover collateral received.
redeem-tokens Freddie's USDA/DIKO transferred to DAO payout Governance/keeper call should assert exact token transfers; currently any caller can trigger it.
migrate-funds Whole token balance moves to new manager DAO migration should assert recipient is exact new manager and token balance becomes zero.
Setters/toggles No direct token movement No token post-condition required; require governance/guardian source.

4. Authority and access-control matrix

Surface Required authority in code Notes
Global shutdown tx-sender == arkadiko-dao.get-guardian-address Guardian can toggle local Freddie shutdown.
DAO-owner admin setters contract-caller == arkadiko-dao.get-dao-owner Used by migration and local variable setters.
Stacker unlock heights contract-caller one of DAO-registered stackers Strong contract-caller based check.
Vault owner actions tx-sender == vault.owner on toggle/stack/deposit/withdraw/mint/withdraw-leftover burn and close-vault are exceptions.
Liquidation contract-caller == DAO liquidator/auction-engine Uses DAO registry; current registry points both liquidator and auction-engine to arkadiko-auction-engine-v4-5.
Reserve/collateral/oracle dependencies Must match DAO registry Some contract arguments are evaluated before all auth checks, but registry checks exist before state updates.
Foundation payout Only 31-day time gate No governance caller check in redeem-tokens.
release-stacked-stx No deployer/owner check despite comment Any principal can trigger once burn-height gate passes.

5. Clarity and CDP best-practice review

  • tx-sender owner checks are used for end-user vault mutation. This is consistent across owner functions but blocks smart-contract wallets and managers. The larger issue is inconsistent absence of the check on close-vault and burn.
  • unwrap-panic is used on DAO registry, map reads, price reads and helper calls in user-facing paths. This creates runtime panics instead of typed errors for malformed vault IDs, missing stacker names and registry/oracle failures.
  • Arithmetic uses raw +, -, *, /. Current live values are well below uint128 limits, but fee and ratio calculations can still abort if governance parameters or debt/collateral grow without bounded checked math.
  • as-contract is used for DAO minting and collecting stability fees into Freddie. The direction is intended, but it means post-conditions should be attached to all fee and payout flows.
  • Trait conformance is broad: reserve and collateral-type traits include state-mutating functions. Registry checks prevent unauthorized state updates, but some external trait calls are in let bindings before every assertion has run.
  • Liquidation flow is access-controlled by DAO registry, but fee collection is uneven: withdraw-leftover-collateral pays fees, while close-vault does not.

6. Findings table

ID Severity Function Line Finding Recommended fix
ARK-01 Medium close-vault, arkadiko-vault-data-v1-1.close-vault Freddie 628-670; vault-data 147-156 close-vault has no owner authorization, and the downstream data contract removes the closed ID from vault-entries under tx-sender rather than the vault owner. A non-owner can force-close an unstacked, non-liquidated vault if the owner has burnable USDA, receive no funds, but destroy the position, return collateral to the owner, and leave the owner's vault-entries pointing at a deleted/default vault. Add asserts! (is-eq tx-sender (get owner vault)) before any burn/withdraw/update. In vault-data, update vault-entries for (get owner vault), not raw tx-sender, or pass the owner explicitly from Freddie.
ARK-02 Medium mint, accrue-stability-fee 517-523, 564-577, 737-740 mint computes updated-vault from the pre-accrual vault, then calls accrue-stability-fee, then writes the stale updated-vault. This can discard the fee just accrued in the same transaction. Separately, accrue-stability-fee sets stability-fee-accrued to only the newly calculated interval, rather than adding to any prior accrued amount. Live read-only calls show non-zero current fees for active vaults, e.g. vault 2869 returns 79156176. After accruing, re-read the vault or merge debt into the post-accrual record. Change accrual to (+ (get stability-fee-accrued vault) newly-calculated-fee). Add regression tests covering two accruals and a mint after fee accumulation.
ARK-03 Medium close-vault 658-667 Closing a vault burns only principal debt and withdraws all collateral without pay-stability-fee. Owners can close an unstacked vault and avoid current and accrued stability fees. When combined with ARK-01, a non-owner caller can trigger this fee bypass while closing someone else's eligible vault. Call pay-stability-fee before burning principal debt, or include principal plus current/accrued fees in a single settlement path. Make the close path use the same fee semantics as burn and withdraw-leftover-collateral.
ARK-04 Low release-stacked-stx 217-245 The comment says the method can only be called by the deployer/contract owner, but the code has no owner/deployer/DAO assertion. Any caller can release liquidated xSTX collateral after burn-height unlock, moving value into stx-redeemable and zeroing stacked-tokens. This is not direct theft, but it removes sequencing control from the protocol. Add the intended deployer/DAO/auction-engine authorization or update the comment and document this as a permissionless keeper action. Emit an event identifying the vault and released amount.
ARK-05 Low redeem-tokens 975-992 Foundation payout is permissionless after the 31-day gate and accepts caller-supplied USDA/DIKO amounts. It updates block-height-last-paid before transfers. If zero-value or dust-value transfers succeed for the token implementation, any caller can reset the payout clock without meaningful payout; even without zero-value success, the caller controls payout timing. Restrict to DAO owner/governance/keeper or require positive, bounded amounts based on actual Freddie balances before updating the clock. Move block-height-last-paid update after successful transfers.
ARK-06 Low get-stacking-unlock-burn-height, DAO lookups, helper paths 70-72 and repeated Missing map entries and DAO/oracle/helper failures use unwrap-panic, producing runtime failures rather than typed errors. A live call to get-stacking-unlock-burn-height("missing") returned UnwrapFailure. Replace unwrap-panic in user-facing/read paths with unwrap! and explicit error constants.

Additional notes

  • burn has no owner check, but it burns USDA from tx-sender, not the vault owner. This allows third parties to voluntarily pay another vault's fees/debt. I rate it as a design inconsistency rather than a direct vulnerability unless the project intended all vault mutations to be owner-only.
  • The current DAO registry is not the same as the v1-1 contracts named in the original deployment. Freddie's registry checks correctly route to the currently configured v3-1 collateral-types and v2-1 SIP10 reserve where applicable.
  • Several active recent vaults have non-zero current fees by read-only calculation while stored stability-fee-accrued remains zero. That is not itself a bug, but it makes the fee-accounting findings economically relevant.

Top 3 summary

  1. close-vault lacks owner authorization and downstream vault-data cleanup uses tx-sender, so a non-owner can force-close eligible vaults and leave stale owner entries.
  2. Stability fee accrual is lossy: mint overwrites the accrual it just triggered, and repeated accrual replaces rather than accumulates prior fee.
  3. close-vault burns only principal debt and never calls pay-stability-fee, allowing current/accrued fees to be skipped on closure.

No high or critical findings were identified; private disclosure was not triggered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment