Chapter 2
Estimated time: 1 hr 3 mins
While a data source can be any type of executable or a Python script, in this course, we'll focus on creating a data source as a Python script. As the data source will be executed in Yoda's Python runtime, the Python packages that a data source are able to use is limited to what Yoda's runtime has available. The available packages on Yoda's Python runtime can be found here.
A data source runtime can be defined into three main sections:
As Yoda treats each data source as an executable, a shebang line in the python script is required to help define the type and path for Yoda when executing the script. If there are any inputs for the script, it is handled as arguments following the executable call. Therefore, we can use the argv()
function from Python's sys
module in order to get the command line arguments that was passed to the Python script.
The example below shows an oracle script that takes two input, symbol
and multiplier
of type str
and int
respectively:
#!/usr/bin/env python3 import sys def main(symbol: str, multiplier: int): pass if __name__ == "__main__": symbol = sys.argv[1] # The symbol input multiplier = int(sys.argv[2]) # The multiplier input print(main(symbol, multiplier))
During the computation flow, the requested data should be retrieved and parsed into a format we expect our oracle script to retrieve.
For example, if our oracle scripts requires the price of a particular asset and the endpoint we've queried returns a message it the following format:
{ "bitcoin": { "usd": 19965.3 } }
We'd need to find a method to parse the data into just 19965.3
.
Below is an example of a data source that queries an endpoint for the Bitcoin Price Index (BPI) and parses the given information into just the BPI rate.
#!/usr/bin/env python3 import requests def main(quote_currency): # Checks if quote currency is supported. # If not, raises an exception. if quote_currency not in {"USD", "GBP", "EUR"}: raise Exception(f"Quote currency {quote_currency} is not supported") # Sends a HTTP GET method to the URL given. r = requests.get("https://api.coindesk.com/v1/bpi/currentprice.json") # If response status code is not 200, raises an exception. r.raise_for_status() # Attempt to get a specific value from the response. If the response # format is not as expected, raises an exception. try: return r.json()["bpi"][quote_currency]["rate_float"] except Exception as e: raise e
Now that we're able to get that the oracle script requires, we need a method to send the data back to BandChain. In order to do so, we will need to print our output so that Yoda is able to read the output and send the data back to BandChain.
In the case of the data source not executing as expected, we can use sys.exit(exit_code)
to set the exit code to 1
as to identify that the data source was not executed successfully.
Below shows an example implementation of the data sources input, computation and execution:
#!/usr/bin/env python3 ## THIS IS CODE IS STRICTLY TO BE USED AS AN EXAMPLE AND IS NOT ## MEANT FOR PRODUCTION USE import sys import requests URL = "https://api.coindesk.com/v1/bpi/currentprice.json" def main(quote_currency): # Checks if quote currency is supported. # If not, raises an exception. if quote_currency not in {"USD", "GBP", "EUR"}: raise Exception(f"Quote currency {quote_currency} is not supported") # Sends a HTTP GET method to the URL given. r = requests.get("https://api.coindesk.com/v1/bpi/currentprice.json") # If response status code is not 200, raises an exception. r.raise_for_status() # Attempt to get a specific value from the response. If the response # format is not as expected, raises an exception. try: return r.json()["bpi"][quote_currency]["rate_float"] except Exception as e: raise e if __name__ == "__main__": try: # Takes the first command line argument passed to the Python script # and runs uses it as the parameter for main(). # The result of main is then printed print(main(sys.argv[1])) except Exception as e: # Prints out any exceptions that the script comes acrosses print(str(e), file=sys.stderr) # Exits with exit status 1 sys.exit(1)
Here's a challenge to help test your understanding of the knowledge acquired in the past 3 chapters:
Given the following endpoint: https://api.binance.com/api/v3/klines, with the following documentation where its endpoints parameters are as follows:
Name | Type | Mandatory | Description |
---|---|---|---|
symbol | STRING | Yes | Pair of asset to query. e.g: BTCUSDT |
interval | ENUM | Yes | Candlestick interval. e.g: 1d |
startTime | LONG | No | Candlestick start time in Unix milliseconds timestamp format. Default is the latest candle. e.g. 1632398400 |
endTime | LONG | No | Candlestick end time in Unix milliseconds timestamp format. Default is the latest candle. e.g. 1632398400 |
limit | INT | No | Limit of results returned. Default is 500. e.g: 494 |
Create a data source that queries the API for a daily candlestick's closing price given the candlestick's opening timestamp and the candlestick pair. The code should return an error along with exit status 1
if the pair does not exist or does not have an candle that opens at the specified timestamp.
The data source should take the following inputs:
Field | Type | Description |
---|---|---|
pair | str | The candlestick pair to query |
timestamp | int | The candlestick's start time in Unix timestamp in seconds |
The example inputs and expected outputs for testing can be found below:
Input:
pair | timestamp |
---|---|
BTCUSDT | 1502928000 |
ETHUSDT | 1662508800 |
BANDUSDT | 1618531200 |
Output:
price |
---|
4285.08000000 |
1630.00000000 |
20.96470000 |