Skip to content

fix(market): flash-batch share pricing and 12 hardening fixes - #220

Open
tiamo wants to merge 39 commits into
mainfrom
dev
Open

tiamo wants to merge 39 commits into
mainfrom
dev

Conversation

@tiamo

@tiamo tiamo commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Summary

Security fixes for the market, found in an internal review of the deployed v1.1.0 code.
Each fix is its own commit with a regression test that fails without it.

Critical. A FlashBorrow inside submit_requests_batch subtracted its principal from the pool's
total_available, so later requests in the same batch priced shares against an understated pool. The
principal is now reserved (FlashReserved), not removed: supply, accrual and caps see the whole pool,
and only payouts are limited to the unreserved part (1ef46d53, fuzzed in c5ad8d78).

Flash loans stay disabled on the deployed market until this fix is live.

Fixes

Commit Change
1ef46d53 Keep flashed assets in the pool's supply base (critical, above)
c6940ef2 Instant config writes (status flags, fee beneficiaries) are carried into a queued pool set
25320f4f A refusing farms contract no longer aborts deposit and withdraw; the push degrades and emits FarmStakePushFailed
5fd05304 Farms: a frozen farm accepts a stake decrease, so a leaving position stops earning
ee8addd5 The take-rate beneficiary setter persists the accrual before it distributes
582f57e4 Constructor bounds update_in_queue_period to 60 s ..= 10 years
45e3c187 Constructor refuses an oracle whose decimals overflow the price factor (> 38)
bc32edcd A pool set's interest rate model is validated when queued
6d4393ea A supply-only pool (open_ltv_bps = 0) no longer traps withdrawals by dividing by zero
3441ea12 A batched Liquidate credits the batch's own (seeded) obligation
c366c60b ClaimCoverBadDebtResults reports how many requests were settled
3dffd435 A scarcity cooldown without a scarcity fee is refused
7bcd1ba8 The rate controller integrates over at most 900 s, so an idle pool cannot pin the modifier

Plus documentation corrections (6b698753, 0af9d03b), test-only commits, client SDK fixes, and the
rebuilt wasms/market.wasm and wasms/farms.wasm that the integration tests import.

Interface changes

  • New error MCError::OracleUnusableAtConstruction = 503.
  • Event ClaimCoverBadDebtResults gains settled: u32.
  • New event FarmStakePushFailed { pool, farm_id, obligation_key, stake }.
  • New instance key FlashReserved(Address); absent at every transaction boundary. Existing storage
    layout is unchanged.
  • Farms: set_stake_delegated on a frozen farm accepts a decrease.
  • Client SDK (breaking): calcUserTotal*InUsd return UsdTotal, not number. getPoolData is typed
    PoolData. New getGlobalState and getOraclePriceDecimals.

Upgrade

Deploy artifact built from this head (--features deploy): sha256
0e460ef2ab044d25888e6a6af76b79d488b8f654c3ba79315daa35aefb3748ea, 113,344 bytes. Compare the attested
release build against it. The upgrade goes through the market manager queue (12 h on the live market).

Verification

On bf8e76b7: cargo fmt --check (stable and nightly), the three clippy runs from CI and the Makefile,
cargo doc with --deny warnings, cargo check --locked, and cargo nextest run --workspace:
356 passed, 1 ignored (test_consecutive_borrows_can_lead_to_unpaid_interest_become_negative, ignored
before this PR). Every fix's regression test fails with that fix reverted. Client SDK: bun test 8/8.

dependabot Bot and others added 30 commits September 7, 2026 19:55
Bumps [eslint](https://github.com/eslint/eslint) from 10.9.1 to 10.10.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.9.1...v10.10.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
…dev/eslint-10.10.0

build(deps-dev): bump eslint from 10.9.1 to 10.10.0
Bumps [eslint](https://github.com/eslint/eslint) from 10.10.0 to 10.11.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.10.0...v10.11.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
…dev/eslint-10.11.0

build(deps-dev): bump eslint from 10.10.0 to 10.11.0
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.23.13 to 4.23.15.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](privatenumber/tsx@v4.23.13...v4.23.15)

---
updated-dependencies:
- dependency-name: tsx
  dependency-version: 4.23.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [release-plz/action](https://github.com/release-plz/action) from 0.5.131 to 0.5.139.
- [Release notes](https://github.com/release-plz/action/releases)
- [Commits](release-plz/action@2eb1d8b...b8d6b54)

---
updated-dependencies:
- dependency-name: release-plz/action
  dependency-version: 0.5.139
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.4.1 to 26.6.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [chalk](https://github.com/chalk/chalk) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/chalk/chalk/releases)
- [Commits](chalk/chalk@v6.0.0...v6.0.1)

---
updated-dependencies:
- dependency-name: chalk
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Copilot AI balanced review requested due to automatic review settings October 3, 2026 11:16
@tiamo tiamo changed the title Market security fixes: flash-batch share pricing and 12 hardening fixes fix(market): flash-batch share pricing and 12 hardening fixes Oct 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

It changes critical flash-loan share-pricing and accounting invariants on a live DeFi market across many interdependent files, which requires human verification before approval.

Review effort: Balanced
Findings: None

What changed in this PR

This PR applies one critical fix plus a series of hardening fixes to the market (and farms) Soroban contracts, found in an internal review of deployed v1.1.0. The headline change reworks how a batch FlashBorrow interacts with pool accounting: the flashed principal is now reserved in a new FlashReserved(Address) instance key rather than subtracted from total_available, so supply/accrual/caps see the whole pool and only payouts are limited to the unreserved part. The remaining commits close governance-rollback, farm-liveness, constructor-validation, division-by-zero, liquidation-crediting, and rate-controller edge cases, each with a regression test. The client SDK is updated to surface unpriced-pool cases via a UsdTotal discriminated union and to add getGlobalState/getOraclePriceDecimals.

Changes:

  • Reserve (not remove) flashed assets via FlashReserved, adding payable_available/reducible_available helpers and threading &Env through require_total_available.
  • Carry instant config writes (status flags, fee beneficiaries) into a ripe queued set via patch_queued_pool_set; gracefully degrade farm stake pushes; bound constructor params and reject over-scaled oracles; validate IR models at queue time; fix supply-only (open_ltv_bps = 0) withdrawals and batch-liquidation seed crediting.
  • Client SDK: calcUserTotal*InUsd now return UsdTotal; getPoolData returns PoolData; new global-state/oracle-decimals getters; dependency/lockfile bumps and large test additions.
File Description
contracts/​market/​src/​pool.rs Reservation-aware adjust_total_available, payable_available/reducible_available, IR-model validation, cooldown-without-fee rejection
contracts/​market/​src/​processors.rs Flash reserve set/guard, liquidation now keyed by full obligation key, settled count in claim event
contracts/​market/​src/​request.rs Clears reservation on flash repay; zero-amount market transfers for transfer-gated collateral
contracts/​market/​src/​storage.rs FlashReserved key + accessors; patch_queued_pool_set preserving timelock
contracts/​market/​src/​obligation.rs Zero-scalar (supply-only) branch; reservation-aware borrow/withdraw caps
contracts/​market/​src/​contract.rs Oracle-decimals guard, queue-period bounds, beneficiary accrual-persist, queued-set patch calls, doc comments
contracts/​market/​src/​interest_rate.rs /​ constants.rs Clamp controller integration to MAX_IR_INTEGRATION_SECONDS; new bound constants
contracts/​market/​src/​interest_rate_model/​* validate promoted to the InterestRate trait
contracts/​market/​src/​farms.rs /​ events.rs /​ error.rs Fallible farm push + FarmStakePushFailed; settled field; error 503
contracts/​farms/​src/​contract.rs Frozen farm accepts a stake decrease
packages/​client-sdk/​* UsdTotal union, new getters, SDK tests, dependency bumps
tests/​*, docs/​parameters.md, Cargo.lock, pnpm-lock.yaml Regression/fuzz tests, doc and lockfile updates
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@tiamo
tiamo requested a balanced review from Copilot October 3, 2026 11:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

It changes critical share-pricing/payout accounting in a deployed DeFi lending market and ships rebuilt market.wasm/farms.wasm binaries that cannot be verified from the diff, so it warrants final human review.

Review effort: Balanced
Findings: None

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

tiamo and others added 7 commits October 3, 2026 22:29
…s/dev/release-plz/action-0.5.139

build(deps): bump release-plz/action from 0.5.131 to 0.5.139
…dev/types/node-26.6.3

build(deps-dev): bump @types/node from 26.4.1 to 26.6.3
…dev/chalk-6.0.1

build(deps): bump chalk from 6.0.0 to 6.0.1
…dev/tsx-4.23.15

build(deps-dev): bump tsx from 4.23.13 to 4.23.15
Bumps [eslint](https://github.com/eslint/eslint) from 10.11.0 to 10.12.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.11.0...v10.12.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
…dev/eslint-10.12.0

build(deps-dev): bump eslint from 10.11.0 to 10.12.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants