Overview
Due to consensus mechanisms used by blockchain networks, they are not able to directly fetch external data. Besides blockchain technology, an oracle system is a vital requirement using which blockchain platforms acquire the flexibility of handling all kinds of applications and digital autonomous organizations by accessing external data. The availability of an oracle system on a blockchain network makes it to be externally-aware. In fact, Oracles are a kind of technology through which users are able to inject real-world data into their smart contracts.
Bridge Oracle’s system is a public oracle technology based on the BNB Chain network that provides all oracle services for blockchain-based DeFi platforms and decentralized applications. Various services of the Bridge Oracle project are categorized into 3 cases to which users’ queries can be sent:
- Public Oracle System
- Enterprise Oracle System
- Decentralized Oracle System
Each of the above oracle systems has its own use case which is going to be discussed in the following sections.
Query & Submitting Request for External Data
A query is a request for real-world data on behalf of users’ smart contracts which should be written and submitted in a special format according to the utilized data source. Subsequently, oracle data carriers can recognize users’ requests and respond to them.
Parsing Helpers
Responses to requests are in the format of JSON, XML or HTML. Thus, in order to extract an exact element – for example in a JSON API response -such raw results should be parsed, initially. In order to parse such data, parsing helpers should be applied to it. Examples:
- JSON Parser: Bridge built-in JSON parser can parse and extract a specific element from JSON format results if the request is sent from users’ smart contracts using the following format
json(https://api.kraken.com/0/public/Ticker?pair=ETHUSD).result.XETHZUSD.p.1
- XML Parser: Bridge built-in XML parser can parse and extract a specific element from XML format results if the request is sent from users’ smart contract using following format:
"xml(https://samples.openweathermap.org/data/2.5/weather?q=London&mode=xml&appid=439d4b804bc8187953eb36d2a8c26a02).current.city.country"
- HTML Parser: This format can be used for HTML scraping purposes using XPATH language. Requests should be sent using following format:
html(https://wikipedia.org/).//*[contains(@class, 'central-textlogo__image')]/text()
Also the matter of how one can use above formats will be discussed further.
Oracle Types
There are various types of services in the Bridge Oracle system. In order to use a specific type of service, a proper API contract should be imported into the user’s smart contract based on the owner’s requirements. So, there are various API contracts in the Bridge ecosystem that each has a special functionality. Various API contracts include:
- Public API contract
- Enterprise API contracts that are dedicated to each company or organization which is volunteer to sell data for BRG token
- Decentralized API contract
It should be noted that using each of the above contracts depends on the user’s requirements.
Public API Contract
Public API contract has been designed to simply obviate requirements from external data that the validity of data or proving the validity of data is not that important for the user. Of course, in this method, various kinds of proofs will be provided to resolve proof of the validity of data in a near future.
Enterprise API Contract
Enterprise API contract has been provided for enterprise companies and organizations that own dedicated and special data which are not published and shared publicly. These companies and organizations can sell such data using the Bridge system and users who need such data can import that company’s dedicated API contract. This API contract redirects users’ requests to that company’s contract on which the company’s dedicated oracle data carriers watch and respond to users’ contracts directly and inject dedicated data into their contract privately. Companies and organizations can exchange their dedicated information for BRG token which is paid by users’ smart contracts. In fact, enterprise organizations can make money by selling data to Bridge users. This is one of Bridge Oracle’s business plans. Business plans are discussed in the whitepaper.
Decentralized API Contract
A Decentralized API Contract has been prepared for those users that need to implement a fully decentralized platform. Thus, they need to fetch external data using a decentralized oracle system as well. As developing such infrastructure is in our future plans, details of designing such a system will be reported in the future.
Public Oracle System
As mentioned, Bridge’s Public Oracle system has been designed to simply obviate requirements from external data, which their validity or proving their validity is not that important for the user. Of course, in this method, various kinds of proofs will be provided to resolve proof of the validity of data in a near future.
Public Oracle Data Sources
Data sources are various kinds of references such as a website or a web API from which required data is requested by oracle data carriers based on users’ needs. One should note that each data source has a special use case and functionality and choosing a suitable data source is of great importance. There are several critical data sources that are expected to exist in the Bridge Oracle system. Various data sources that are supported by Bridge include:
URL: URL data source enables users to fetch every kind of data using HTTP/HTTPS request APIs into their smart contracts. This data source supports both HTTP/HTTPS GET and POST requests. If only one parameter is sent in the query, the request will be considered as an HTTP/HTTP GET request, automatically. Otherwise, if a second parameter is sent through a query, the oracle will treat it as a POST request.
Complex URL: This data source is similar to the mentioned URL, but has a little more flexibility for fetching data.
WolframAlpha: This data source redirects users’ requests and questions to the computational knowledge engine of WolframAlpha Company which is able to answer what one wants to calculate or know about. WolframAlpha computational intelligence answers the inquiries and corresponding responses are returned to the users’ smart contracts.
Random: This data source generates random numbers which have many critical use cases including statistical sampling, computer simulation, cryptography, completely randomized design, scientific calculations, etc.
Nested: This case enables users to utilize the combination of different types of data sources or multiple requests of the same data source that returns a unique result.
Quick Start
Integrating BNB Smart Chain Smart Contracts with Bridge Oracle System
In order to integrate a BNB Smart Chain smart contract with Bridge oracle system, previous knowledge of Solidity language and BEP-20 is required. Interaction between BNB Smart Chain smart contracts and Bridge oracle system is an asynchronous procedure. Any kind of request on behalf of user which is sent by their smart contract is subjected in two steps:
⦁ In first step, user sends a request by executing a function and performing a transaction and broadcasting a query into BNB Smart Chain blockchain. Executed function has specific properties that is able to exactly manifest the user’s request to Bridge oracle system which is constantly monitoring BNB Smart Chain blockchain for such requests.
⦁ In second step, based on broadcasted parameters, Bridge recognizes what data is needed. Then Bridge fetches or computes corresponding result and after that it signs and broadcasts a transaction that injects data into the user’s contract. In fact Bridge triggers __callback function (which should be put by users into their smart contracts) and injects data into this method. Of course note that only Bridge’s oracle data carriers can trigger this function. Calling __callback method by other invalid sources is impossible.
Public Oracle
In this section, several examples of public oracle are presented in order to show how to use various data sources.
Cautious: It is better to use BNB Testnet as a testing environment. Therefore Bridge oracle is using BNB Smart Chain public node for catching events and replying to requests in BNB Testnet; so, it is not completely efficient and it is possible that users’ queries to be failed in it.
Connecting to Public Oracle Using BridgePublicAPI.sol
Before starting to develop your own contract, you need to take special actions to connect your main smart contract to public oracle. You need to import BridgePublicAPI.sol into your smart contract. There are two methods to integrate your contract with public oracle as follows:
⦁ using Remix upload tool
⦁ copying the whole content into a *.sol file in local directory of main contract and import the file into the main smart contract
In first method you need to refer to Remix link and push GitHub button.
Then copy BridgePublicAPI.sol directory from Bridge Oracle System GitHub repository in the blank field which is something like https://github.com/BridgeOracle/Bridge-Oracle/blob/master/BridgePublicAPI.sol and then click OK.
In second method you can refer to BridgePublicAPI.sol file in GitHub and copy the full content into a *.sol file in local directory of your main smart contract as follows:
Then you should import the BridgePublicAPI.sol into your main smart contract as follows:
Using one of the above methods, you can integrate your smart contract with public oracle of Bridge ecosystem. After integration you can develop your thoughts into your main smart contract.
URL Data Source
JSON Queries
Following example can fetch real-time exchange rate of BTC/USD from Coinbase website. Every time send_query() function is called, latest price of BTC/USD is injected into the smart contract.
pragma solidity ^0.5.9;
//Import "BridgePublicAPI.sol" from https://github.com/cryptoland-blockchain-laboratory/Bridge-oracle-system/ using Remix IDE upload tool or copy the whole content into a *.sol file in your smart contract's local directory and import the file into your main smart contract.
import "./BridgePublicAPI.sol"; contract priceFeed is BridgePublicAPI { event price(uint256 _amount); string public BTCUSD; function send_query() public { bridge_query("URL", "json(https://api.pro.coinbase.com/products/BTC-USD/ticker).price"); } function __callback(bytes32 _myid, string memory _result) public { require(msg.sender == oracle_cbAddress()); BTCUSD = _result; uint256 res = parseInt(_result, 0); emit price(res); } }
Note that this is a sample from user’s smart contract. Above example includes several important sections:
⦁ It shows that user’s smart contract needs to inherit from Bridge’s Public oracle API (BridgePublicAPI.sol).
⦁ Any further actions and computations should be written in __callback function in order to be calculated based on fetched result. In this example BTCUSD state variable is filled by _result that is injected by Bridge oracle system.
XML Queries
In this example fetching country of a region is presented:
pragma solidity ^0.5.9; import "./BridgePublicAPI.sol"; contract country is BridgePublicAPI { event countryLog(string _amount); string public countryName; constructor() public { countryName = "US"; } function send_query() public { bridge_query("URL", "xml(https://samples.openweathermap.org/data/2.5/weather?q=London&mode=xml&appid=439d4b804bc8187953eb36d2a8c26a02).current.city.country"); } function __callback(bytes32 _myid, string memory _result) public { require(msg.sender == oracle_cbAddress()); countryName = _result; emit countryLog(_result); } }
As you can see, we initially filled countryName with US string but after executing send_query() function, the countryName was changed into GB .
HTML Queries
In this example fetching name of Wikipedia website is expected:
pragma solidity ^0.5.9; import "./BridgePublicAPI.sol"; contract wikiName is BridgePublicAPI { event LogWikiName(string _name); string public name; constructor() public { send_query(); } function send_query() public { bridge_query("URL", 'html(https://wikipedia.org/).//*[contains(@class, 'central-textlogo__image')]/text()'); } function __callback(bytes32 _myid, string memory _result) public { require(msg.sender == oracle_cbAddress()); name = _result; emit LogWikiName(_result); } }
Random Data Source
Using this data source, you can infuse a random digit between two numbers into your smart contract. For now and for test use cases in testnet, Bridge Oracle use random.org API, however we will implement a method to create random number from within a TEE (trusted execution environment like Ledger or Intel SGX) in order to be highly reliable for severe cases in main net in the future. This version is for test and temporary. You can infuse a random number like following:
pragma solidity ^0.5.9;
import "./BridgePublicAPI.sol";
contract randomNumber is BridgePublicAPI {
event myRandomNum(string randNum);
string public randomNum;
function send_query() public {
bridge_query("RANDOM", '1-10'); //The result of this format will be a random number between 1 and 10. Template is like following: bridge_query("RANDOM", 'minimum number-maximum number') This format will infuse a random number between min and max numbers in which min and max numbers are both included.
}
function __callback(bytes32 _myid, string memory _result) public {
require(msg.sender == oracle_cbAddress());
randomNum = _result;
emit myRandomNum(randomNum);
}
}
WolframAlpha Data Source
Using this data source, users are able to ask any knowledge-based questions in their smart contracts and Bridge Oracle will reply the questions based on information of WolframAlpha knowledge-based engine. One can infuse data from wolframalpha knowledge engine using “WOLFRAMALPHA” data source through following instructions:
pragma solidity ^0.5.9;
import "./BridgePublicAPI.sol";
contract president is BridgePublicAPI {
event presidentName(string name);
string public name;
function send_query() public {
bridge_query(“WOLFRAMALPHA”, ‘who is 44th US president?’); //The result will be 44th president of US, ‘Barack Obama’.
}
function __callback(bytes32 _myid, string memory _result) public {
require(msg.sender == oracle_cbAddress());
name = _result;
emit presidentName(name);
}
}
Queries in the Future
Execution of queries can be scheduled for a future date. Users can decide to receive the response to their queries both after a certain period of time or on a certain time stamp. bridge_query function accepts an argument as a delay parameter from the current time in seconds or a future time stamp on which query will be executed. It should be noted that in both methods the interval should not be more than 90 days. It means that for the case of delay time, this parameter should not be defined as more than 3 days or 259,200 seconds and for the case of upcoming time stamp, the difference between the current time stamp and inserted argument should not be inserted more than 3 days or simply the argument should be less than current timestamp +259,200. Otherwise, the user will receive an empty argument.
You can see an example for each case in the following:
pragma solidity ^0.5.9;
import "./BridgePublicAPI.sol";
contract BTCprice is BridgePublicAPI {
function send_query() public {
bridge_query(30, "URL", "json(https://www.therocktrading.com/api/ticker/BTCEUR).result.0.last");
}
string public res;
event btcPrice(string _price);
function __callback(bytes32 _myid, string memory _result) public {
require(msg.sender == oracle_cbAddress());
res = _result;
emit btcPrice(_result);
}
}
In the example above, the query will be executed exactly after 30 seconds of executing the send_query function.
pragma solidity ^0.5.9;
import "./BridgePublicAPI.sol";
contract BTCprice is BridgePublicAPI {
function send_query() public {
bridge_query(now + 30, "URL", "json(https://www.therocktrading.com/api/ticker/BTCEUR).result.0.last");
}
string public res;
event btcPrice(string _price);
function __callback(bytes32 _myid, string memory _result) public {
require(msg.sender == oracle_cbAddress());
res = _result;
emit btcPrice(_result);
}
}
In the above example, the query will be executed exactly on the timestamp of right now +30 second.
Updating a Variable Every n Seconds
Using scheduled queries and a trick in the __callback method, one can inject changing data every (n) seconds, repetitively which is called recursive queries. This method is useful for implementing periodic updates of on-chain reference data such as a cryptocurrency price or checking an off-chain condition, regularly. Using a new call-in __callback method will result in implementing recursive queries.
Notice: Recursive queries should be used cautiously, as the contract will pay to the oracle every time that sends a new query. Therefore, it is recommended to use purposeful queries.
In the following example, if the send_query function is executed for the first time, after that as long as the contract has enough funds to pay for the Bridge’s fees, BTC/EUR exchange rate will be updated in the res variable every 60 seconds.
pragma solidity ^0.5.9;
import "./BridgePublicAPI.sol";
contract BTCprice is BridgePublicAPI {
function send_query(uint256 delay_time) public {
bridge_query(delay_time, "URL", "json(https://www.therocktrading.com/api/ticker/BTCEUR).result.0.last");
}
string public res;
event btcPrice(string _price);
function __callback(bytes32 _myid, string memory _result) public {
require(msg.sender == oracle_cbAddress());
res = _result;
emit btcPrice(_result);
send_query(60);
}
}
Query ID
Using Query ID for Better Control on Process
When a user sends a request by calling the bridge_query() function, a unique query id is dedicated to the specific request. The query id identifies the specific request which is returned to the user’s smart contract as a parameter of the __callback() function.
It is recommended that smart contract developers use specified query id as the following example in order to ensure that each query response is processed only once and the abusage of smart contract logic is avoided.
pragma solidity ^0.5.9;
import "./BridgePublicAPI.sol";
contract BNBprice is BridgePublicAPI {
event price(string BNBprice);
constructor() public {
send_query("json(https://api.binance.com/api/v3/ticker/price?symbol=BNBUSDT).price");
}
string public BNBUSD;
mapping (bytes32 => bool) public pendingQueries;
function send_query(string memory arg) public {
bytes32 queryId = bridge_query("URL", arg);
pendingQueries[queryId] = true;
}
function __callback(bytes32 _myid, string memory _result) public {
require(msg.sender == oracle_cbAddress());
require(pendingQueries[_myid] == true);
BNBUSD = _result;
emit price(BNBUSD);
delete pendingQueries[_myid];
}
}
Also it should be noted that query id can be used to behave differently into the __callback() method for each specific query ID in cases that more than one pending callback result is expected to be injected by Bridge Oracle into user’s smart contract.
pragma solidity ^0.5.9;
import "./BridgePublicAPI.sol";
contract BNBprice is BridgePublicAPI {
event result(string _result);
mapping (bytes32 => bool) public pendingQueries;
bytes32 public BTCid;
bytes32 public BNBid;
function send_query1() public {
BTCid = bridge_query("URL", "json(https://api.pro.coinbase.com/products/BTC-USD/ticker).price");
pendingQueries[BTCid] = true;
}
function send_query2() public {
BNBid = bridge_query("URL", "json(https://api.binance.com/api/v3/ticker/price?symbol=BNBUSDT).price");
pendingQueries[BNBid] = true;
}
function __callback(bytes32 _myid, string memory _result) public {
require(msg.sender == oracle_cbAddress());
if(_myid == BTCid) {
require(pendingQueries[BTCid] == true);
emit result(_result);
delete pendingQueries[BTCid];
}else if (_myid == BNBid) {
require(pendingQueries[BNBid] == true);
emit result("hello world");
delete pendingQueries[BNBid];
}
}
}
Pricing System
Payment Methods
In order to place a request for injecting data into the Bridge Oracle system, users are asked to pay the required fee. Bridge Oracle provides two various payment methods for users to pay the cost of their requests including:
- BRG (Bridge’s native token)
- BNB
The price of a request is calculated based on a certain on-chain algorithm and the amount is paid automatically from the user’s smart contract. In fact, in order to use Bridge Oracle’s services, the user’s contract is charged a fee in BRG or BNB tokens.
Before discussing the payment process, note that the price of the request is calculated based on BNB. Thus, in order to pay the price in BRG, an up-to-date real-time exchange rate of BRG/BNB must be available for the pricing system, using which BRG-based prices can be easily calculated every moment. For solving this issue, a bot has been developed that tracks BRG/BNB’s exchange rate constantly and compares it with the rate which is injected into Bridge Oracle’s pricing system. If the difference is more than 1%, the bot updates the price and injects the new exchange rate into the blockchain. Then, the costs of the next requests are calculated based on the new exchange rate.
In the first step, Bridge Oracle checks the BRG balance of the user’s smart contract. If there are enough tokens in the user’s contract according to the cost of the request, Bridge will automatically charge the related amount of BRG tokens to the user’s contract. If the BRG balance in the user’s contract is insufficient, in the second step, Bridge Oracle will check the BNB balance of the user’s contract. This time, if there is enough BNB in the user’s contract, Bridge will charge the related amount of BNB tokens to the user’s contract. Finally, if there is not enough BRG and BNB balance in the user’s contract, the user’s request will be rejected and no response will be reported into the user’s smart contract by the Bridge Oracle system.
Note: Charging BRG tokens to the user’s contract includes a percentage of discount in comparison to BNB-based payments. In fact, paying BRG tokens as the fee will be more affordable than paying the fee in BNB as of proceeding with placing a request in the Bridge Oracle system.
Pricing Algorithm
As mentioned before, the price of each request is calculated based on a special algorithm. The price is consisted of 3 main parameters:
Query Price = Cost of Query Type + Query Fee Limit + Maximum Bandwidth Price
Each kind of query from an oracle type or data source point of view has a specific price that creates the “Cost of Query Type” part. In fact, this is the cost required for using Bridge’s services and is consisted of two parts that will be calculated as follows:
Cost of Query Type = Base Price × Multiplier
The base price was introduced to calculate the cost of Bridge’s services. The base price is the unit price of providing service for a request with Multiplier 1. Multiplier shows the complexity of a task based on resource consumption. One can investigate the base price and multiplier of various types of requests in the following tables:
Data Sources | Base Price | Multiplier | Cost of Query Type |
---|---|---|---|
URL | 0.0014 BNB | *1 | 0.0014 BNB |
RANDOM | 0.0014 BNB | *1 | 0.0014 BNB |
Note: Base Price is constant for all kinds of requests; however, it may be changed based on conditions.
Query Fee Limit is determined by users for their requests. In fact, this part limits the complexity of the callback function which is triggered by Bridge Oracle. This is the cost that is paid to the blockchain as a contract triggering fee in order to generate the user’s contract. Users should test their contract in the testnet to achieve the closest amount with which the response is sent to their contract. This is because the difference between paid Query Fee Limit and the amount with which the oracle can trigger their contract is not refunded.
Note: If the Query Fee Limit is not set by the user, Bridge Oracle will assign Default Fee Limit to the user’s request that its amount is equal to 0.0014 BNB.
In order to fully understand the concept, please consider the following query:
bridge_query("URL", "json(https://api.pro.coinbase.com/products/BTC-USD/ticker).price", 2500000);
In this query, the maximum cost which can be paid to BNB Chain for calling the __callback() function and triggering the user’s smart contract by Bridge Oracle, must be less than 0.0007 BNB which is equal to 250,000 energy when calling the function.
Maximum Bandwidth Price is a constant amount which is consumed when the oracle wants to trigger users’ smart contracts.
Note: Max Bandwidth Price is a constant amount which is equal to 0.0000029 BNB that may be changed based on conditions.
Cautious: For ease of development and proceeding with initial tests, Bridge Oracle will not charge a contract for its first request. That is because if users want to send a request from their smart contracts right away after deployment, they need to write their request to the constructor. Therefore, Bridge responds to users’ smart contracts with no fees charged for the first time.