Skip to content
LogoLogo

Generated — do not edit. Source of truth: crates/terp-rs/tools/hash-market/docs/oracle-connect-bounds.md. Edit there, then rebuild docs.

Hashmerchant as oracle — multi-source bounds (Tacit-aligned)

Status: design + lib types (oracle module). Default VE path unchanged.
Program spine: plans/terp-private-shielded-dex-bridge/PLAN.md · Tacit SSOT crates/tacit
Reference (do not reinvent): skip-mev/connect — providers, market map, aggregation sidecar, VE broadcast.


1. Why this exists

Tacit locks:

Asset classOracle role
cBTC-like conservationNo oracle mint. Peg/backing from proven locks + kernels.
cUSD-like boundsOracles price bounds only — e.g. (\textitmin_out \ge g(\textitmid, \textitslippage))

hashmerchant / VEs never invent balances. They may supply multi-source mid / TWAP bounds that private DEX settle uses as quote guards, while pool math / LCs own solvency.

Trust is feature-tiered (Tacit Tier 0/1/2) — multi-source aggregation is Tier-declared honesty, not “all trustless.”


2. Two ingress kinds (separation of concerns)

KindWhat it attestsDefault todayElevated option
state_rootForeign chain root / height (Ethereum, etc.)✅ Current providers + VoteExtensionHashDataunchanged
price_boundMulti-source mid for a market id (cUSD-like)❌ not aggregatedConnect-style feeders + aggregate

Do not collapse these into one blob. Roots are conservation/interop fabric (with LCs). Bounds are pricing honesty for CDP/quote guards only.

  ┌──────────────── state_root path (default) ────────────────┐
  │ eth_getProof / LC headers → provider feeder → VE root     │
  │ → HashRoot quorum → contracts / ZK (interop fabric)       │
  └───────────────────────────────────────────────────────────┘

  ┌──────────────── price_bound path (optional elevated) ─────┐
  │ Connect-style providers (API/WS) per market_id            │
  │   → attribute (source, ts, raw)                           │
  │   → aggregate (median / weighted / trimmed)               │
  │   → OracleBoundObservation                                 │
  │   → VE or query API as bounds only (never mint)           │
  └───────────────────────────────────────────────────────────┘

3. Skip Connect mapping (synthesis, not fork)

Connect concepthash-market placement
Provider plugins (API + WS)Extend [[providers]] + transport; optional Connect-compatible market map config
Market / currency pair mapmarket_id (e.g. USDT/USD, ETH/USD) on price providers
Oracle sidecar aggregationhash_market::oracle::aggregate_* then store under ProviderKey or dedicated bounds map
Vote extensionsExisting ABCI++ path; bounds payload is additional kind or tagged algo (price_median_v1)
On-chain oracle moduleTerp may later mirror Connect’s module; v1 can keep bounds in hashmerchant query + VE only

Rule: Prefer vendoring patterns and config shapes from Connect over rewriting a price network. If/when we path-dep Connect crates or gRPC APIs, they plug in as a SourceBackend::ConnectSidecar { url }.


4. Granularity: source → attribute → aggregate

4.1 Source

A source is one named feeder endpoint:

[[providers]]
name = "binance_eth_usd"
kind = "price_bound"          # default if omitted: state_root (legacy)
market_id = "ETH/USD"
mode = "http_poll"            # or websocket; Connect backends later
address = "https://…"
interval_secs = 5
weight = 1.0                  # optional, aggregation

Legacy entries without kind remain state_root.

4.2 Attribute

Each observation carries:

FieldPurpose
sourceProvider name
market_idWhat is priced
priceDecimal or fixed-point integer + decimals
observed_atUnix secs
foreign_heightOptional if source is on-chain feed
raw_refOptional exchange trade id / ticker hash

Stored in-process; exposed on GET /providers / future GET /oracle/bounds?market_id=.

4.3 Aggregate (sum / equal / mid)

MethodUse
medianDefault multi-source mid (Connect-like resilience)
meanEqual weight average
weighted_meanBy weight
trimmed_meanDrop high/low then mean
min / maxBound envelope (strict min_out / max_in)

“Sum” is not used for mid pricing; it is for portfolio / basket markets if we define composite market_ids later. Equal weight = mean. Quorum: require min_sources fresh within max_age_secs.

Hard invariant in code comments and API: aggregated price is a bound input, not a mint instruction.


5. Tacit trust mapping

Bound pathTier postureNotes
Single operator feederTier 1-ish (pilot)Declare; not Tier 0
Multi-source median + ⅔ VEStronger attestation of published midStill not conservation proof
LC-proven reserves + AMM curveTier 0 settle pathMid only clamps slippage

cBTC-like assets must not consume price_bound as mint authority.


6. oline / elevated packaging

ModeDeployConfig
DefaultCurrent hashmerchant elevated (trees + optional state_root VE)ve_enabled + root providers only
Elevated sophisticationSame lease; config enables price_bound providers + aggregationoracle_bounds = true in config.toml via SFTP

No new football phase. Optional peer env later: OLINE_HASHMERCHANT_ORACLE_BOUNDS=1 to seed example multi-source TOML.


7. Iterative delivery order

SliceDeliverableStatusBreaks default?
O0This doc + oracle types + pure aggregate + testsDoneNo
O1Config: optional kind / market_id / [oracle] sectionDoneNo (defaults preserve state_root)
O2Feeder path for price HTTP tickers → attribute storeDone (oracle::feeder, OracleAttributeStore)No if unused
O3GET /oracle/bounds + optional VE price_median_v1DoneOpt-in
O4Connect sidecar adapter (gRPC/HTTP to existing Connect process)PlannedOpt-in
O5Wire private DEX min_out path to bounds queryPlannedApp-level

O2/O3 runtime behaviour (shipped)

  1. Config: [oracle] bounds_enabled = true + [[providers]] with kind = "price_bound" and market_id.
  2. Each price feeder polls address for ticker JSON ({"price":"…"} / nested data).
  3. Ticks land in OracleAttributeStore; market re-aggregates under policy (median default).
  4. API: GET /oracle/bounds?market_id=ETH/USD → one mid, role=bound_only. Debug: GET /oracle/ticks.
  5. VE (optional): when ve_enabled, publishes VoteExtensionHashData with algo=price_median_v1, root = decimals_be‖mantissa_be (20 bytes), empty attestations. Quorum is classic power vote on that mid — same security model as state roots, not multi-source bag consensus.

Hard rule: multi-source is off-chain ingress. Each provider (sidecar) submits one aggregated price per pair.


8. Non-goals (v1)

  • Replacing Tacit ConfidentialPool / reflection with oracle mint
  • Full Connect chain module port as day-1
  • Claiming multi-source mid is Tier 0 solvency
  • Folding Skip Go widget (crates/skip-go frontend) into the sidecar — different product

9. Public API (stable library)

Module: hash_market::oracle (always on; no feature flag for pure aggregate).
Session handoff: reviews/HASHMERCHANT-ORACLE-CONNECT-2026-07-20.md.
Example: examples/price_oracle.rs.

9.1 Types & functions (library contract)

APINotes
ProviderKind::{StateRoot, PriceBound}Default StateRoot for legacy
AttributedPricesource, market_id, fixed-point mantissa/decimals, observed_at, weight
AggregationMethodmedian (default), mean, weighted_mean, trimmed_mean, min, max
AggregationPolicymethod + min_sources + max_age_secs
aggregate_bounds(market, &[AttributedPrice], &policy, now)Pure; drops stale; enforces min_sources
OracleBoundResult mid; always document role = "bound_only"
OracleAttributeStoreUpsert tick + re-aggregate per market
parse_ticker_json / parse_decimal_strIngress helpers for HTTP feeders
ALGO_PRICE_MEDIAN_V1"price_median_v1" — never a keccak state root
encode_bound_root / decode_bound_root20 bytes: u32_be(decimals) ‖ i128_be(mantissa)
bound_to_vote_extensionMid → VE payload; empty ICS-23 / bag attestations
price_chain_uid(market)Default synthetic uid price:market_id

Invariants (semver-stable intent):

  1. Aggregation is deterministic given the same fresh multiset of ticks + policy + now.
  2. Output is never a mint instruction (role=bound_only).
  3. VE root encoding for mids is only valid under price_median_v1 (or future price_* family).
  4. Callers must not collapse state_root and price_bound into one semantic blob.

9.2 HTTP (runtime, O3)

GET /oracle/bounds?market_id=ETH/USD
→ {
  "market_id": "ETH/USD",
  "method": "median",
  "price": "3450.12",
  "decimals": 8,
  "mantissa": 345012000000,
  "n_sources": 3,
  "sources": ["binance", "coinbase", "okx"],
  "as_of": 1710000000,
  "role": "bound_only"
}

Also: GET /oracle/ticks?market_id=… (raw attributed sources).
GET /health includes "oracle": { "bounds_enabled": true, ... }.

9.3 VE packing (optional)

When ve_enabled and bounds feeders run: publish VoteExtensionHashData with:

  • algo = price_median_v1
  • root = encode_bound_root(decimals, mantissa)
  • chain_uid = provider chain_uid or price:market_id
  • empty attestations (mid is the commitment)

Quorum remains classic voting-power agreement on that root — same class of security as foreign state-root oracles, not multi-source bag consensus.