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.
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
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.
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.
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.
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.
Several bounded-list and collateral-validation paths use unwrap-panic.
Transactions revert atomically, but structured errors would improve monitoring
and integration diagnostics.
| 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. |
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.