Skip to content

Instantly share code, notes, and snippets.

@BenItBuhner
Created June 3, 2026 05:16
Show Gist options
  • Select an option

  • Save BenItBuhner/cce4b4bdb7383e2ba13033c7dc4b28a3 to your computer and use it in GitHub Desktop.

Select an option

Save BenItBuhner/cce4b4bdb7383e2ba13033c7dc4b28a3 to your computer and use it in GitHub Desktop.
AIBTC mpwj1rjde88d5b53b990 Zest Pool Borrow v2-3 static analysis

Zest pool-borrow-v2-3 Static Analysis

Scope

Contract: SP2VCQJGH7PHP2DJK7Z0V48AGBHQAW3R3ZW1QF4N.pool-borrow-v2-3

Reviewed source: https://api.hiro.so/v2/contracts/source/SP2VCQJGH7PHP2DJK7Z0V48AGBHQAW3R3ZW1QF4N/pool-borrow-v2-3

Source SHA-256: 61bddd761b3168df35d5d4ba7a4c55ec1ddb2f7bdffa73fa4e8bef9cfe2994fd

Referenced deployed contracts and traits were reviewed to verify delegated authority and asset movement:

  • .pool-0-reserve-v2-0
  • .liquidation-manager-v2-3
  • .flash-loan-trait
  • .ft-trait
  • .a-token-trait
  • .oracle-trait
  • .redeemeable-trait-v1-2

This is a static analysis of the deployed Zest lending-pool orchestrator and the immediately referenced contracts needed to explain its trust boundary. It found no high- or critical-severity issue requiring private disclosure before publication.

1. State Model

Local state

Lines Store Purpose
47 users-id Numeric entry to user principal map.
49 last-user-id Next numeric user-entry ID.
623 configurator Principal authorized to mutate pool and integration configuration.
992 approved-contracts Contract-caller allowlist guarding user-facing orchestrator and flashloan entry points.

Delegated state

Contract State role
.pool-0-reserve-v2-0 Reserve state, user reserve balances, supplied assets, e-mode state, isolated assets, flashloan fees, reserve transfers, and lending-pool integration authority.
.pool-reserve-data* Underlying reserve, grace-period, e-mode, and isolation-debt data stores.
.liquidation-manager-v2-3 Health checks, liquidation arithmetic, reserve updates, debt repayment, and collateral transfer.
LP and a-token contracts Supply balances, mint, burn, cumulative balances, and optional liquidation transfer.

2. Function Inventory

User and liquidation functions

Lines Function Authority and checks Mutations and external effects
59-123 supply Approved contract-caller; positive amount; active and unfrozen reserve; matching LP token; owner == tx-sender; supply cap. May enable first collateral, mints LP, transfers underlying to reserve, appends user-entry row.
125-147 validate-use-as-collateral Read-only helper. Checks LTV, isolation mode, and existing collateral list.
149-197 withdraw Approved caller; validated complete asset list; matching LP and oracle; owner == tx-sender; positive balance; reserve liquidity; health check. Burns LP, disables emptied collateral, updates reserve, returns underlying.
199-283 borrow Approved caller; validated complete asset list; owner == tx-sender; active reserve; borrowing enabled; available liquidity; isolation and e-mode checks; collateral and borrow caps. Updates reserve debt and transfers borrowed underlying.
285-421 Isolation helpers Internal/read-only. Tracks isolated-asset debt ceilings and pricing.
423-472 repay Approved caller; debt exists; unfrozen reserve; positive amount; payer == tx-sender. Updates reserve debt and transfers repayment to reserve.
474-522 liquidation-call Approved caller; validated complete asset list; unfrozen reserves; matching a-token and oracles. Delegates liquidation, then reduces isolated debt.
525-552 get-assets, validate-assets, check-assets Read-only. Requires caller-supplied asset list to exactly match configured ordering, LP tokens, and oracles.
554-621 flashloan-liquidation-step-1, flashloan-liquidation-step-2 Approved caller; liquidity; non-zero fees; active, unfrozen, flashloan-enabled reserve. Step 1 transfers reserve assets to receiver. Step 2 transfers principal plus fee back and updates reserve accounting.

Configuration and collateral-mode functions

Lines Function group Authority and effects
625-633 set-configurator, is-configurator Current configurator can replace itself.
635-711 E-mode selection helpers Approved caller plus tx-sender == user; validates assets, mode-compatible debt/collateral, and resulting health.
713-784 set-user-use-reserve-as-collateral and getters Approved caller plus tx-sender == who; validates reserve, LP, oracle, isolation, and health.
786-870 init, set-reserve, set-borrowing-enabled Configurator-only reserve creation and mutation.
872-989 Reserve, isolation, freeze, grace, and e-mode setters/getters Configurator-only delegated configuration.
994-1005 set-approved-contract, is-approved-contract Configurator controls the contract-caller allowlist.

3. Post-Condition Coverage Matrix

Public function Token movement Recommended caller post-conditions
supply Sends underlying and receives LP tokens. Cap underlying sent and require minimum LP tokens received.
withdraw Burns LP tokens and receives underlying. Cap LP burn and require minimum underlying received.
borrow Receives borrowed underlying. Require expected underlying received and verify asset principal.
repay Sends debt underlying. Cap asset sent and verify on-behalf-of principal.
liquidation-call Sends debt asset and receives collateral or a-token. Cap debt sent and require minimum collateral received.
Flashloan steps Step 1 sends reserve asset to receiver; step 2 sends principal plus fee back. Expose only through a wrapper that executes borrow, liquidation, repayment, and reserve-accounting update atomically. Verify receiver, asset, principal, and fee before dispatch.
Configuration setters No direct user asset movement. Verify target reserve, allowlist principal, and full replacement state before configurator execution.

4. Authority and Access-Control Matrix

Surface Authority Notes
User supply, withdraw, borrow, repay, liquidation, e-mode, collateral toggle, and flashloan steps Allowlisted contract-caller Most user paths additionally bind owner or payer to tx-sender.
Approved-caller list Local configurator An approved integration receives broad access to user-flow and flashloan primitives.
Reserve mutation Local configurator through .pool-0-reserve-v2-0 Reserve contract independently requires lending-pool or configurator authority depending on function.
Reserve transfer out Lending pool or liquidator in .pool-0-reserve-v2-0 pool-borrow-v2-3 is configured as lending pool and can dispatch reserve payouts.
Liquidation execution pool-borrow-v2-3 into .liquidation-manager-v2-3 Liquidation manager independently requires its configured lending-pool caller.
Configurator replacement Current local configurator Single-step transfer in this orchestrator; referenced reserve contract uses staged transfer for its own configurator.

5. Clarity Best-Practice Review

Flashloan trust boundary

The flashloan interface is split into two independently callable public functions. Step 1 transfers reserve assets to an arbitrary receiver and returns success. Step 2 later collects principal plus fee and updates reserve state. The supplied flashloan-script trait parameter is unused in both functions. Atomic repayment therefore depends entirely on every approved caller exposing only a safe wrapper sequence.

Caller binding

User flows generally require an approved contract-caller, then bind owner, payer, or who to tx-sender. This prevents an approved helper from silently mutating another user's position. The allowlist remains security-critical because flashloan step 1 has no corresponding sender binding or repayment coupling.

Asset-list validation

Health-sensitive flows require the supplied asset list to exactly match the configured asset ordering and each reserve's LP token and oracle. This reduces caller-controlled omission and oracle-substitution risk.

Configuration replacement

set-reserve permits the configurator to replace the complete reserve tuple. Other setters mutate isolation, e-mode, freeze, and grace state. Operational processes should treat configurator compromise as a protocol-level trust risk and monitor allowlist and reserve-state changes.

Panic operations

Several bounded-list and collateral-validation paths use unwrap-panic. Transactions revert atomically, but structured errors would improve monitoring and integration diagnostics.

6. Findings Table

ID Severity Function Line Finding Recommended fix
ZEST-01 Medium flashloan-liquidation-step-1, flashloan-liquidation-step-2 554-621 Flashloan payout and repayment are separate public calls. Step 1 transfers amount reserve assets to an arbitrary receiver and returns success without invoking a callback, requiring step 2 in the same transaction, or binding a debt record. Both functions accept flashloan-script but never use it. The path is restricted to approved contract callers, so this is a cross-contract trust-boundary weakness rather than a demonstrated unprivileged drain: any approved helper must enforce atomic pairing correctly or its public surface can expose uncompensated reserve transfer. Replace the split primitives with one public flashloan function that invokes a typed callback and verifies repayment before return. If split helpers must remain, make them private or callable only from one immutable audited wrapper, and record a transient obligation that step 2 must clear atomically.
ZEST-02 Low supply 84-85 Every supply call inserts owner at the next last-user-id and increments the counter, including repeat supplies by an existing owner. This creates duplicate user rows and unbounded index growth unrelated to unique-user count. Track a reverse membership map and append only on first registration, or document that this is intentionally an append-only activity index.
ZEST-03 Low set-borroweable-isolated 924-931 The configurator path appends an isolated-borrow asset without checking for an existing entry. Repeated configuration can introduce duplicates and consume the bounded list 100 capacity prematurely. Reject duplicates before append or normalize the list through a membership map.
ZEST-04 Informational borrow, flashloan steps, user functions 61, 150, 200, 206-207, 257, 558, 583 Several parameters and bindings are unused: pool-reserve, fee-calculator, interest-rate-mode, borrow-balance, and flashloan-script. In particular, borrow passes literal mode u0 into the reserve update regardless of the exposed interest-rate-mode input. Remove obsolete parameters or enforce and document the supported mode. Use the flashloan callback parameter as part of the atomic execution design.
ZEST-05 Informational supply, list helpers 91, 930, 944 Bounded-list and collateral-validation paths use unwrap-panic. Failures revert safely but become panic-style diagnostics. Replace reachable panic unwraps with structured errors where practical.

Conclusion

The Zest orchestrator validates caller allowlists, user ownership, reserve activity, LP and oracle correspondence, caps, isolation limits, e-mode compatibility, and caller-supplied health-evaluation assets. The main actionable issue is its split flashloan interface: reserve payout returns without core-level atomic repayment enforcement, placing a strong and easy-to-miss obligation on every approved integration wrapper. No high- or critical-severity issue was found.

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