Our First Dragonchain Smart Contract: a Simple Price Oracle

It. Is. Time.

Let’s write up a simple shell script and deploy it to our Dragonchain business node as a smart contract.

Goals for This Post

By the end of this post, we will have:

  1. Written a simple shell script to access the CoinMarketCap API and pull the current price data for Dragonchain
    1. This script will take as an argument our CoinMarketCap API key
    2. It will output a simple JSON object which will become the payload for the response from our smart contract
  2. Written a docker file with the necessary “stuff” our image will need to run our script and publish the image to Docker Hub
  3. Created a smart contract on our Dragonchain business node that will execute on a standard cron schedule (so that we can ledger the current Dragonchain price data every <x> minutes|hours|etc.)
    1. Will have tested creating our smart contract with both DCTL and using the console website
  4. Updated our already installed smart contract

By the way: HUMONGOUS thanks to Dragonchain developer Adam (@cheeseandcereal on the Slack) for both the weather oracle demo video and his constant one-on-one help getting me going.

Let’s get into it.

Get the Git

All of the files for this contract (there aren’t many for this particular one) and the next will be available on the Community Github:

The Script

Our script is going to be pretty straightforward. It will call the CoinMarketCap API via curl, then parse the output using a tool called jq to extract only the USD price data for Dragonchain, then echo a new JSON object as a string that will become our payload.

(By the way: you can sign up for a free CoinMarketCap API key here.)

It looks like this:

#!/bin/sh

#Get the current $DRGN price using the CoinMarketCap API
#Note: we'll pass in our CoinMarketCap API key as an argument ($1)
#Note: CoinMarketCap's internal ID for Dragonchain is 2243

DRGN_DATA=$(curl -s -H "X-CMC_PRO_API_KEY: $1" -H "Accept: application/json" -d "id=2243" -G https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest)

#Parse out the price with jq and echo a JSON object as the payload for the response from our smart contract
DRGN_PRICE_DATA=$(echo $DRGN_DATA | jq -c '.data."2243".quote.USD')
echo "{\"drgn_price_data_usd\":$DRGN_PRICE_DATA}"

And we can test it by running:

sh getprice.sh YOURAPIKEY

Which will give us a JSON object like the following as output (the actual output will be crammed into one line):

{
    "drgn_price_data_usd": {
        "price":0.0376209123787,
        "volume_24h":21822.8235368512,
        "percent_change_1h":-1.99972,
        "percent_change_24h":0.00147633,
        "percent_change_7d":-5.92949,
        "market_cap":8969650.91389967,
        "last_updated":"2019-10-10T18:04:06.000Z"
    }
}

Nifty. That’s all there is to our smart contract code.

MAJOR BIG NOTE: the way we’re passing our API key to the smart contract here is mega bad for what in hindsight are some fairly obvious reasons. Doing things this way will actually expose our API key in the blockchain data itself. Not good.

There’s a MUCH better way of doing this that the dev Adam shared with me using “secrets” that can be stored and encrypted outside of our chain itself which I’ll explore in a future post. For now, I’ll leave things like they are for demonstration purposes.

Preparing the Docker Image

Next up, we need to write our very simple Dockerfile to package this up into a sweet Docker container and image, then push it to Docker Hub.

Our Dockerfile will look like this:

FROM alpine:latest
RUN apk --no-cache add curl jq
COPY getprice.sh /getprice.sh

Very simple, but it has two major differences from the example in my post on Docker basics:

  1. We added a “RUN apk…” line that tells the OS to install the curl and jq commands (remember that alpine linux is super barebones out of the box)
  2. We DON’T have an “ENTRYPOINT” line to tell it to run our shell script. We’ll get to that in a second.

For now, let’s build our Docker image (replacing your username and repository name with whatever name you want (I use “shell-example” in the example below)):

docker build -t johnwantsmore/shell-example .

And then push it to Docker Hub with:

docker push johnwantsmore/shell-example

Note: if you didn’t create this repository on the Docker Hub website, the push command will create the repository as a PUBLIC repo.

You may want to go into the Settings tab on the Docker Hub website to change it to private if you don’t want the world to have access to your repo. You can also go to your Account Setings and make the default privacy setting “private” for new repos.

Finally, let’s test our image with the following command:

docker run johnwantsmore/shell-example sh /getprice.sh YOURAPIKEY

Assuming we get the expected output from our script, we’re ready to rock!

Creating a Smart Contract on our Dragonchain Business Node with DCTL

It’s time for the really fun stuff. Let’s create our smart contract using the Dragonchain command line tool (learn the setup and basics of DCTL here if you haven’t already).

We’ll need to have a few things ready to go before we run the command:

  1. A name for our smart contract (which will become the Transaction Type for later purposes)
  2. If our Docker Hub repo is private, we’ll need a base64 encoded username and password pair to pass in our dctl create command
  3. The cron schedule expression for how often we want to execute the contract

I’ll just keep things simple and use “shell_example” as my smart contract name.

To get my base64 encoded credentials for Docker Hub, I’ll run this on the command line and copy the output:

printf "myusername:mypassword" | base64

And to test my contract, I want to run it every minute on the minute, so my cron schedule expression will just be “* * * * *” (you can play with cron schedule expressions with this nifty little tool).

And now we’re ready. So to create the smart contract on my Dragonchain node, I’ll run:

dctl contract create shell_example \
johnwantsmore/shell-example:latest \
sh /getprice.sh MYCOINMARKETCAPAPIKEY \ 
-c '* * * * *' \
-r MYBASE64DOCKERHUBCREDENTIALS

There’s a good bit there, so let’s break it down.

  1. The first line sets up the create with “shell_example” as the contract name.
  2. The next line names my Docker Hub repository (with the latest tag) as the container to pull
  3. The next line is the command to run in our smart contract (it’s the same as the command we tested with and that we passed to “docker run” above)
  4. The next line with the -c flag sets up our cron schedule
  5. The last line with the -r flag passes our base64 encoded Docker Hub credentials

And if all went well, we should see something like the following as output:

{
  "status": 202,
  "response": {
    "dcrn": "SmartContract::L1::AtRest",
    "version": "1",
    "txn_type": "shell_example",
    "id": "ea702787-99b4-4681-94ec-db3522498c55",
    "status": {
      "state": "Pending",
      "msg": "Contract creating",
      "timestamp": "2019-10-10 19:34:04.800645"
    },
    "image": "johnwantsmore/shell-example:latest",
    "auth_key_id": null,
    "image_digest": null,
    "cmd": "sh",
    "args": [
      "/getprice.sh",
      "MYCOINMARKETCAPAPIKEY"
    ],
    "env": null,
    "existing_secrets": null,
    "cron": "* * * * *",
    "seconds": null,
    "execution_order": "parallel"
  },
  "ok": true
}

Boom! Then, to check the latest status on the contract (note that its current state is “pending” above), we can run the “contract get command” to check in using the id given in the response above:

dctl contract get ea702787-99b4-4681-94ec-db3522498c55

Next, I’ll hop over to the block explorer (see how to get it at the bottom of this post) and keep an eye out for our first transaction to come through…

And bingo:

Example Dragonchain Smart Contract Transaction

SWEET. We can now see that our smart contract is in fact running on the minute, every minute, and recording to the blockchain the current Dragonchain price information from CoinMarketCap.

Alternative Deployment Method: Using the Dragonchain Console Website

While the DCTL tool is by far my preferred method of managing everything to do with my node, you CAN use the Dragonchain console website to deploy your smart contracts in a more visual manner.

It looks like this:

Example of deploying a Dragonchain smart contract using the console website
Example of deploying a smart contract using the console website

Very straightforward. A few notes:

  1. Since I’m using a private Docker Hub repo, I’ll need to make sure to check the private box and enter that base64 encoded username:password combo in the “Private Disk Image Key” box
  2. Note how the command line is split into the command with two different arguments (and, again, note that it’s a bad idea to put API keys here, but I’m too lazy to change this whole tutorial)
  3. I’ve selected parallel execution order (which is also the default choice) since I’m not worried about two executions of this contract colliding with one another (mostly a concern if you’re maintaining some kind of state on the smart contract and you don’t want race conditions or competing data updates).
  4. That “Secrets” section would be a great place to put our API key instead, just note that accessing secrets from shell scripts is a little different than using the SDK (since we don’t have an SDK in a shell script). I’ll cover secrets in much more detail in a future post.

So now we’ve seen two ways to deploy our smart contracts so that they’ll execute on a cron schedule.

But do we REALLY need to pull it every minute? Probably not, so let’s update our smart contract to only execute every hour.

Updating a Smart Contract

The DCTL command to update our contracts is incredibly simple:

dctl contract update ea702787-99b4-4681-94ec-db3522498c55 -c '0 * * * *'

Then, after we give it a sec to do its thing, we can GET the smart contract again and we’ll see, at the very bottom, that the cron schedule was in fact updated:

"cron": "0 * * * *"

Freakin’ awesome. You can also use that command to update the image if your contract code changes, etc.

Wrapping Up

And that, folks, is all there is to deploying a simple shell script-based price oracle as a Dragonchain smart contract.

I don’t even want to know how much harder that would have been on Ethereum or some such. Dragonchain makes it easy. That’s what I need when I just want to get stuff working.

If you have any questions or problems, be sure to come join us in the Dragonchain developer’s Slack. There are a fair few of us running around that can help you out these days.

In the next post, we’ll look at deploying a simple nodejs smart contract that we’ll MANUALLY invoke by creating a transaction (rather than automatically via cron).

Until next time,
John

Leave a comment

Your email address will not be published. Required fields are marked *