Skip to content

D3lphi Oracle

On-chain oracles on the Bitcoin Cash network.

The D3lphi oracle is being developed as part of the Moria project, to serve as an on-chain oracle for the lending protocol.

The D3lphi oracle contracts allows for getting price information using transaction introspection. By taking a oracle utxo as input, transaction introspection can be used to fetch price information from the oracles NFT commitment.

flowchart LR
   D3lphiInput --> DeFiTransaction--> D3lphiOutput
   DefiContractIn --> DeFiTransaction --> DefiContractOut

Benefits of using an on-chain oracle in this manner include:

  • No need to pass oracle and message as input and verify it.
  • You do not need to query external sources for oracle messages.
  • You can get the median price of multiple oracles as one input (see aggregated.cash).
  • The oracle contract owner can swap out the underlying oracle if it goes offline.

Format

The oracle contains a 36 byte commitment at all times that is as follows:

Bytes Purpose
20 bytes  Hash of the oracle public key
16 bytes Oracle message as defined by the oracles.cash protocol

Note: Users should always verify that the price is not 0. This occurs when a contract owner swaps the oracle public key.

Notable trade-offs

You can optionally verify that the oracle used in the contract has not been changed by checking the first 20 bytes of the commitment. These bytes should match the hash of the oracle public key (hash160).

If you decide to not verify enforce the oracle public key hash, you put additional trust to the contract owner in addition to the oracle, essentially allowing the owner to swap out the oracle public key.

Ideally, you should deploy your own oracle contract.

Deployed contracts

Category / Token ID Contract Address Contract Owner Oracle owner  Contract owner pubkey  Oracle pubkey Notes
b0b6fc3d5cda81f4bb3fe464767dcc33e80b6356e4838f4dda40a1871a625950 bitcoincash:pdm0hsy0twjt6zv0psx6z2sn6keznd5vd4lre0ge0jgwcqdwz94tjqaeveg6z Riften Labs AS General Protocols  03341a6fb68e883fb2c5ce0d0d186e9e09792839479bfb14adda2f498fc2dfaacf 02d09db08af1ff4e8453919cc866a4be427d7bfe18f2c05e5444c196fcf6fd2818 Updated at 10 minute intervals. Runs until May 2024.

Fetch latest on-chain price with oracletools.js price <category> <contractAddress>

Usage, development and testing

This repository contains tests on regtest for all oracle calls.

These tests also serve as examples on how to use the oracle.

Components

Tool: oracletool.js

A tool for deploying and updating a contract. It can also be used to fetch price information from the blockchain.

Contract: oracle.cash

This is the main contract that holds the price information from a single oracle.

Calls:

update(oraclePubKey, oracleSig, oracleMessage)

Called to update the contract commitment to a newer oracle message.

use()

Called to take the contract as an input into a transaction, allowing another contract to accesse price information via transaction introspection.

The contract using price information as input must verify that the category of the contract is correct. The contract can optionally verify that the oracle has not been swapped.

This call requires that the utxo be re-created at the same output index as input index. There is a 1000 satoshi fee to use the contract.

withdrawBCH(ownerSignature)

Allows owner to withdraw BCH to contract, without changing the commitment.

swapOracle(ownerSignature)

Allows the owner to swap the underlying oracle used in the contract. An oracle swap requires that the oracle message is set to blank [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Contract: aggregated.cash

This is a contract to aggregate the price from multiple oracles.

It takes 3 oracles and inputs and produces the median price of the 3.

This contract is barely within the OP code limit of Bitcoin Cash consensus, so adding more seems unfeasable, however, we can add more oracle entities by chaining aggregated oracles.

flowchart LR
   Oracle1 --> AggregatedMedian1
   Oracle2 --> AggregatedMedian1
   Oracle3 --> AggregatedMedian1
   AggregatedMedian1 --> MedianOfMedian

   Oracle4 --> AggregatedMedian2
   Oracle5 --> AggregatedMedian2
   Oracle6 --> AggregatedMedian2
   AggregatedMedian2 --> MedianOfMedian

   Oracle7 --> AggregatedMedian3
   Oracle8 --> AggregatedMedian3
   Oracle9 --> AggregatedMedian3
   AggregatedMedian3 --> MedianOfMedian

In this image we take the "median of three from the median of three", with total of 9 different oracle entities. This can be aggregated further if needed to add more entities.

update()

Takes 3 oracle inputs at this contracts input offset +1, +2 and +3. Parses the median price and updates its own commitment to match.

use()

Called to take the contract as an input into a transaction, allowing another contract to accesse price information via transaction introspection.

withdrawBCH(ownerSignature)

Allows contract owner to withdraw fees.

Format

Bytes Purpose
20 bytes  Hash of all oracle public keys
16 bytes Oracle message with median price of 3

Deployment

It is suggested to deploy many threads. You do not need to update them all, but this allows you to scale if the utxos being updated get congested.

Deployer must not create a minting NFT, as this will allow him to publish messages without using the oracle.

Future work

Contract owners may want to deploy their own proxy oracle, allowing them to swap out underlying aggregated oracles.

flowchart LR
   Oracle1 --> AggregatedOracle
   Oracle2 --> AggregatedOracle
   Oracle3 --> AggregatedOracle
   AggregatedOracle --> ProxyOracleIn
   ProxyOracleIn --> TxThatNeedsPrice
   TxThatNeedsPrice --> ProxyOracleOut

   OtherInputs --> TxThatNeedsPrice --> OtherOutputs

Adding a proxy would allow them to swap out the aggregated oracle. Proxy contract is not written yet.