Repository navigation
feat: add per-asset emergency shutdown - #1499
Merged
github-actions[bot] merged 1 commit intoOct 20, 2026
Merged
github-actions[bot] merged 1 commit into
github-actions[bot] merged 1 commit into
Conversation
|
@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. |
|
@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! 🚀 |
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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[MODIFY]
api/src/routes/emergency.ts[MODIFY]
api/src/routes/emergency.routes.ts[MODIFY]
api/src/routes/v1/assets/index.ts[ADD]
api/src/__tests__/emergencyPause.service.test.tsVerification Results
emergencyPause.service.test.tsCloses #1411