-
-
oracle deployed on Polygon providing BTC price
-
price chart which use the data subscribed from Polygon
-
logs of oracle node (missing singatures of other nodes)
-
logs of oracle node (with enough signatures)
-
configration for aggragators of many DEX and CEX
-
Oracle Intro 8
-
Oracle Intro 7
-
Oracle Intro 4
-
Oracle Intro 3
-
Oracle Intro 1
-
Oracle Intro 2
-
Oracle Intro 6
-
Oracle Intro 5
Build a dedicated oracle
Why do people need yet another oracle?
For DeFi protocols, the price of crypto assets is very important, because the price is a signal that determines subsequent operations, such as the liquidation of collateral assets.
However, popular oracles in the industry, such as Chainlink, have not yet solved the problem of the accuracy of price data. The fundamental reason is that their feeding nodes do not use fresh data. The data are provided by centralized vendor such as coingecko. Chainlink only solves the problem of preventing feeding nodes from doing evil. That is not enough to reduce the security risk.
Solution to build a dedicated oracle
For critical DeFi applications, relying on third-party prices is dangerous, project owner must build their own feeding network with their partners. We provide a solution to build up dedicated oracle networks, which are comprised of two components: oracle-node and oracle-contract.
- Oracle-node is used to build up a p2p network, in which all the nodes commit price data into the blockchain in a round-robin way. They crawl trading pair's price from self-specified exchanges and trading pairs and then aggregate the data to calculate a price-weighted by trading volumes.
- Oracle-contract is used to store the price on blockchain, and also used to maintain the permitted node list for the dedicated oracle network.
Demo networks with two nodes
Architecture Overview

Concatenation Calculation Expression of trading-pairs
- in order to support calculate the price by using trading-paris with different quotes, concatenation expression is used
- currently, we support two operators: multiplication and division
- for example
- an exchange only provides the price off two pairs:
WBTC/ETH,ETH/USDC, but sometimes pepole want to use USDC as a standard quote - In this case, a concatenation expression could be used as
WBTC/ETHmulETH/USDC - On the other side, if only
WBTC/USDTandUSDC/USDTare provided, and we want the quote to beUSDC, in this case, use the expression asWBTC/USDTdivUSDC/USDT
- an exchange only provides the price off two pairs:
Price-feeding scheduling
The basic scheduling is in a round-robbin way, each node can do feeding several times one by one. In each round, one node is selected as the leader, who is responsible for collecting prices observed by other nodes, and making a summary to commit data into a smart contract. The leader node does the following tasks:
- verify the signatures in the message sent from other nodes
- check the difference between price observed by other nodes and local, and reject the data with a large difference
- remove the price data recognized to be outliers
- outliers detection details: https://github.com/tokenInsight/ti-oracle/blob/main/node/src/fetcher/aggregator.rs#L88
- calculate the price weighted by the trading volumes
Suppose we maintain a counter for how many times have fed from the beginning of the contract deployed, as a variable N.
- a variable
T, which specifies how many times one node can feed in each round. - a variable
M, which specifies how many nodes in the network are permitted to do price feeding works. - Then, the accepted leader should be the one
nodes[(N / T) % M]
For example, assuming the price data fed once per minute, and there are a total of 3 price feeding nodes: a, b, c, each node can be fed 5 times in a row, then the ordinary sequence should be:
+--------+--------+--------+
| #Count | #Round | Leader |
+--------+--------+--------+
| 0 | 0 | a |
| 1 | 0 | a |
| 2 | 0 | a |
| 3 | 0 | a |
| 4 | 0 | a |
| 5 | 1 | b |
| 6 | 1 | b |
| 7 | 1 | b |
| 8 | 1 | b |
| 9 | 1 | c |
| 10 | 2 | c |
| 11 | 2 | c |
| 12 | 2 | c |
| 13 | 2 | c |
| 14 | 2 | c |
| 15 | 3 | a |
+--------+--------+--------+
What if one node is crashed or disconnected?
- Each round should have a timeout of doing the feeding, like, 300 seconds, to prevent the system outage
- If the time passed between the last feeding and the current block. timestamp, then any node in the permitted list is allowed to feed data
- The details can be checked in the source code of the smart contract: https://github.com/tokenInsight/ti-oracle/blob/main/contracts/src/TIOracle.sol#L69
- Simply speaking, we use the smart contract like the role of the Zookeeper in a traditional distributed system
Developement Guide
Source code overview
contracts/src
└── TIOracle.sol # smart contract of TokenInsight oracle
contracts/test
└── TIOracle.t.sol # unit test for smart contract
node/src
├── bin
│ └── server.rs # the entry point, core logic of node
├── chains
│ ├── eth.rs # functions about crypto sigh, hash, and smart contract invoke
│ └── mod.rs
├── fetcher
│ ├── aggregator.rs # functions about weighted price calculating, and outliers detection
│ ├── expression.rs # caculate price by using operators lik div, mul
│ ├── binance.rs # fetching data from Binance
│ ├── coinbase.rs # fetching data from Coinbase
│ ├── curve.rs # to be done
│ ├── ftx.rs # fetching data from FTx
│ ├── kucoin.rs # fetching data from Kucoin
│ ├── mod.rs
│ ├── okex.rs # fetching data from okex
│ ├── uniswapv2.rs # fetching data from uniswap v2
│ └── uniswapv3.rs # fetching data from uniswap v3
├── flags.rs # command line flags & configuration options
├── lib.rs
└── processor # network processors
├── gossip.rs # p2p gossip messages handlers
├── mod.rs
├── swarm.rs # setup swarm for serving p2p node
└── utils.rs # some utility functions
Run unit test for smart contracts
- firstly, install foundry:
curl -L https://foundry.paradigm.xyz | bash - cd contract && forge test --gas-report
Deploy smart contract
for example, we deploy a contract for Bitcoin price feeding
forge create TIOracle --rpc-url=https://polygon-rpc.com --interactive --constructor-args bitcoin 5 300 --gas-price 65000000000- you can deploy the
contracts/src/TIOracle.solin any way you like, andforgeis just one choice
meaning of the above constructor arguments
- pricing feeding is for
bitcoin - feed
5times each round - timeout for one round is 300 seconds
- pricing feeding is for
adding the address of the permitted transmission nodes
- call this method of the contract
addNode(address newNode) - you can do this by your wallet connected to etherscan
- Or, you can use the tool
cast, as the following command cast send --rpc-url https://polygon-rpc.com ${contract_address} 'addNode(address newNode)' ${node_address} --private-key=$NODE_PRIVATE_KEY --gas-price ${gas_price}
- call this method of the contract
start transmission node
- cargo test --noCapture
- cd node && cargo build
- the binary will be built under the directory
target/debug
- the binary will be built under the directory
- export NODE_PRIVATE_KEY=${you private key}
- start the node
ti-node -c config/node.yaml- explaining for the configuration file
when you start one node successfully, you will get the following logs on your terminal:

use export RUST_LOG=debug, if you want more tracing details.
- join the network
- use
--peersto specify bootstrap nodes with the IPFS-style address separated by a comma - e.g.
ti-node --peers /ip4/192.168.10.228/tcp/55909
- use
Log in or sign up for Devpost to join the conversation.