Skip to content

feat: add per-asset emergency shutdown - #1499

Merged
github-actions[bot] merged 1 commit into
Smartdevs17:mainfrom
Abayomi-fx:fix/issue-1411-add-emergency-shutdown-of-an-asset
Oct 20, 2026
Merged

github-actions[bot] merged 1 commit into
Smartdevs17:mainfrom
Abayomi-fx:fix/issue-1411-add-emergency-shutdown-of-an-asset

Conversation

@Abayomi-fx

Copy link
Copy Markdown
Contributor

Overview

This PR adds support for per-asset emergency shutdown, allowing a single asset to be paused independently of the rest of the system. The shutdown is scoped to the targeted asset, reversible via a resume path, and covered by service-level tests.

Related Issue

Changes

🛑 Per-Asset Emergency Shutdown

  • [MODIFY] api/src/controllers/security.controller.ts

    • Adds controller handling for asset-scoped emergency pause/resume actions.
  • [MODIFY] api/src/routes/emergency.ts

    • Implements the asset-scoped shutdown and resume logic.
  • [MODIFY] api/src/routes/emergency.routes.ts

    • Registers the emergency shutdown/resume routes.
  • [MODIFY] api/src/routes/v1/assets/index.ts

    • Wires the asset routes to respect the per-asset shutdown state.
  • [ADD] api/src/__tests__/emergencyPause.service.test.ts

    • Tests covering scoped shutdown, reversibility (resume), and isolation from other assets.

Verification Results

npm test -- api/src/__tests__/emergencyPause.service.test.ts
✅ emergencyPause.service tests passed
Acceptance Criteria Status
Scoped ✅ Shutdown targets a single asset without affecting others
Reversible ✅ Resume path restores the asset to normal operation
Tested ✅ Covered by emergencyPause.service.test.ts

Closes #1411

@vercel

vercel Bot commented Oct 20, 2026

Copy link
Copy Markdown

@Abayomi-fx is attempting to deploy a commit to the smartdevs17's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Oct 20, 2026

Copy link
Copy Markdown

@Abayomi-fx Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@github-actions
github-actions Bot merged commit 73e7d75 into Smartdevs17:main Oct 20, 2026
2 of 3 checks passed
charleswilliams788 pushed a commit to charleswilliams788/stellarlend that referenced this pull request Oct 22, 2026
Smartdevs17 pushed a commit that referenced this pull request Oct 26, 2026
* feat(ops): improve deployment and platform readiness

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: flash loan support, reentrancy protection, liquidation bot interface, partial liquidation (#1485)

## Summary

Implements four Stellar Wave issues in a single cohesive PR:

---

### Issue #1400 — Flash Loan Support

Exposes the missing query and configuration entrypoints for the existing callback-based flash loan implementation:

- `get_flash_loan_fee_bps()` — read current fee (default 9 bps / 0.09%)
- `get_flash_loan_security_config()` / `set_flash_loan_security_config()` — TWAP / price-impact / liquidity-cap knobs
- `get_flash_loan_security_snapshot(asset)` — audit trail of the last loan per asset
- `is_asset_flash_loan_active(asset)` — external reentrancy / monitoring probe

**Acceptance criteria met:** callback-based ✅ fee configurable ✅ tested ✅

---

### Issue #1401 — Flash Loan Reentrancy Protection

Documents and tests the existing RAII `FlashLoanGuard` which provides two independent locks:
1. **Global reentrancy guard** (`ReentrancyGuard` key) — blocks any re-entry from within the callback
2. **Per-asset concurrent loan guard** (`ActiveFlashLoan(asset)` key) — blocks a second loan for the same asset

New tests:
- Concurrent same-asset loan blocked
- Active flag cleared after successful loan (RAII `Drop` verified)
- Global guard blocks cross-entry

**Acceptance criteria met:** guarded ✅ tested ✅ reviewed ✅

---

### Issue #1402 — Liquidation Bot Interface

Adds `bots/liquidation-bot/README.md` documenting the full bot interface:
- Position discovery API (`GET /api/liquidations/opportunities`)
- Real-time WebSocket health feed subscription
- Liquidation execution (`POST /api/lending/liquidate`)
- Full configuration reference table
- Target strategy guide (`highest_profit` / `highest_risk` / `lowest_hf`)
- Health-check / stats API
- Architecture diagram, security notes, dev guide

**Acceptance criteria met:** documented ✅ tested (existing tests) ✅ example bot (runnable `src/`) ✅

---

### Issue #1403 — Partial Liquidation

Adds a dedicated `partial_liquidate` contract entrypoint:

```rust
pub fn partial_liquidate(
    env: Env,
    liquidator: Address,
    borrower: Address,
    debt_asset: Address,
    collateral_asset: Address,
    repay_amount: i128,
) -> Result<LiquidationResult, BorrowError>
```

Delegates to the same `liquidate::liquidate` core; close-factor enforcement caps `repay_amount` automatically. 8 new boundary tests.

**Acceptance criteria met:** configurable ✅ health restored ✅ tested ✅

---

## Files Changed

| File | Change |
|---|---|
| `stellar-lend/contracts/lending/src/lib.rs` | New entrypoints: flash loan queries + `partial_liquidate` |
| `stellar-lend/contracts/lending/src/flash_loan_test.rs` | Tests for #1400 and #1401 |
| `stellar-lend/contracts/lending/src/liquidation_boundary_test.rs` | 8 tests for #1403 |
| `bots/liquidation-bot/README.md` | New bot documentation for #1402 |
| `SOLUTIONS_ISSUES_1400_1401_1402_1403.md` | Implementation notes |

Closes #1400
Closes #1401
Closes #1402
Closes #1403

* feat: add observability stack with metrics, logging, and config (#1480)

## Overview

This PR introduces an observability foundation for the API: a request-logging middleware, a metrics controller exposing a metrics endpoint, and supporting configuration — wired into the app so metrics, logs, and traces can be collected and surfaced.

## Related Issue


## Changes

### 📊 Observability Stack

* **[ADD]** `api/src/middleware/requestLogger.ts`
  * Request-scoped logging middleware capturing request lifecycle events for log collection.

* **[ADD]** `api/src/controllers/metrics.controller.ts`
  * Metrics controller exposing collected metrics for scraping/dashboards.

* **[MODIFY]** `api/src/app.ts`
  * Registers the request logger middleware and metrics route in the application pipeline.

* **[MODIFY]** `api/src/config/index.ts`
  * Adds observability-related configuration (metrics/logging settings) to the central config.

* **[MODIFY]** `api/package.json`
  * Adds the dependencies required by the logging and metrics components.

## Verification Results

```
npm run build
✅ API compiles with new middleware, controller, and config

Manual check:
✅ Request logger emits entries per request
✅ Metrics endpoint responds with collected metrics
```

| Acceptance Criteria | Status |
| --- | --- |
| Integrated | ✅ Logger middleware and metrics controller wired into `app.ts` |
| Dashboard | ✅ Metrics endpoint exposed for dashboard consumption |
| Documented | ✅ Configuration surfaced via `config/index.ts` |

Closes #1427

* feat(lending): add funded supplier reward allocations and claims (#1487)

## What Changed

Adds configurable, funded supplier rewards to the modular lending contract. The protocol admin selects a reward token and assigns cumulative entitlements; suppliers authorize claims paid directly to their own addresses. Allocations cannot exceed the funded budget, and repeating an allocation or claim never pays twice.

- Exact-transfer SEP-41 funding with separate available/reserved/paid accounting.
- Immutable reward token, admin-only allocation, and a switch to disable new allocations while preserving existing claims.
- Shared reentrancy protection, atomic rollback on transfer failures, typed events and registered errors.
- Ten integration tests, test-runner registration, operator documentation and changelog.

## Why

Suppliers currently have no incentive distribution mechanism. This implements an admin-funded allocation model. The existing deposit API records balances without transferring collateral, so administrators must independently verify supplier eligibility and backing; deposit balances alone never generate rewards. This trust requirement is documented in `docs/SUPPLIER_REWARDS.md`.

## Testing

- [x] Integration tests added
- [x] Relevant targeted checks passed (fork CI)
- [x] Deployable Wasm build completed

[Validation and unchanged-base comparison](https://github.com/toyosi-14/stellarlend/actions/runs/39260674768) (validation workflow stays on a separate fork branch):

| Check | Observed result |
| --- | --- |
| `cargo test --manifest-path stellar-lend/Cargo.toml -p stellarlend-lending --test supplier_rewards` | 10 passed, including authorization, allocation limits, repeated claims, disable/withdrawal, rollback and callback rejection |
| `cargo test --manifest-path stellar-lend/Cargo.toml -p stellarlend-errors` | 41 unit tests and 3 doctests passed; 1 doctest ignored |
| `stellar contract build --manifest-path stellar-lend/contracts/lending/Cargo.toml` (CLI 28.1.0) | Passed; optimized Wasm 114,749 bytes |
| New Rust files: `rustfmt --edition 2021 --check`; `git diff --check` | Passed |
| `SUITES=lending-integration bash scripts/testing/run-contract-tests.sh test` | Stops at existing `event_topics` failure: liquidation incentive event lacks `asset`; reproduced on unchanged base |
| Lending `cargo clippy --lib -- -D warnings` | Existing `derivable_impls` error in `contracts/security/src/error.rs`; reproduced on unchanged base |
| Workspace `cargo fmt --all -- --check` | Existing formatting differences; reproduced on unchanged base |

Baseline comparison used `252708a1ffae7bf0e21e65541f1e8056703abddc`. The aggregate validation run remains red because it includes those baseline failures. `local-ci.sh` was attempted before installing Stellar CLI and stopped at its missing-CLI prerequisite; the full script was not completed. Local Rust execution was unavailable due to a read-only WSL filesystem; runtime validation ran on the fork.

| Acceptance criterion | Evidence |
| --- | --- |
| Configurable | Admin selects token and enables/disables allocations; unauthorized changes and token replacement rejected |
| Claimable | Funded entitlements transfer to authenticated suppliers, including after withdrawal or disabling allocations |
| Tested | Ten passing reward tests, shared error tests, and successful Wasm build |

## Related Issues

Closes #1409

* fix: reentrancy guards, gas gate, archive storage and error taxonomy (#1385, #1384, #1383, #1378) (#1488)

Closes #1385
Closes #1384
Closes #1383
Closes #1378

## Summary

Four independent hardening changes, one commit each.

- **#1385 reentrancy (`stellar-lend/contracts/amm`)**: `execute_swap`, `add_liquidity`, `remove_liquidity` and `auto_swap_for_collateral` now take a temporary-storage lock. A nested call returns `AmmError::ReentrancyDetected` (new code 15). `validate_amm_callback` is intentionally not guarded, because AMM protocols call it back during a swap.
- **#1384 performance gate (`scripts/`)**: `gas-regression.sh` crashed under `set -u` when run with no arguments, which is the documented default. Argument parsing is fixed and the threshold is validated. `gas_benchmark_report.py` now warns about benchmarks that have no baseline entry, which previously passed the gate silently. Unit tests are added under `scripts/tests/`.
- **#1383 indexer storage (`api/src/services/eventIndex/archive.ts`)**: re-archiving a day wrote a new segment generation but never deleted the superseded object, so each re-archive leaked a file. `ColdStorage` gains `delete()`, and superseded generations are removed after the new manifest is written.
- **#1378 error taxonomy (`api/src/utils/errors.ts`)**: adds the compliance codes that were emitted as string literals, gives 413 its own `PAYLOAD_TOO_LARGE` code, and documents the taxonomy in `api/docs/ERROR_TAXONOMY.md`, including known gaps.

## Not verified

No build or test commands were run, per the requirements for this change. Rust and TypeScript changes were checked by reading only. The Python tests were run and pass. Please let CI run `cargo test -p stellarlend-amm`, the api jest suite, and the gas regression workflow.

## Known gaps (out of scope)

- About 478 API responses still use string-only error bodies. Migrating them is a separate change.
- The rate limiter emits a 429 without `RATE_LIMITED`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

* feat(api): add cached position health endpoint (#1489)

## Summary

Closes #1413

Adds a position health endpoint that enables users to query accurate, on-chain lending position health information through the REST API.

## Changes Made

- Added `GET /api/v1/lending/positions/:userAddress/health` to retrieve position health information.
- Integrated the existing Stellar service to fetch contract-calculated health data.
- Returns collateral value, debt value, health factor, liquidation status, and borrow capacity.
- Implemented caching using the existing Redis-backed read-cache service and position cache TTL.
- Added Stellar address validation to reject invalid account addresses.
- Added error handling for incomplete contract responses and upstream failures.
- Added OpenAPI documentation for the new endpoint.

## Testing

All seven test cases passed successfully, covering:

- Accurate position health calculations and liquidation thresholds.
- Liquidatable position detection.
- Cache reuse across repeated requests.
- Cache invalidation and refresh.
- Invalid Stellar address handling.
- Incomplete contract responses.
- Upstream RPC failure handling.

**Test Results:** All 7 tests passed.

## Files Modified

- `api/src/services/stellar.service.ts`
- `api/src/controllers/lending.controller.ts`
- `api/src/routes/lending.routes.ts`
- `api/src/__tests__/positionHealth.routes.test.ts`

Changes are limited to the necessary API files, without modifying existing smart contracts or unrelated functionality.

## Acceptance Criteria

- [x] **Accurate:** Uses contract-calculated position health data.
- [x] **Cached:** Integrates with the existing caching infrastructure.
- [x] **Tested:** All seven automated tests passed.

* feat: harden lending operations and indexer observability (#1354)

## Summary
- instrument the primary indexer with batch latency, throughput, error, and backlog metrics
- add direct regression coverage for nested state-changing reentrancy guards
- expose an admin-guarded emergency_pause entrypoint that controls the global pause scope
- verify emergency pause blocks borrowing, deposits, and withdrawals

Closes #1212
Closes #1213
Closes #1214
Closes #1215

* Solve issues (#1490)

## What Changed

Summarize the main code and behavior changes.

## Why

Explain the problem this PR solves.

## Testing

- [ ] Unit tests updated or added
- [ ] Relevant local checks passed
- [ ] Manual verification completed when needed

## Related Issues

Link the related issue(s), for example: Closes #123

close #1404
close #1405 
close #1406
close #1407

* feat(api): scoped keys, usage analytics, liquidation webhooks, sandbox (#1374 #1375 #1376 #1377) (#1491)

Combined PR closing the four assigned API issues. Existing developer API
surfaces (API keys, developer portal, usage/rate-limit view, webhook test
stub) were present but shallow; each change here makes one of them real.

## Verification

- **22 new tests pass** (`apiKey.scopes-usage`, `webhook.service`,
  `sandbox.service`); existing `apiKey.service` + `auth` suites still pass
  (18 tests).
- Full-project `tsc --noEmit` reports **pre-existing** TS7030 errors in
  unrelated controllers; **no type errors in any touched file**.

---

### Issue #1374: Add API auth with scoped keys

**Problem:** API keys authenticate but carry no capability limits — every key
is all-powerful, so a read-only integration key can mutate protocol state.

**Resolution:** `ApiKeyRecord` now has `scopes` (`read`, `write`, `admin`,
`liquidation:read`, `webhooks:manage`; `*` wildcard default for backwards
compatibility). `create` validates and stores scopes, rotation preserves
them, and `authenticateApiKey` attaches the key id + scopes to the request.
New `requireScope(...scopes)` middleware enforces them (403
`ForbiddenError` when missing). `POST /developer/api-keys` accepts `scopes`.
Covered by tests for defaults, validation, wildcard, rotation and
enforcement.

Closes #1374

### Issue #1375: Add API usage analytics

**Problem:** Usage is invisible — there is no way to see which keys are
active, how many requests they make, or which endpoints they hit.

**Resolution:** Per-key usage counters in `apiKeyService` (total requests,
per-endpoint breakdown, first/last used) recorded by the API-key auth
middleware. New `GET /developer/api-keys/:keyId/usage` and
`GET /developer/usage/keys` endpoints expose them for the existing developer
portal dashboard. Covered by tests for counting, unused keys and aggregation.

Closes #1375

### Issue #1376: Add webhook notifications for liquidations

**Problem:** Liquidations are not notified; the existing
`/developer/webhooks/test` endpoint only returned a fake "queued" object and
the liquidation monitor had no outbound notification path.

**Resolution:** New `webhook.service` performs **real HMAC-SHA256 signed**
deliveries (`X-StellarLend-Signature: t=<unix>,v1=<hmac>` over
`timestamp.body`), with up to **3 retries** using capped exponential backoff
+ jitter and constant-time receiver-side `verifySignature` helper. The
liquidation monitor now emits `liquidation.alert` to subscribers. Added
subscription CRUD (`/developer/webhooks`), made `/webhooks/test` deliver for
real, and documented events/signature/retries in `api/docs/WEBHOOKS.md`.

Closes #1376

### Issue #1377: Add API sandbox environment

**Problem:** No sandbox exists — integrators have no isolated, resettable
environment and must test against shared state.

**Resolution:** New sandbox service + routes: `GET /api/sandbox` (status,
isolation model, resource counts) and `POST /api/sandbox/reset` (wipes sandbox
API keys and webhook subscriptions, seeds a `read`-scoped demo key, returns
the raw key once). State is fully **isolated in-memory**; production requires
explicit `SANDBOX_ENABLED=true`. Documented in `api/docs/SANDBOX.md`, with
env vars added to `.env.example`.

Closes #1377

## Scope note

Webhook subscriptions and usage counters are in-memory, matching the existing
API-key store design; durable persistence/queueing is left for the storage
layer, and noted here rather than faked.

* feat(oracle): pull-mode query API, historical pagination, and OpenAPI spec (#1370 #1371 #1372 #1373) (#1492)

## Summary

Adds a read-side query API to the oracle and makes the push/pull update model configurable, so consumers can fetch current and historical prices on demand.

### Issue 1370: Add push vs pull oracle support

**Problem:** The oracle only periodically aggregated prices and pushed them on-chain; there was no way to run it as a pull-based price source.

**Resolution:** Added `ORACLE_MODE` (`push` | `pull` | `both`, default `push` for backwards compatibility) to the config schema and `OracleServiceConfig`. `OracleService.start()` is now mode-aware: `push` keeps the existing interval loop, `pull` skips pushes and serves reads over HTTP, `both` runs the loop and the query API together. `getStatus()` reports the active mode.

### Issue 1371: Add oracle historical price queries

**Problem:** Historical observations were stored in the circular buffer but could not be queried.

**Resolution:** Added `PriceHistoryService.queryHistory(asset, { from, to, limit, cursor })` with inclusive time bounds, plus a short-lived query cache (5s default) that is invalidated whenever new entries are added or history is cleared. The query server exposes `GET /prices`, `GET /prices/:asset`, and `GET /prices/:asset/history`.

### Issue 1372: Add pagination and filtering to queries

**Problem:** History reads had no pagination or filtering, so responses were unbounded.

**Resolution:** History queries return a `pagination` block (`limit`, `count`, `total`, `hasMore`, `nextCursor`) and accept `from`/`to` unix-second filters plus an opaque `cursor`. Page size defaults to 20 and is capped server-side at 100; non-integer query params return `400`.

### Issue 1373: Add OpenAPI spec generation

**Problem:** There was no machine-readable description of the oracle's read API.

**Resolution:** The routes are declared once in `src/api/openapi.ts` and used by both the server and `scripts/generate-openapi.ts`, so the spec cannot drift. `npm run openapi:generate` writes `oracle/openapi.json`, served at `GET /openapi.json`; `npm run openapi:check` fails on drift locally and `tests/openapi.test.ts` fails `npm test` if the committed spec is stale.

## Changes

- `oracle/src/types/index.ts` – `OracleMode`, `oracleMode`
- `oracle/src/config.ts` – `ORACLE_MODE` env validation + `loadConfig()` wiring
- `oracle/src/index.ts` – mode-aware `start()`, `getMode()`, `getLatestPrices()`, query server lifecycle
- `oracle/src/services/price-history.ts` – `queryHistory()`, query cache, `HistoryQuery`/`HistoryPage`
- `oracle/src/api/openapi.ts`, `oracle/src/api/query-server.ts`, `oracle/scripts/generate-openapi.ts` (new)
- `oracle/openapi.json` (generated, committed)
- `oracle/package.json` – `openapi:generate`, `openapi:check`
- `oracle/README.md` – modes, endpoints, OpenAPI usage

## Testing

- `npx vitest --run` in `oracle`: **681/681 passing** (23 new tests covering mode config, history filtering/pagination/caching, query-server endpoints, and OpenAPI no-drift).
- `npx prettier --check "src/**/*.ts"` clean; `npm run lint` 0 errors (pre-existing warnings only).
- `npx tsc --noEmit` reports only pre-existing errors in `oracle/src/services/price-validator.ts` (`nextPrior` possibly undefined), which is unrelated to this change and untouched.

Closes #1370
Closes #1371
Closes #1372
Closes #1373

* perf: liquidation gas, dispatch, query plan review and API cache invalidation (#1379-#1382) (#1493)

## Summary

One branch covering the four Stellar Wave issues assigned to me. Each issue has its own commit.

Closes #1379
Closes #1380
Closes #1381
Closes #1382

### #1379 — reduce gas on the liquidation path (`contracts/lending/src/liquidate.rs`, `borrow.rs`)
- The variable debt record is reused when it is the selected bucket, instead of being read twice.
- The health check builds its debt view from the already-loaded position and one interest calculation, instead of calling `get_user_debt`, which re-read both debt records.
- The collateral map is read once and shared by valuation, the availability check, and the seizure (`debit_collateral_balances`).
- Error order is unchanged: an interest error still surfaces only after the health and availability checks.

### #1380 — dispatch overhead (`contracts/lending/src/calldata.rs`)
- `flush_deposits` moved the pending batch out instead of cloning it on every flush.
- This is a small change. The entrypoint dispatch generated by `#[contractimpl]` is not touched.

### #1381 — query plan review (`api/db/init/09_query_plan_indexes.sql`, `api/docs/query-plan-review.md`)
- Dropped two indexes that are prefixes of composite indexes already defined: `idx_price_history_asset` and `idx_position_risk_address`.
- Added `idx_pool_perf_events_recorded_at` for the unfiltered event feed.
- The review is static. The doc lists the checks to run with `EXPLAIN ANALYZE`.

### #1382 — API caching: TTL, invalidation, metrics (`api/src/services/readCache.service.ts`, `api/src/controllers/lending.controller.ts`)
- Post-transaction invalidation now goes through `readCacheService.invalidateAfterWrite()`. It used to call Redis directly, which left those invalidations out of the per-kind metrics.
- The same five kinds are invalidated as before. Gas and price entries still expire on TTL.
- Added a test that checks the invalidations are counted.

## Not verified

- **No build, test, or benchmark was run** for this PR. The Rust changes have not been compiled and the gas numbers have not been measured, so the "measured reduction" and "benchmark" criteria of #1379 and #1380 are still open. The gas baseline (`benchmarks/gas-baseline.json`) has not been updated.
- The new API test and the SQL migration have not been executed.
- The SQL migration changes existing deployments only if applied by hand, because the Docker init directory runs only on first boot.

* Fix issues #1366-#1369: Oracle TWAP pricing, monitoring, source onboarding, and dispute handling (#1494)

## Summary of changes

This PR addresses four issues assigned to the contributor:

### Issue #1366: Add TWAP pricing for volatile assets
- Enhanced  with volatility-aware window adjustment
- Added  and  configuration options
- TWAP window is dynamically scaled based on recent price volatility for better stability during volatile market conditions

### Issue #1367: Add oracle update monitoring
- Added update cadence tracking to  with per-asset update intervals
- Added  method to 
- Enhanced metrics endpoint with  and  fields per asset
- Tracks time since last update and flags when updates are slower than threshold (3 minutes)

### Issue #1368: Add oracle source onboarding process
- Created  with provider configuration validation
- Added , , , and  static methods
- Ensures new price sources can be safely added with proper weighting and priority

### Issue #1369: Add oracle dispute handling
- Added  incident type to 
- Added , , and  methods
- Provides mechanism for reporting and resolving problematic price feeds
- Integrates with existing pause/recovery logic

### Changes made:
- : Volatility-aware TWAP window adjustment
- : Update cadence and staleness tracking
- : Price dispute tracking + update cadence monitoring
- : New file - provider onboarding service

* oracle-hub: storage TTL, fuzz coverage, median/deviation hardening (#1495)

Implements four oracle-hub improvement issues in one change set.

#1362 Contract fuzz tests
- Expand the oracle_actions fuzz target to drive the governance recovery
  path (rebase), the TTL manager, and the retained history, asserting the
  hub's invariants (positive served price, bounded history, total TTL bump).
- Add an oracle_ttl fuzz target that mutates TTL policies directly and
  checks invalid policies are rejected rather than trapping.
- Wire both targets into the fuzz workflow with a configurable, reproducible
  budget (FUZZ_MAX_TOTAL_TIME, FUZZ_SEED, FUZZ_TIMEOUT) and a nightly
  schedule; add a seed corpus for oracle_ttl.
- Declare an empty [workspace] in the fuzz crate so its nightly-only graph
  never resolves against an unrelated ancestor workspace.

#1363 Storage TTL management
- New 	tl module: a TtlPolicy (enabled, 	hreshold, extend_to) with a
  hub-wide default and per-asset overrides, validated up against
  MAX_TTL_EXTEND_TO.
- set_ttl_policy / get_ttl_policy / �ump_storage entry points, the last
  permissionless and idempotent (host extend_ttl only acts below threshold).
- Persistent mirrors of the latest price and history entries, refreshed
  amortized on every write path (report, rebase, pull).
- TtlConfigUpdatedEvent and TtlExtendedEvent.

#1364 Multi-oracle median aggregation
- Hardened by the new fuzz target and invariant assertions; median, outlier
  filtering, and deviation-checked fallback were already implemented and are
  now exercised across the retained history and TTL surfaces.

#1365 Price deviation guard
- The configurable per-update jump bound and 
ebase_price recovery path are
  now covered by the expanded fuzz target in addition to the unit tests.

Docs: README and ARCHITECTURE document the new 	tl module, storage keys,
and entry points.

Closes #1362
Closes #1363
Closes #1364
Closes #1365

Co-authored-by: Ebuka321 <corneliusemmanuel55@gmail.com>

* feat(api): add asset list and metadata endpoints (#1415) (#1497)

Add read-only asset metadata under /api/v1/assets:

- GET /assets returns every supported asset
- GET /assets/:symbol returns one asset (case-insensitive symbol,
  404 NOT_FOUND for unknown symbols)

Metadata (symbol, name, category, decimals, isNative) lives in a single
registry in assetMetadata.service.ts. Responses are cached with the
existing cacheReadResponse middleware and send a matching Cache-Control
header. TTL is configurable via ASSET_METADATA_TTL_MS (default 5 min).

Add tests covering the registry, both endpoints, cache HIT/MISS/BYPASS,
cache-disabled behavior, and the 404 error format.

Closes #1415

* feat: add signed liquidation webhooks (#1498)

## Summary

- Added signed `liquidation.confirmed` webhooks for successful real liquidation requests.
- Added HMAC-SHA256 signatures, timestamp and event-id headers, and bounded retries for transient failures.
- Added configuration examples, consumer verification guidance, and focused tests.

## Testing

- Focused webhook suite: `npm test -- --runInBand src/__tests__/liquidationWebhook.service.test.ts` — 5 passed.
- Liquidation bot suite: `npm test -- --runInBand --forceExit` — 8 passed. Jest required `--forceExit` because the existing WebSocket lifecycle leaves an open handle.
- Bot build: `npm run build` — blocked by the pre-existing `minDiscountBps` error in `src/auctionBidder.service.ts`.
- Lint: unavailable; the bot package has no `lint` script.
- Root type-check: unavailable in this checkout because root dependencies are not installed.

No unrelated production changes were made. The webhook delivery is best effort and uses the event id for consumer-side deduplication.

Closes #1412

* feat(security): multi-sig admin, event monitoring, formal verification, static analysis gate (#1486)

Closes #1390 #1391 #1392 #1393

## #1390 — Add multi-sig for protocol admin
- Add stellar-lend/contracts/lending/src/multisig.rs
  - M-of-N threshold multi-sig gate for all privileged admin operations
  - Supported actions: SetMinCollateralRatio, SetBaseBorrowRate,
    SetReserveFactor, SetLiquidationIncentiveBps, SetOracle,
    PauseProtocol, RotateAdmin, UpgradeContract
  - 48-hour execution timelock on every approved proposal
  - Proposer auto-approves; any admin can cancel
  - Events: ms_creatd, ms_apprvd, ms_execd, ms_cancld, ms_admins
- Add docs/MULTISIG_ADMIN.md — architecture, API reference, security model

## #1391 — Add event monitoring for suspicious activity
- Add stellar-lend/contracts/lending/src/event_monitor.rs
  - Detection rules: LARGE_BORROW, BURST_TX, RAPID_LIQ,
    PRICE_DEVIATION, FLASH_LOAN_ABUSE, ADMIN_ACTION
  - Per-user counters in Soroban temporary storage (auto-expiring)
  - All rules emit typed (sec_alert, rule_id) Soroban events
- Add monitoring/alerts/suspicious_activity.rules.yml
  - Prometheus alert rules for each detection rule
  - Severities: Info / Warning / Critical
  - Runbook URLs included in annotations

## #1392 — Add formal verification of core math
- Add stellar-lend/formal-verification/safe-math-proofs/core_math_invariants.smt2
  - 10 SMT-LIB 2 proofs (Z3/CVC5) for core protocol invariants:
    safe_add, safe_mul, WAD round-trip, collateral-ratio lower bound,
    liquidation close-factor, interest non-negative growth,
    reserve-factor bounded, utilization [0,BPS], liquidation incentive,
    borrow capacity upper bound
  - Every check-sat must return unsat (property holds for ALL inputs)

## #1393 — Add static analysis (Clippy/audit) gate
- Add .github/workflows/static-analysis-gate.yml
  - fmt: cargo fmt --check (zero tolerance)
  - clippy: cargo clippy --all-targets -D warnings + wasm32 target
  - audit: cargo audit --deny warnings (pinned v0.21)
  - deny: cargo deny check advisories bans licenses sources
  - static-analysis-gate job: fails PR if any step is non-success

* fix: upgrade runbook, audit checklist automation, admin timelock (#1387, #1388, #1389) (#1496)

- docs/runbooks/upgrade-and-migration.md: add step-by-step devnet rehearsal
  procedure, rollback rehearsal steps, and timelock integration section
  (closes #1387)

- scripts/security/audit-checklist.sh: automated pre-audit checklist covering
  toolchain (fmt, clippy, cargo-audit, semgrep), source-code invariants,
  documentation presence, and optional on-chain health checks; emits a
  machine-readable JSON report and exits non-zero on any failure (closes #1388)

- .github/workflows/audit-checklist.yml: CI workflow that runs the checklist on
  every push/PR and uploads the JSON report as an artifact (closes #1388)

- contracts/timelock/src/lib.rs: rewrite the timelock contract with configurable
  min/max delay (default 48 h, floor 1 h, ceiling 30 days), argument-integrity
  check (SHA-256 of XDR-encoded args stored at schedule time and verified at
  execute time), cancellation by admin, executor rotation, and structured events
  for every state transition (closes #1389)

Co-authored-by: Dev Fashman <jajafwangshak86@gmail.com>

* feat: add per-asset emergency shutdown (#1499)

## Overview

This PR adds support for **per-asset emergency shutdown**, allowing a single asset to be paused independently of the rest of the system. The shutdown is scoped to the targeted asset, reversible via a resume path, and covered by service-level tests.

## Related Issue


## Changes

### 🛑 Per-Asset Emergency Shutdown

* **[MODIFY]** `api/src/controllers/security.controller.ts`
  * Adds controller handling for asset-scoped emergency pause/resume actions.

* **[MODIFY]** `api/src/routes/emergency.ts`
  * Implements the asset-scoped shutdown and resume logic.

* **[MODIFY]** `api/src/routes/emergency.routes.ts`
  * Registers the emergency shutdown/resume routes.

* **[MODIFY]** `api/src/routes/v1/assets/index.ts`
  * Wires the asset routes to respect the per-asset shutdown state.

* **[ADD]** `api/src/__tests__/emergencyPause.service.test.ts`
  * Tests covering scoped shutdown, reversibility (resume), and isolation from other assets.

## Verification Results

```
npm test -- api/src/__tests__/emergencyPause.service.test.ts
✅ emergencyPause.service tests passed
```

| Acceptance Criteria | Status |
| --- | --- |
| Scoped | ✅ Shutdown targets a single asset without affecting others |
| Reversible | ✅ Resume path restores the asset to normal operation |
| Tested | ✅ Covered by `emergencyPause.service.test.ts` |

Closes #1411

* docs: add asset onboarding checklist (#1500)

## Overview

This PR addresses the ad hoc onboarding process by adding a structured asset onboarding checklist. The checklist is documented in the repository's contributor-facing documentation and surfaced through the pull request template so contributors follow a consistent onboarding path.

## Related Issue


## Changes

### 📋 Onboarding Checklist

* **[MODIFY]** `README.md`
  * Added an asset onboarding checklist section covering the steps needed to onboard a new asset.

* **[MODIFY]** `CONTRIBUTING.md`
  * Documented the onboarding checklist expectations for contributors.

* **[MODIFY]** `.github/pull_request_template.md`
  * Added checklist items so onboarding-related PRs are tracked consistently.

## Verification Results

```
Documentation-only change — no code or tests modified.
✅ Checklist documented in README.md and CONTRIBUTING.md
✅ PR template updated to reference the checklist
```

| Acceptance Criteria | Status |
| --- | --- |
| Complete | ✅ Onboarding checklist added across docs and PR template |
| Reviewed | ✅ Changes limited to documentation and template files |
| Documented | ✅ Checklist documented in README.md and CONTRIBUTING.md |

Closes #1410

* feat(oracle): centralize secret access metadata (#1501)

## Summary\n\n- Add a centralized environment secret provider with rotation identifiers and access audit callbacks.\n- Add focused validation behavior for the affected flow.\n\nCloses #1426

* feat(lending): add configurable per-asset supply caps (#1360) (#1502)

## What Changed

- Added configurable per-asset supply caps (`set_asset_supply_cap`) manageable by contract admin.
- Implemented persistent storage keys `DepositDataKey::AssetSupplyCap(Address)` and `DepositDataKey::AssetTotalSupply(Address)` to track limits and aggregate deposits per asset.
- Enforced per-asset supply caps in single deposits (`deposit_with_auth`) and batch deposits (`deposit_batch_with_auth`), aborting atomically with `DepositError::ExceedsDepositCap` when cap is exceeded.
- Updated withdrawal routines (`withdraw_with_auth`, `sweep_deposit_dust`, and `emergency_withdraw`) to safely decrement `AssetTotalSupply`, freeing capacity under the asset cap.
- Emitted `SupplyCapUpdatedEvent` with `admin`, `asset`, `previous_cap`, and `new_cap`.
- Added public query methods `get_asset_supply_cap` and `get_asset_total_supply` on `LendingContract`.
- Added comprehensive unit tests in `contracts/lending/src/deposit_test.rs` and complete integration test suite in `contracts/lending/tests/supply_caps.rs`.

## Why

Addresses #1360: Previously, asset supply was uncapped or only limited by global parameters. Adding fine-grained per-asset supply caps allows protocol risk management to constrain exposure to specific collateral types, mitigating systemic insolvency risks and pool imbalances.

## Testing

- [x] Unit tests updated or added (`deposit_test::test_per_asset_supply_cap_configurable_and_enforced`)
- [x] Relevant local checks passed (`cargo test --test supply_caps` 7/7 pass)
- [x] Manual verification completed when needed

## Asset Onboarding

- [x] Complete
- [x] Reviewed
- [x] Documented

## Related Issues

Closes #1360

/claim #1360

* fix(ci): repair API lint error and cargo-deny config schema

* fix(ci): unblock npm installs and audit checklist on main

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: King Jaja <150508794+devJaja@users.noreply.github.com>
Co-authored-by: Gift Afolayan <afolayangodgift@gmail.com>
Co-authored-by: Asogba Emmanuel Mautin <asogbaoluwatoyosi14@gmail.com>
Co-authored-by: goldandrew <goldandrew112@gmail.com>
Co-authored-by: V1ctor-o <victorokeke0@gmail.com>
Co-authored-by: Big Della <okoyeemmanuelobiajulu@yahoo.com>
Co-authored-by: mbo-hub11 <harvsteve336@gmail.com>
Co-authored-by: dimka <110455012+dimka90@users.noreply.github.com>
Co-authored-by: Yilkash <yilritdimka@gmail.com>
Co-authored-by: financialcrackerjack-max <financialcrackerjack@gmail.com>
Co-authored-by: Marcus David <davidmarcus827@gmail.com>
Co-authored-by: samsonmbah002 <samsonmbah39@gmail.com>
Co-authored-by: Ebuka321 <corneliusemmanuel55@gmail.com>
Co-authored-by: King David <davidoladoyin166@gmail.com>
Co-authored-by: Emmanuel Fasola <olaolumanuel@gmail.com>
Co-authored-by: Dev. Fashman <jajafwangshak86@gmail.com>
Co-authored-by: Lekan101 <169077572+Lekan101@users.noreply.github.com>
Co-authored-by: Abayomi-fx <abdulqudusabdulwahab330@gmail.com>
Co-authored-by: Onuora Chiemelie Chinedu | Dev <onuorahchiemelie460@gmail.com>
Co-authored-by: Brymo14 <lukmanib2005@gmail.com>
Co-authored-by: duongynhi000005-oss <duongynhi000005@gmail.com>
Co-authored-by: Fury03 <fury03@users.noreply.github.com>
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.

Add emergency shutdown of an asset

1 participant