unrip/archive/implementation/20260518T162356Z-db-backed-asset-registry-and-multi-pair-strategy-configuration-implementation.md
philipp fb547f24d9
All checks were successful
deploy / deploy (push) Successful in 48s
fix: scope inventory fetch by submission time window to prevent OOM
Instead of fetching up to 50k inventory snapshots (~442MB), scope the
query to only the time range of the current submission batch with a
15-minute buffer. For a typical 1-hour batch this drops from 50k rows
to ~300 rows, well within the 1280Mi pod memory limit.

The coalesced_at_idx on intent_inventory_snapshots covers the BETWEEN
clause so this remains efficient.

Proof: history-writer OOM kills from refreshQuoteOutcomes inventory fetch
Assumptions: 15min buffer covers the attribution window for all submissions
Still fake: heuristic gap outcomes may attribute trades imprecisely
2026-06-16 17:17:19 +02:00

9.4 KiB

Implementation Turn: DB-backed asset registry and multi-pair strategy configuration

Status: open Opened: 2026-05-12

Goal

Move supported assets, enabled pairs, pair filters, and per-pair edge/trading limits out of environment variables and into durable Postgres state, with a re-importable NEAR Intents asset catalog and fail-closed live trading behavior.

Selected backlog items

  • none selected; this turn was opened directly from the approved operator request on 2026-05-12.

Design rules

  • DB state is the source of truth for assets, enabled pairs, edge, limits, and pair modes.
  • Env remains only for infra/secrets/service wiring.
  • Importing an asset never enables trading by itself.
  • Pair config updates are versioned. Historical decisions must remain explainable.
  • Services fail closed if DB config is missing, stale, invalid, or internally inconsistent.
  • Current nBTC/EURe behavior must survive the migration before any additional pair trades.

External source

The supported asset catalog comes from the NEAR Intents 1Click tokens endpoint:

GET https://1click.chaindefuser.com/v0/tokens

The docs describe this endpoint as the source for supported token assetId values. A live check on 2026-05-12 returned an array of 163 token records and included:

  • nep141:nbtc.bridge.near
  • nep141:btc.omft.near
  • nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near

Backend changes

1. Add config schema

Add tables through the tracked schema path:

supported_asset_import_runs
trading_assets
trading_pairs
pair_strategy_configs
pair_price_routes
pair_config_audit_log

Minimum fields:

  • supported_asset_import_runs: run id, source URL, fetched_at, status, token_count, added_count, updated_count, retired_count, raw response hash, error.
  • trading_assets: asset id, venue, symbol, label, decimals, blockchain, contract address, latest price, price updated at, supported, retired_at, enabled_for_inventory, raw payload, updated_at.
  • trading_pairs: pair id, venue, asset_in, asset_out, mode, enabled, status, created_at, updated_at.
  • pair_strategy_configs: config id, pair id, version, active, edge_bps, max_notional, min_notional, slippage_bps, min_deadline_ms, price_max_age_ms, inventory_max_age_ms, created_at, created_by, reason.
  • pair_price_routes: route id, pair id, source, base asset id, quote asset id, route config JSON, max age, enabled.
  • pair_config_audit_log: audit id, entity type, entity id, action, old value, new value, changed_at, changed_by, reason.

2. Build supported-token importer

  • Add a pure normalizer for 1Click token records.
  • Add an idempotent importer that fetches /v0/tokens, validates shape, upserts trading_assets, and writes supported_asset_import_runs.
  • Mark previously supported assets as supported=false and retired_at=<run time> when missing from the latest import.
  • Keep raw payloads and source metadata.
  • Add an operator-safe command/API to rerun the importer.
  • Add tests for success, duplicate import, token update, asset retirement, malformed response, and network failure.

3. Seed current production truth

  • Seed current nBTC, legacy OMFT BTC, and EURe assets if absent.
  • Seed both directed nBTC/EURe pair rows needed by maker quote response.
  • Seed the active strategy config at 49 bps.
  • Seed current max notional/deadline/freshness settings from existing production defaults.
  • Keep legacy OMFT BTC inventory-visible but not active for maker trading unless explicitly enabled.

4. Runtime DB config loader

  • Add a loader module that returns:
    • asset registry map
    • enabled pair set
    • pair strategy config by pair
    • tracked asset ids
    • supported/retired flags
  • Cache briefly but refresh without restart.
  • Expose config load state in service /state.
  • Fail closed when DB is unreachable or config is invalid.
  • Store config version metadata in emitted decisions and commands.

5. Replace env-backed pair filtering

  • Remove runtime dependency on NEAR_INTENTS_PAIR_FILTER.
  • near-intents-ingest loads enabled observed pairs from DB.
  • Ingest should preserve raw quote data but only normalize/publish pairs approved for observation/trading.
  • Pair updates must apply without redeploy.
  • Keep a safe disabled state when no pairs are configured.

6. Replace env-backed strategy edge

  • Remove runtime dependency on STRATEGY_GROSS_THRESHOLD_PCT.
  • Strategy selects the active pair_strategy_configs row for the incoming quote pair.
  • Decisions include pair_id, pair_config_id, pair_config_version, edge_bps, and active limits.
  • Unsupported/disabled/unpriced pairs emit explicit rejected decisions, not silent drops where practical.
  • Existing nBTC/EURe math remains the first compatibility target.

7. Generalize asset and pair math

  • Replace tradingBtc/tradingEure assumptions in strategy with pair assets from DB.
  • Use DB decimals for amount conversion.
  • Preserve current BTC/EURe reference-price support first.
  • Block pairs whose price route is missing or stale.
  • Keep pair direction explicit instead of inferring BTC/EURe-only names.

8. Price route model

  • Current market reference is BTC/EUR-specific.
  • Add DB price routes before enabling non-BTC/EURe trading.
  • For this turn, support the current nBTC/EURe route and make other pairs blocked with price_route_missing.
  • Persist enough price-route metadata so later sources can be added without another env-based pair model.

9. Inventory and funding visibility

  • Inventory sync uses DB trading_assets for tracked assets.
  • Dashboard shows balances for known/tracked assets including retired assets.
  • Funding/deposit observations label assets from DB metadata.
  • Assets may be supported=false but still inventory-visible.

10. Dashboard controls

  • Add an asset catalog section:
    • import status
    • import button/control
    • counts for supported/retired/known assets
    • searchable asset table
  • Add a pair config section:
    • pair rows with mode/status
    • active strategy config and edge bps
    • price route status
    • controls to enable observe-only and update edge
  • Live trading enablement must remain explicit and separated from import.
  • Show pair config version in quote lifecycle rows.

11. Alerts and health

  • Runtime health uses DB active pair set instead of a single activePair.
  • Alerts must include pair id where applicable.
  • Missing DB config or invalid active pair config becomes a critical blocked state for trading services.
  • NEAR upstream incident relevance should be scoped to DB tracked assets/chains.

12. Deployment config cleanup

  • Remove pair/asset/edge env vars from deploy/k8s/base/unrip.yaml after DB seed and runtime loading are in place.
  • Keep infra/secrets env vars.
  • Ensure a repo push deploys the whole change without manual database editing.

Concrete implementation order

Phase 1. Schema and seed

  • Add DB schema and helper functions.
  • Add seed routine for current nBTC/EURe production config.
  • Add tests proving seed is idempotent.

Phase 2. Asset importer

  • Implement 1Click token fetch/normalize/import.
  • Add CLI/control path for re-import.
  • Add tests for import and retirement semantics.

Phase 3. Runtime config loader

  • Implement DB loader and fail-closed validation.
  • Convert asset registry consumers that only need labels/decimals.
  • Add tests for missing/invalid config.

Phase 4. Ingest migration

  • Replace env/file pair filter with DB pair set.
  • Preserve current pair behavior.
  • Add tests for pair update and no-config disabled behavior.

Phase 5. Strategy migration

  • Load pair strategy config from DB.
  • Remove global env edge use from runtime.
  • Persist pair config version in decisions/commands.
  • Add current-pair compatibility and edge-version tests.

Phase 6. Dashboard and controls

  • Add asset import and pair config surfaces.
  • Add controls for re-import and observe-only/pair edge update.
  • Add tests proving dashboard data comes from DB.

Phase 7. Deployment cleanup and validation

  • Remove asset/pair/edge env vars from deployment config.
  • Run targeted tests and full test suite.
  • Build dashboard bundle.
  • Deploy through repo workflow.
  • Validate live importer, current pair config, quote ingest, decisions, and dashboard bootstrap.

Test plan

  • Asset import normalizer tests.
  • Import idempotency tests.
  • Retired asset tests.
  • Schema/seed idempotency tests.
  • Runtime config loader tests.
  • Fail-closed missing DB config tests.
  • Ingest DB pair filter tests.
  • Strategy per-pair edge tests.
  • Strategy config versioning tests.
  • Dashboard asset/pair config tests.
  • Deployment config static test proving pair/edge env vars are absent.

Validation checklist against the proof

  • DB contains current assets and current nBTC/EURe pair.
  • Supported-token importer can rerun and record a new import run.
  • Current active pair and 49 bps edge load from DB.
  • NEAR Intents ingest no longer depends on NEAR_INTENTS_PAIR_FILTER.
  • Strategy no longer depends on STRATEGY_GROSS_THRESHOLD_PCT.
  • Dashboard shows asset import status and pair strategy config.
  • New imported assets are not trade-enabled by default.
  • Current pair still emits decisions and quote responses when armed.
  • All services deploy from repo push.

Known fakes allowed at start of this turn

  • Only current BTC/EURe pricing route is real.
  • Additional imported pairs may be observe-only or blocked until price routes exist.
  • Venue-native terminal fills remain unavailable unless existing paths already provide them.
  • Fee-complete realized PnL remains unavailable.