We’ve been preparing our oracles platform to work with EOSIO. Feel free to test and use it :)
Go through this tutorial to create your first oraclized contract in EOS test network.
We’ll be taking the following steps:
• Creating an account on the Testnet
• Creating a local wallet and importing an account
• Generating boilerplate contract
• Creating interaction endpoint with contract
• Deploying and testing it in EOS Jungle testnet
Step 0. Preparation
Make sure you have the following software on your working PC:
• Scatter browser extension
• NodeJS at lease v9.x.x
• Docker at least v17.x.x.
Before we start, open your terminal and pull required image from DockerHub:
docker pull eosio/eos-dev:v1.1.1
And install development tools as global node module:
npm install — global DucaturFw/eosic
Step 1. Create Jungle account
Every EOS account requires an Owner key and an Active key. The Owner key has superpowers, and as such it should be stored as safely as possible. The Active key is your day to day usage key. For your own security store and keep your private keys privately.
In the tutorial case we’ll use the same key for Owner and Active permissions.
Open and unlock scatter extension in your browser. In key pairs window press New button and follow the instructions to generate keys!
NB: Don’t forget to copy and store your keys. There is no way to recover them in case of loss!
Now that we have our keys, let’s head over to the Jungle testnet site. Click on the “Create account” button.
NB: Esure you’re using public keys against private. They’ll start with “EOS…”
Now let’s double-check that the account was created. Close that window and on the home page click on “Account info” and type in your new account name and click “Get”. You should see the information of your newly created account, unfortunately with zero balance. We’ll fix that in a blink of an eye. (Special note here: As of this writing, the testnet is creating the account with the same Owner and Active key. Double check this under account info and we’ll use that when importing to Scatter).
Let’s get some test EOS and Jungle tokens into this account. Hit the “Faucet” link and type in your account name and click “Send coins”.
Step 2. Create local wallet and import account
We’ll use docker to isolate keosd from the host machine. First let’s cover the running process. Create folder to store docker-compose.yml file in it:
mkdir ~/eos && cd ~/eos
We’ve prepared a gist for you which is ready to use. Just copy the contents and paste to docker-compose.yml:
version: "3"
services:
nodeosd:
image: eosio/eos-dev
command: /opt/eosio/bin/nodeosd.sh --data-dir /opt/eosio/bin/data-dir -e --http-alias=nodeosd:8888 --http-alias=127.0.0.1:8888 --http-alias=localhost:8888
container_name: nodeosd
hostname: nodeosd
ports:
- 8888:8888
- 9876:9876
expose:
- "8888"
volumes:
- ./data/nodeos:/opt/eosio/bin/data-dir
cap_add:
- IPC_LOCK
stop_grace_period: 10m
keosd:
image: eosio/eos-dev
command: /opt/eosio/bin/keosd --wallet-dir /opt/eosio/bin/data-dir --http-server-address=127.0.0.1:8900 --http-alias=keosd:8900 --http-alias=localhost:8900
container_name: keosd
hostname: keosd
links:
- nodeosd
volumes:
- ./data/keosd:/opt/eosio/bin/data-dir
stop_grace_period: 10m
Run environment with docker-compose up. It should print default output from nodeos and keosd:
We aren’t interested in the local node, it’s just requirement to sign transaction before we pass them to the Jungle Testnet. For the interaction we’ll use cleos with lots of parameters. It will be helpful to add special alias:
alias cleos=”docker exec -ti keosd /opt/eosio/bin/cleos -u https://jungle.eos.smartz.io:443/ — wallet-url http://localhost:8900"
Let’s test our alias and connection:
cleos get info
It should provide actual blockchain info of the Jungle. Check head_block_num, it should be at least 11 037 948 (or much more… it depends on the period you’re reading this article)
Create wallet
To create the default wallet just print cleos wallet create and save the password (from output) to unlock it in the future.
It doesn’t have any keys yet and we should import them. After previous step we have public and private key. So, let’s add private key to our wallet with:
cleos wallet import — private-key <PASTE KEY HERE>
Now we ready to sign transaction with our account from Owner and Active permissions.
Step 4. Create interaction endpoint
1. Create EOSIC project
Open terminal and create directory on your computer for tutorial project:
mkdir ~/ducatur-tutorial
Open directory and initialize default eosic environment:
cd ~/ducatur-tutorial && eosic init && npm install.
EOSIC will prepare file structure and base configuration for us:
- ~/ducatur-tutorial
— /contracts — folder with source codes of project contracts
— /migrate — JS files runs immediately after eosic start command
— /test — Mocha&Chai test files
— eosic.json — EOSIC configuration
— config.ini — Local node configuration
— package.json — NPM dependencies
2. Create contract
In the root directory of our project run command: eosic contract priceoraclize.
Command creates folder and boilerplate contracts/priceoraclize/priceoraclize.cpp file for us.
Open .cpp file in any text editor and paste generated code.
NB: We recommend using Visual Studio Code with CPP Extension
As a result we have contract without the custom logic, but it’s ready to consume external data to operate with.
3. Endpoint and oraclize struct API
At the end of the contract definition add new method interact:
// @abi action
void interact()
{
// implementation goes here
}
Also, append method as part of ABI at bottom of the contract:
EOSIO_ABI(priceoraclized, (interact)(setup)(pushprice))
Check freshness of data
We’ll prevent the interaction if the data is outdated. Custom structure to store oraclized data provides useful api to check freshness and availability of data:
data.fresh() — returns true if data is fresh enough, overwise false
data.require_update() — returns true if data is waiting for update, overwise false
data.exists() — returns true if data has pushed early, overwise false
data.value() — returns data if it is available, overwise throws
data.get() — returns raw oraclized object to access timestamps
Let’s add some assets in method to throw error in unexpected cases:
// @abi action
void interact(uint64_t amount)
{
eosio_assert(ethbtc.exists(), “ethbtc data didn’t pushed yet”);
eosio_assert(ethbtc.fresh(), “ethbtc data has been outdated”); // get newest oraclized price
price current_ethbtc = ethbtc.value();
eosio_assert(current_ethbtc.value <= 4100000, “Oraclized price lower than predefined 0.041 BTC per ETH threshold”);
eosio_assert(current_ethbtc.decimals == 8, “Unexpected oraclized price tolerance”);
if (ethbtc.require_update())
{
eosio::print(“Oraclized data is fresh enough, but awaiting for coming update”);
} auto oraclize_record = ethbtc.get(); // same as ethbtc.fresh()
eosio_assert(oraclize_record.best_before <= now(), “ethbtc data has been outdated”);
// same as ethbtc.require_update()
if (oraclize_record.update_after <= now())
{
eosio::print(“Oraclized data is fresh enough, but awaiting for coming update”);
} eosio::print(“\n ETH/BTC price is: “, current_ethbtc.value);
}
As you can see you could make checks manually with ethbtc.get().best_before or use this data in custom logic
NB! ethbtc.get().best_before — <predefined retire data block count> equals to moment when data appear!
Latest step is preparing ABI and WAST files by compilation of source files. With eosic it is as simple as possible. Just run eosic compile in root of the project and let eosic to do his job.
Check the compilation result with ls -lha ~/ducatur-tutorial/contracts/priceoraclize. It should print list of files contains priceoraclize.abi and priceoraclize.wast:
total 608
drwxr-xr-x 7 aler staff 238B 13 авг 15:04 .
drwxr-xr-x 6 aler staff 204B 15 авг 10:05 ..
-rw-r — r — 1 aler staff 0B 9 авг 18:56 .gitkeep
-rw-r — r — 1 aler staff 2,6K 15 авг 10:00 priceoraclize.abi
-rw-r — r — 1 aler staff 5,9K 15 авг 10:02 priceoraclize.cpp
-rw-r — r — 1 aler staff 5,9K 15 авг 10:02 priceoraclize.hpp
-rw-r — r — 1 aler staff 24K 15 авг 10:00 priceoraclize.wasm
-rw-r — r — 1 aler staff 267K 15 авг 10:00 priceoraclize.wast
Step 5. Deploy to the Jungle
Before we set code and provide ABI to network we should prepare our account to store it and execute. It requires some RAM, CPU and NET.
Check how much it has: cleos get account <your account name>
By default newly created accounts in the Jungle already have some RAM space and NET and CPU bandwidthes.
memory:
quota: 202.1 KiB used: 3.365 KiBnet bandwidth:
staked: 100.0000 EOS (total stake delegated from account to self)
delegated: 0.0000 EOS (total staked delegated to account from others)
used: 0 bytes
available: 18.58 MiB
limit: 18.58 MiBcpu bandwidth:
staked: 100.0000 EOS (total stake delegated from account to self)
delegated: 0.0000 EOS (total staked delegated to account from others)
used: 0 us
available: 3.68 sec
limit: 3.68 secEOS balances:
liquid: 10000.0000 EOS
staked: 200.0000 EOS
unstaking: 0.0000 EOS
total: 10200.0000 EOS
Buy 1000Kb of RAM with cleos system buyram — kbytes <your account> <your account> 1000.
First of all we need to copy compiled files to docker container:
docker cp ~/ducatur-tutorial/contracts/priceoraclize.wast keosd:/contract/priceoraclize.wast
docker cp ~/ducatur-tutorial/contracts/priceoraclize.abi keosd:/contract/priceoraclize.abi
Now we are ready to push them into the network. It could be done with two transactions:
- Setting code transaction by: cleos set code <your account> /contracts/priceoraclize.wast
- Setting ABI transaction by: cleos set abi <your account> /contracts/priceoraclize.abi
To finalize the setup of our contract we need to configure permission to allow the contract making actions from itself. All actions made by contract run with special eosio.code permission. It’s requires to add special signer to permission table:
{
“threshold”: 1,
“keys”: [
{
“key”: <YOUR PUBLIC KEY>,
“weight”: 1
}
],
“accounts”: [
{
“permission”: {
“actor”: <YOUR ACCOUNT NAME>,
“permission”: “eosio.code”
},
“weight”: 1
}
],
“waits”: []
}
Pay attention to “key”: <YOUR PUBLIC KEY> as well as “actor”: <YOUR ACCOUNT NAME>. First allowance tells about manualy signed transaction from public key, but seconds allows to providen account use eosio.code to sign actions.
Replace <YOUR PUBLIC KEY> with public key (start with EOS…) generated before and <YOUR ACCOUNT NAME> with connected account. After just remove all spaces and line endings and run next command with this argument:
cleos set account permission <YOUR ACCOUNT NAME> active \
{“threshold”:1,”keys”:[{“key”:”<YOUR PUBLIC KEY>”,”weight”:1}],\
“accounts”:[{“permission”:{“actor”:”<YOUR ACCOUNT NAME>”,”permission”:”eosio.code”},\
“weight”:1}],”waits”:[]} \
owner
NB!: Pay attention on last owner argument. It’s parent to setting permission.
Setup oraclized contract
Last but not least thing to do is setup of request. Generated contract has setup method to do that.
Push action to contract to call setup method with next arguments:
cleos push action <YOUR ACCOUNT> setup ‘{“administrator”:”<YOUR ACCOUNT>”,”oracle”:”<ORACLE ACCOUNT NAME>”,”master”:”<MASTER ACCOUNT NAME>”}’ -p <YOU ACCOUNT>@active
You should change <YOUR ACCOUNT> with contract account name and <ORACLE ACCOUNT NAME> with provided oracle account in generation step as well as <MASTER ACCOUNT NAME> with provided master account.
Execute command and request current requests table cleos get table <MASTER ACCOUNT NAME> <MASTER ACCOUNT NAME> request:
{
“rows”: [
{
“task”: “0x5d4e98a94bf3e6c7d539f4988cc5f7557fe12c8e53ec6a193b7c0ad92dafe188”,
“contract”: “<YOUR ACCOUNT NAME>”,
“timestamp”: 1534576794,
“active”: 1
}
],
“more”: false
}
Now you’ve fully configured contract ready to consume oraclized price data. After few block our oracles will push data to your contract.
Sure, you will get use out of this. We really want to hear your feedback, so we can give you several places to release it:
- Our lovely Telegram chat
- Our lovely EOS Github and Backend Github
If you want us to cover some topics regarding the usage of Oracles feel free to inform us in our chat.