DLC Oracle
Spec-compliant BIP-340 Schnorr attestations for Discreet Log Contracts. Three event types — enum, numeric, and threshold — with auto-resolution at maturity. Wire format compatible with kormir, dlcdevkit, rust-dlc, and any spec-compliant DLC library.
Overview
The Mycelia Signal DLC Oracle provides cryptographic attestations that enable two parties to construct trustless financial contracts on Bitcoin. The oracle publishes pre-committed nonces in an announcement, then reveals Schnorr signatures at maturity. Those signatures are the only thing that can unlock the on-chain contract — the oracle never touches anyone's money and doesn't know who the counterparties are or how much they bet.
Three event types are supported:
Enum
Disjoint union — 2 to 100 categorical outcomes. Prediction markets, elections, sports, weather categories, coarse price buckets.
Numeric
Digit decomposition — precise value attestation. Configurable base (2-256), digits (1-32), optional sign and scaleFactor for sub-unit precision.
Threshold
Binary outcome — price crosses above or below a strike. Bitcoin-collateralized loans, perpetual hedges.
All event types use BIP-340 Schnorr signatures over secp256k1 with spec-compliant tagged hashes per discreetlogcontracts/dlcspecs. Flat $7.00 USDC / 10,000 sats per event — covers nonce generation, secure storage, and auto-attestation at maturity.
TLV wire format is byte-for-byte compatible with kormir, dlcdevkit, rust-dlc, bitcoin-s, cfd-dlc, and NDLC. The serialized field in every announcement response is a hex-encoded TLV blob you can feed directly into any spec-compliant DLC library.
Oracle Identity
The DLC oracle runs on a dedicated US instance. The public key is stable and published for independent verification.
| Property | Value |
|---|---|
| Endpoint | api.myceliasignal.com/dlc/ |
| Oracle Pubkey | 03ff5589b5812f07dec1aad7af189e0513160799820386128a80d37d1503a8e84f |
| BIP-340 x-only | ff5589b5812f07dec1aad7af189e0513160799820386128a80d37d1503a8e84f |
Payment
Two payment rails are supported. Send a request without credentials to receive HTTP 402 with both options — you only need to implement one.
L402 — Lightning (10,000 sats)
Standard L402 protocol. Pay the invoice with any Lightning wallet (Phoenix, Strike, Alby, etc.) and retry with the preimage.
# Step 1 — POST to get the 402 with invoice curl -s -X POST https://api.myceliasignal.com/dlc/oracle/enum \ -H "Content-Type: application/json" \ -d '{"label":"btc-bucket","outcomes":["below_70k","70k_80k","above_80k"],"maturityTs":1780000000}' # Response: 402 with L402 invoice + x402 payment requirements # Pay the invoice, then retry with preimage: curl -s -X POST https://api.myceliasignal.com/dlc/oracle/enum \ -H "Content-Type: application/json" \ -H "Authorization: L402 <macaroon>:<preimage>" \ -d '{"label":"btc-bucket","outcomes":["below_70k","70k_80k","above_80k"],"maturityTs":1780000000}'
DLC macaroons use URL-safe base64 encoding. Path-bound to the actual endpoint (e.g. /dlc/oracle/enum). Expire in 30 minutes.
x402 — USDC on Base ($7.00)
Standard x402 v2 protocol using EIP-3009 transferWithAuthorization. Sign the transfer from your Base wallet and include the encoded payload.
# Step 1 — POST to get payment requirements curl -s -X POST https://api.myceliasignal.com/dlc/oracle/enum \ -H "Content-Type: application/json" \ -d '{"label":"btc-bucket","outcomes":["below_70k","70k_80k","above_80k"],"maturityTs":1780000000}' # Response: 402 with payment-required header (x402 v2 requirements) # Sign EIP-3009 transfer, then retry with PAYMENT-SIGNATURE header: curl -s -X POST https://api.myceliasignal.com/dlc/oracle/enum \ -H "Content-Type: application/json" \ -H "PAYMENT-SIGNATURE: <base64-encoded-payload>" \ -d '{"label":"btc-bucket","outcomes":["below_70k","70k_80k","above_80k"],"maturityTs":1780000000}'
| Rail | Amount | Notes |
|---|---|---|
| L402 Lightning | 10,000 sats (~$7) | Any Lightning wallet. Macaroon expires in 30 minutes. |
| x402 USDC | $7.00 USDC on Base | EIP-3009 signature. Header: PAYMENT-SIGNATURE. Settlement via Coinbase CDP facilitator. |
Endpoints
Enum (disjoint union)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /dlc/oracle/enum | Paid | Create 2-100 outcome event |
POST | /dlc/oracle/enum/preview | Free | Dry-run validation, no nonces |
GET | /dlc/oracle/enum | Free | List all enum events |
GET | /dlc/oracle/enum/{eid} | Free | Get announcement JSON |
GET | /dlc/oracle/enum/{eid}/serialized | Free | Hex-encoded TLV blob |
GET | /dlc/oracle/enum/{eid}/attestation | Free | Get attestation (404 if pending) |
Numeric (digit decomposition)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /dlc/oracle/numeric | Paid | Create digit decomposition event |
POST | /dlc/oracle/numeric/preview | Free | Dry-run validation, no nonces |
GET | /dlc/oracle/numeric | Free | List all numeric events |
GET | /dlc/oracle/numeric/{eid} | Free | Get announcement JSON |
GET | /dlc/oracle/numeric/{eid}/serialized | Free | Hex-encoded TLV blob |
GET | /dlc/oracle/numeric/{eid}/attestation | Free | Get per-digit attestation (404 if pending) |
Threshold (legacy)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /dlc/oracle/threshold | Paid | Register threshold contract |
DELETE | /dlc/oracle/threshold/{eid} | Paid | Cancel contract, attest safe |
GET | /dlc/oracle/threshold | Free | List active threshold contracts |
Discovery (free)
| Endpoint | Description |
|---|---|
GET /dlc/oracle/pubkey | Oracle public key, key format, curve, instance identifier |
GET /dlc/oracle/status | Announcement counts, supported pairs, instance |
GET /dlc/oracle/announcements | All announcements (all types) |
GET /dlc/oracle/announcements/{eventid} | Single announcement by event ID |
GET /dlc/oracle/attestations/{eventid} | Attestation. HTTP 425 if not yet attested. |
Enum Events — Create and Settle
Enum events are for categorical outcomes where the answer is one bucket from a known set. Customer defines outcomes at creation; oracle publishes one Schnorr signature at maturity revealing the winning outcome.
Create an enum event
curl -s -X POST https://api.myceliasignal.com/dlc/oracle/enum \
-H "Content-Type: application/json" \
-H "Authorization: L402 <macaroon>:<preimage>" \
-d '{
"label": "btc-price-bucket-may1",
"outcomes": ["below_70k", "70k_75k", "75k_80k", "above_80k"],
"maturityTs": 1780000000,
"metadata": {
"resolverConfig": {
"kind": "price_buckets",
"source": "http://localhost:9200/oracle/price/btc/usd",
"boundaries": [70000, 75000, 80000]
}
}
}'
{
"status": "registered",
"eventid": "enum-btc-price-bucket-may1-2f6704559a1e-1780000000",
"type": "enum",
"outcomes": ["below_70k", "70k_75k", "75k_80k", "above_80k"],
"maturityTs": 1780000000,
"oraclePubkey": "ff5589b5812f07dec1aad7af189e0513...",
"rpoints": ["15c5e0fa...", "c5fa8cf5...", "8e016027...", "e28a267a..."],
"announcementSignature": "52ca48c15de58dd2...",
"serialized": "d82452ca48c15de5...",
"rail": "l402",
"priceUsd": 7.0
}
The serialized field is a hex-encoded TLV blob per dlcspecs — feed it directly into kormir, dlcdevkit, or any spec-compliant DLC library. One rpoint per outcome.
Enum request fields
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Human-readable event label |
outcomes | string[] | Yes | 2-100 outcomes, max 256 chars each |
maturityTs | int | Yes | Unix timestamp, 60s to 365 days future |
metadata.resolverConfig | object | No | Auto-resolution config (see below) |
Numeric Events — Create and Settle
Numeric events decompose a value into individual digits, each attested separately. This allows counterparties to construct CETs covering all possible price outcomes with arbitrary precision.
Create a numeric event
curl -s -X POST https://api.myceliasignal.com/dlc/oracle/numeric \
-H "Content-Type: application/json" \
-H "Authorization: L402 <macaroon>:<preimage>" \
-d '{
"label": "btc-usd-may1",
"base": 10,
"nbDigits": 7,
"unit": "USD",
"maturityTs": 1780000000,
"metadata": {
"resolverConfig": {
"kind": "price_source",
"source": "http://localhost:9200/oracle/price/btc/usd",
"scaleFactor": 1
}
}
}'
At maturity, BTC at $77,553 decomposes as digits [0, 0, 7, 7, 5, 5, 3] — seven separate Schnorr signatures revealed. Use scaleFactor for sub-unit precision: scaleFactor=100 against ETH at $4,500.42 gives integer 450042, decomposed as [0, 4, 5, 0, 0, 4, 2] — cent-level precision.
Numeric request fields
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Human-readable event label |
base | int | No | Numeral base, 2-256. Default: 10 |
nbDigits | int | No | Number of digits, 1-32. Default: 7 |
isSigned | bool | No | Negative values supported. Default: false |
unit | string | No | Unit label, max 32 chars. Default: "USD" |
maturityTs | int | Yes | Unix timestamp, 60s to 365 days future |
metadata.resolverConfig | object | No | Auto-resolution config (see below) |
Auto-Resolution
Events created with a resolverConfig in metadata self-settle at maturity. The oracle scheduler checks every 60 seconds for events past maturity and resolves them automatically.
| Event Type | Resolver Kind | Config | Behavior |
|---|---|---|---|
| Enum | price_buckets | source, boundaries | Fetches price, finds matching bucket |
| Enum | webhook | url | GETs URL, expects {"winningOutcome":"..."} |
| Numeric | price_source | source, scaleFactor | Fetches price, multiplies by scaleFactor, decomposes digits |
| Numeric | webhook | url | GETs URL, expects {"value":<int>} |
Events without a resolverConfig can be manually attested via the admin endpoint (POST /dlc/oracle/{enum,numeric}/{eid}/attest with X-Oracle-Secret header).
Threshold Contracts
Threshold contracts monitor a price continuously and attest when the price crosses a defined level. Binary outcome — breached or safe. Use for Bitcoin-collateralized loans, perpetual hedges, or any binary outcome contract.
Register a threshold contract
curl -s -X POST https://api.myceliasignal.com/dlc/oracle/threshold \
-H "Content-Type: application/json" \
-H "Authorization: L402 <macaroon>:<preimage>" \
-d '{
"pair": "BTCUSD",
"strike": 90000,
"direction": "above",
"expiry": 1780000000,
"webhookUrl": "https://your-server.com/dlc-webhook"
}'
| Field | Type | Required | Description |
|---|---|---|---|
pair | string | Yes | Uppercase concatenated, e.g. BTCUSD |
strike | float | Yes | Price level to monitor |
direction | string | Yes | "above" or "below" |
expiry | int | No | Unix timestamp. Defaults to 1 year. |
webhookUrl | string | No | Oracle POSTs attestation on breach/expiry. 3 retries with backoff. |
Supported Pairs
All 17 pairs support threshold contracts. Enum and numeric events can use any pair via resolverConfig, or any custom data source.
Cryptography
| Property | Value |
|---|---|
| Curve | secp256k1 |
| Signature scheme | BIP-340 Schnorr with tagged hashes |
| Announcement tag | DLC/oracle/announcement/v0 |
| Attestation tag | DLC/oracle/attestation/v0 |
| Public key format | 32-byte BIP-340 x-only (y-coord implicit even) |
| Nonces | Fresh per event, x-only normalized. Deleted post-attestation. |
| Wire format | TLV per dlcspecs/Messaging.md — BigSize, NFC strings, x_point, signature |
| TLV types | 55302 (enum), 55306 (numeric), 55330 (event), 55332 (announcement), 55400 (attestation) |
| Enum attestation | Single signature for winning outcome |
| Numeric attestation | Per-digit signature, one per digit position |
| Threshold attestation | Binary — breached or safe. Single R-point, single s-value. |
| R correctness | Signature R component always equals pre-committed announcement R — verified by integration test |
Every Schnorr signature's R component equals the pre-committed nonce R from the announcement. This is what makes the attestation actually unlock on-chain CETs — not just a signed message. If R doesn't match, counterparties' locked BTC can't be spent.
The oracle public key is long-lived and published at GET /dlc/oracle/pubkey. Contracts built against this key will settle using attestations from the same instance. See the Public Keys reference for all signing keys.