# 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: ```text 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: ```sql 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=` 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.