ICON DAPP From A-Z Part 1: Tools & Environment

Oct 23, 2018 · 16 min read
Image for post
Image for post
ICON Network Topology & Ecosystem

This tutorial assumes you have zero prior knowledge of blockchain development, may have taken a course or two with minimal understanding of programming concepts, are looking to build your first decentralized application or to learn the fundamentals of ICON smart contracts development. By the end of this tutorial you will become familiar with ICON’s dev environment, learn to write and deploy your SCORE smart contract to the ICON network. This is part 1 of an intensive and comprehensive training program that will empower you to build your first ICON DAPP from A-Z.

Level: Beginner


Programming Concepts ☆☆☆☆

Python ☆☆☆☆☆

ICON SDK ☆☆☆☆☆

T-Bears (Local Emulated ICON Node) ☆☆☆☆☆

SCORE (Smart Contract) ☆☆☆☆☆

How to Follow this Tutorial

This tutorial is designed for beginners, it should be followed from the top down in sequence. If you already have the knowledge of a certain section, feel free to skip and move onto the next section.

Let’s Get Started — Learn Python

Currently there are three SDKs (Python, Java, Javascript) available, this is the interface that allows you to call all the core ICON services. Since ICON’s entire code base is written in Python, this is probably the best language to start off as Python SDK will likely be most stable and up-to-date.

Install Python

First we need to install Python, follow this complete installation tutorial for your operating system, make sure to install the latest version or at least 3.6+ for everything to operate correctly.

Python 3 Installation & Setup Guide: https://realpython.com/installing-python/

Learn Python

I have reviewed several books, online courses and video tutorials, I find ‘Learn Python’ by far the easiest to pick up your Python basics. We don’t want to get into all the nitty-gritty details, and we want to spend as little time as possible, just enough so that we can read/write smart contract code and use the SDK without the language being a barrier. Over time you should be able to translate your logic into Python code through practices and actual building.

Learn Python: http://learnpython.org

The site also has interactive mode embedded where you can experiment with the code directly on the site. Make sure to do all the exercises at the end of each tutorial, think on it before revealing the solution, people often think they know when they don’t. Finish the entire tutorial from “Hello World” all the way to the end of “Advanced Tutorials”. Depending on your programming skill level, this should take somewhere from 1–2 hours to 2–3 days. Once you’re done with this, you should have enough Python skills to fulfill your SCORE development.

Virtualenv Python Environment

Image for post
Image for post

Before anything, let’s create a Python virtual environment. A Python virtualenv is an isolated working environment where you can specify which Python version to install and use without affecting the root installation. Associated Python packages are also installed to this environment, so to simply put, this is a virtual environment where you can specify which versions of the software package you want to work under without affecting rest of your projects. It is highly recommended that you always work under a virtualenv. Assuming you have python 3.x and pip 3.x installed from earlier instructions, to install virtualenv run

# Install virtualenv
$ sudo pip3 install virtualenv
# Make a folder to work under
$ mkdir icon-tutorial && cd icon-tutorial
# Make the virtual environment
$ virtualenv -p python3.6 venv
# Activate the environment
$ source venv/bin/activate # enter deactivate to exit

By now you should see ( venv ) prefixed to your working path. You can double check the environment’s Python version with

$ python -V

Now we have the basics down, let’s take a deep dive into ICON development environment. I have tried to build the environment under Windows, macOS and Linux, they should all work under different installation instructions. For the purpose of this tutorial, I’ll narrow down the scope to macOS, which is also the preferred dev environment by the ICON team.


ICON SDK for Python is a collection of libraries which allow you to interact with a local or remote Loopchain node, using an HTTP connection.

We will explore the SDK in depth shortly, first let’s get it installed.

$ pip3 install iconsdk

Making a Query

Now let’s make our first API call using ICON Python SDK

# Enter interactive mode
$ python
>>> from iconsdk.icon_service import IconService
>>> from iconsdk.providers.http_provider import HTTPProvider
>>> icon_service = IconService(HTTPProvider("https://bicon.net.solidwallet.io/api/v3"))
>>> block = icon_service.get_block("latest")
>>> print(block)
>>> exit()
  • The IconService class contains a set of API methods. It accepts a HTTPProvider which serves the purpose of connecting to HTTP and HTTPS based JSON-RPC servers.
  • A provider defines how the IconService connects to Loopchain.
  • The HTTPProvider takes the full URI where the server can be found. For local development, this would be something like http://localhost:9000. In this example we’re connecting to one of the many testnet API endpoints, for a list of mainnet and testnet URIs, as well as their service availability, visit: icon-status page.

We tried to grab the latest block from the testnet node, this should return a similar result to this

{'version': '0.1a', 'prev_block_hash': '47a450d4d7b44cba89fad97cf590d1aa375bf762788ddad7f29438c193758372', 'merkle_tree_root_hash': '2f018ea43471ca941977b70d27b7edd8c4d7a9a473e0ee0b7e1d3530412bff5b', 'time_stamp': 1539958749423136, 'confirmed_transaction_list': [{'from': 'hx2a445fe4e846403ff3698cece2f1e2a5a24fa7eb', 'to': 'hx56a9ccece79441b72aac162a8e95e0486fab94bf', 'value': 1022000000000000000, 'version': 3, 'nid': 3, 'stepLimit': 100000, 'timestamp': 1539958748264000, 'signature': 'nHXKv0/jBtOY/1a1fYHJujHL4TW2bdYMZWg795UjLTUxfuKq+ZZdQDNd03HsHcloy4NZ0xoRGQlAXlPFGFt2qwE=', 'txHash': '0x2f018ea43471ca941977b70d27b7edd8c4d7a9a473e0ee0b7e1d3530412bff5b'}], 'block_hash': '7b4771a571045e8f415f14f0607e3b5d2ffb0138107bb1f72dd9d67ef5e8ced1', 'height': 9697, 'peer_id': 'hxf64fc9c20c4a5b8c59e999405fbc941a96bc2c00', 'signature': '8NXFWdyHuFx5eD8eaJSOr9XPPQZ/236eFFUO0nFeKEkKnRNSWLLILHbVh1wxAimpmPuKZNK4AnUOsTtZXuTqgAE='}

Congratulations, you just performed an operation on an ICON node, some call themselves ‘blockchain expert’ on their CV at this point, but let’s get better than that.

Let’s explore some more!

The SDK provides a set of methods for querying information, a set of methods for KeyWallet operations and a set of methods for signing and sending transactions. These are commonly associated to smart contract queries and also a bit of a mind boggle for some developers where the application logic exist, we’ll get to that.

Creating a Wallet

We tried get_block which is part of the querying API methods, let’s play with KeyWallet now, which allows you to create an ICX wallet without using ICONex.

# Enter interactive mode
$ python
# Create a wallet
>>> from iconsdk.wallet.wallet import KeyWallet
>>> wallet = KeyWallet.create()
# Check the wallet address
>>> wallet.get_address()
# Let try getting the private key
>>> wallet.get_private_key()
# Now let's create a keystore
>>> wallet.store('./iconkeystore', '@icon111')
# Let's create another wallet to test transactions later
>>> wallet2 = KeyWallet.create()
>>> wallet2.store('./iconkeystore2', '@icon222')
>>> exit()

In the above example we created an ICX wallet, this is effectively the same as creating a wallet from the official ICONex wallet. We can test this by actually loading the wallets through ICONex either via keystore or private key.

Image for post
Image for post
Select Load Wallet
Image for post
Image for post
Either option is fine
Image for post
Image for post
Use the keystore ‘iconkeystore’ you generated as well as the password ‘@icon111’ when you saved the wallet

You should have successfully loaded the wallets you created, in ICONex.

Let’s deposit some test ICX, enter your wallet address starting with ‘hx’ to ICON Testnet ICX Faucet, you should receive 20 test ICX *shortly. Let’s use the SDK to verify,

*ICON currently generates one block every 2 seconds, so shortly is matter of seconds.

# Enter interactive mode
$ python
# Get balance through the querying method>>> from iconsdk.icon_service import IconService
>>> from iconsdk.providers.http_provider import HTTPProvider
>>> icon_service = IconService(HTTPProvider("https://bicon.net.solidwallet.io/api/v3"))>>> balance = icon_service.get_balance('hxb94e8c183b72255057d9d05a28532afa2787682c')
>>> print(balance)

You should now see a balance of 20000000000000000000 in your wallet. The token uses 18 decimal places, so your balance is 20.000000000000000000 ICX. If you want to verify the balance in ICONex, make sure to switch to the testnet ‘YEOUIDO’. By default ICONex points to the ICON mainnet, follow this instruction: How to change network in ICONex Chrome extension to enable testnet selections.

Sending a Transaction

The code is slightly longer on this one, so let’s work in a .py file instead of interactive mode. Create a new file called ‘send_transaction.py’ in our current directory.

The value is based on unit ‘loop’, 1000000000000000000 loops = 1 ICX. This is a lot of zeros to count, and not very legible. For this reason I’ve developed a simple tool to convert into different units, you can access it here: Unit Converter

# send_transaction.py# Load our existing wallet 1 from iconsdk.wallet.wallet import KeyWallet
wallet = KeyWallet.load("./iconkeystore", "@icon111")
# Build a transaction instance, hard-code it to send 1 ICX from wallet 1 to wallet 2from iconsdk.builder.transaction_builder import (
from iconsdk.signed_transaction import SignedTransaction
transaction = TransactionBuilder()\
from iconsdk.icon_service import IconService
from iconsdk.providers.http_provider import HTTPProvider
icon_service = IconService(HTTPProvider("https://bicon.net.solidwallet.io/api/v3"))
# Returns the signed transaction object having a signature
signed_transaction = SignedTransaction(transaction, wallet)
# Sends the transaction
tx_hash = icon_service.send_transaction(signed_transaction)

Save the file.

  • from_ : The wallet to initiate the transaction
  • to : The wallet to receive a transaction (this can be a SCORE, we’ll get to that)
  • value : The amount of ICX to be sent, remember it’s 18 decimals.
  • step_limit : The maximum step value for processing a transaction. Similar to gas limit of Ethereum.
  • nid : Network ID. Default is 1 that points to ICON mainnet, the node we’re testing in is network 3, https://bicon.net.solidwallet.io/
  • nonce : An arbitrary number used to prevent transaction hash collision. (optional)
  • build : Returns an ICX transaction object.

Now we can execute it by

$ python send_transaction.py

In this example we’ve generated a transaction instance through the SDK and executed it via IconService’s send_transaction method. You can check the balance of each wallet shortly via get_balance() method that we just practiced, alternatively you can view the updated balance on ICONex.

Image for post
Image for post

Since we’re making the transfers on the ICON testnet, you can also check the transaction details on the live tracker: https://bicon.tracker.solidwallet.io/address/hx4fbeb2879960248111e2006f12b8aba5173d1edd

Transaction Fees

Let’s spend a minute to quickly explain transaction fees,

‘Step’ is the base unit for calculating transaction costs in the ICON network, the conversion rate is 100,000,000 Step = 1 ICX.

Steps required for each transaction = max ( [ ( ∑ βiSi + C ), C ] ) where ∑ βiSi is a sum of various SCORE operations, the weights are defined in the Transaction Fee and SCORE Operation Policy yellow paper, and C is a constant of 100,000 steps (which is equivalent to 0.001 ICX)

Actual transaction fee is calculated with

Fee = usedStep * stepPrice where usedStep is what we just calculated above and stepPrice is the actual $ICX exchange rate.

At the time of this writing, we do not have any functional SCOREs running on the ICON network, so transaction fees are 0.001 ICX across the ICON tracker since they’re all ICX transfers without invoking any SCORE functions.

That’s it for ICON SDK! For a full list of ICON Python SDK API methods, visit: https://github.com/icon-project/icon-sdk-python/blob/master/README.md

ICON T-Bears

So far we have been using the SDK to perform basic operations directly on a testnet node using ICON Service. This isn’t ideal for obvious reasons, we should be testing locally under a test environment against our own SCORE and deploy when everything is ready. ICON has prepared a development suite called T-Bears with a set of CLI commands as well as a local emulated environment to a full node on the ICON network.

T-Bears Overview

T-Bears is a suite of development tools for SCORE. T-Bears provides a project template for SCORE to help you start right away. You can code and test your smart contract locally in an emulated environment, and when ready, deploy SCORE onto the ICON network from command-line interface.

Image for post
Image for post


A module that handles ICON JSON-RPC API request and sends response back to the client. The API specifications are defined: ICON JSON-RPC API v3

ICON Service

A module that manages the lifecycle of SCORE and its execution. SCORE’s state transition is stored in a database (LevelDB). ICON Service also manages the state of ICX.

T-Bears CLI

T-Bears Command Line Interface. Supports following functions :

  • Manage T-Bears service
  • Deploy SCORE
  • Send transaction
  • Send query request

For full information visit: Command-line Interfaces (CLIs)

T-Bears Block Manager

LoopChain emulator for T-Bears Service. It does not have full ‘consensus’ and ‘peer management’ functions. This module handles transaction and emulates block generation.

Message queue

Message queue is used for inter-component communication.

No worries if you don’t understand what each of the above component does, we’ll learn through exercise shortly. First let’s get T-Bears service environment installed.

Installation Option 1: Docker Container (not recommended for this tutorial)

The team has prepared a docker image with every component already built inside, you can obtain the image with this command.

$ docker run -it --name tbears-container -p 9000:9000 iconloop/tbears

Installation Option 2: Install each component one by one (recommended for this tutorial)

This tutorial uses option 2, to install each component one by one, so we won’t be working inside a docker container.

# Install levelDB
$ apt-get install libleveldb-dev
# Install libSecp256k
$ apt-get install libsecp256k1-dev

# install RabbitMQ and start service
$ apt-get install rabbitmq-server
# Install ICON Service
$ pip3 install iconservice
# Install ICON Commons (for T-Bears configurations and logs)
$ pip3 install iconcommons
# Install ICON RPC Server
$ pip3 install iconrpcserver
# Install T-Bears
$ pip3 install tbears

At this point we have a local emulated node ready to be spun up, we’ll need to start the message queue service first

$ sudo rabbitmq-server

Then we can start T-Bears

$ tbears start

Now let’s experiment T-Bears, remember our first query API to the ICON testnet? Let’s make the same API call, this time to our local emulated node,

# Enter interactive mode
$ python
>>> from iconsdk.icon_service import IconService
>>> from iconsdk.providers.http_provider import HTTPProvider
>>> icon_service = IconService(HTTPProvider("http://localhost:9000/api/v3"))
>>> block = icon_service.get_block("latest")
>>> print(block)
>>> exit()

Notice this time instead of connecting to the testnet node bicon.net.solidwallet.io, we’re connecting to localhost instead.

T-Bears has 19 commands, init, start, stop, deploy, clear, samples, genconf, transfer, txresult, balance, totalsupply, scoreapi, txbyhash, lastblock, blockbyheight, blockbyhash, keystore, sendtx , call

You can run

tbears -h

To list the available commands, for usage of a particular command, you can run

tbears command -h

Let’s try a few T-Bears commands, remember the send_transaction.py script we experimented earlier via the SDK? We can achieve the same results via T-Bears, first run the help command to see what arguments are needed,

$ tbears transfer -husage: tbears transfer [-h] [-f FROM] [-k KEYSTORE] [-n NID] [-u URI][-p PASSWORD] [-s STEPLIMIT] [-c CONFIG]to valueTransfer ICX coin.positional arguments:to                    Recipientvalue                 Amount of ICX coin in loop to transfer (1 icx = 1e18loop)optional arguments:-h, --help            show this help message and exit-f FROM, --from FROM  From address.-k KEYSTORE, --key-store KEYSTOREKeystore file path. Used to generate "from" addressand transaction signature-n NID, --nid NID     Network ID (default: 0x3)-u URI, --node-uri URIURI of node (default: PASSWORD, --password PASSWORDKeystore file's password-s STEPLIMIT, --step-limit STEPLIMITStep limit-c CONFIG, --config CONFIGConfiguration file path. This file defines the defaultvalues for the properties "keyStore", "uri", "from"and "stepLimit". (default: ./tbears_cli_config.json)

We want to send from wallet 1 so we’ll need to provide the keystore/password via -k keystore and -p password. Since our balances are stored in the testnet node, we’ll need to specify the node-uri to perform the operation under, via -u URI, if you don’t specify the correct node (localhost by default), you’d get out of balance exception since you don’t have any ICX in these wallets locally. The very last argument is the value to send in ‘loops’, which is the smallest unit in ICX, the conversion rate is 1e18 loops = 1 ICX. (1e18 = 1,000,000,000,000,000,000)

$ tbears transfer -k ./iconkeystore -p "@icon111" hx4fbeb2879960248111e2006f12b8aba5173d1edd -u https://bicon.net.solidwallet.io/api/v3 1e18

Check the balance shortly via the SDK or ICONex, 1 ICX should’ve been transferred from wallet 1 to wallet 2.

Let’s inspect the most important features of T-Bears — SCORE related development and execution. tbears init to initialize a new SCORE project and tbears samples to generate sample SCORE projects. tbears deploy, tbears sendtx and tbears call commands are used to deploy the SCORE, send a transaction, and call a function.

*We’re going to initialize a brand new SCORE project, you can run tbears samples which will generate two sample SCORE projects automatically.

# Create a new directory to work under
$ mkdir SCORE && cd SCORE
# Initialize a SCORE project
$ tbears init my_first_score FirstScore

This will initialize SCORE development environment and generate my_first_score.py and package.json in my_first_score directory. The name of the SCORE class is FirstScore. Default configuration files, “tbears_server_config.json” used when starting T-Bears and “tbears_cli_config.json” used when deploying SCORE, are also generated. A keystore_test1 is also created, this is the default wallet in all these configuration files, open the file and copy the wallet address

# Check how much ICX keystore_test1 has
$ tbears balance hxe7af5fcfd8dfc67530a01a0e403882687528dfcb

balance in decimal: 800460000000000000000000000

Great, they gave us the total supply, we have plenty of ICX to work with.

# Deploy the SCORE we just created
$ tbears deploy my_first_score/

You should see a similar message to this

If you want to check SCORE deployed successfully, execute txresult command
transaction hash: 0xb4563bc9994572ff4e9ca744628c388127b41f3072919a923a53f48f9f195085

Follow the instruction

$ tbears txresult 0xb4563bc9994572ff4e9ca744628c388127b41f3072919a923a53f48f9f195085

If you don’t see any error messages in the result JSON, the SCORE is successfully deployed to your localhost.

A convenient way to check which functions you can call on the SCORE is by querying its public methods through scoreapi,

$ tbears scoreapi cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b

You should see a ‘hello’ function

"type": "fallback",
"name": "fallback",
"inputs": []
"type": "function",
"name": "hello",
"inputs": [],
"outputs": [
"type": "str"
"readonly": "0x1"

This is a full list of external APIs in your SCORE, you can verify this by opening the my_first_score/my_first_score.py file, functions with @external decorator are exposed for public use.

Next we’ll make a call to this function through ICON JSON-RPC V3. First make a JSON file “call.json”, we’re going to call the hello function,

# call.json{
"jsonrpc": "2.0",
"method": "icx_call",
"params": {
"from": "hxe7af5fcfd8dfc67530a01a0e403882687528dfcb",
"to": "cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b",
"dataType": "call",
"data": {
"method": "hello"
"id": 1

Execute it with

$ tbears call call.json

The ‘hello’ function is executed and returns a string “Hello”.

response : {
"jsonrpc": "2.0",
"result": "Hello",
"id": 1

Next we’re going to try the tbears sendtx method, which invokes icx_sendTransaction ICON JSON-RPC method.

# Check SCORE balance first
$ tbears balance cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b

balance in decimal: 0

As expected, the contract has 0 balance. In the next exercise, we will purposely create some errors and fix them, you will learn a few more things that will be helpful when you debug your SCORE in the future. First create a new file send.json

Notice the value here takes hex format, you can use this tool to make the conversion from decimals. Unit Converter

# send.json{
"jsonrpc": "2.0",
"method": "icx_sendTransaction",
"params": {
"version": "0x3",
"from": "hxe7af5fcfd8dfc67530a01a0e403882687528dfcb",
"value": "0xde0b6b3a7640000",
"stepLimit": "0x200000",
"nid": "0x3",
"nonce": "0x1",
"to": "cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b",
"dataType": "call"
"id": 1

Now send this query through T-Bears CLI

$ tbears sendtx -k keystore_test1 send.json# default password is test1_Account

You should be prompted to enter your password for the keystore, the default password is: test1_Account

Now check the SCORE balance shortly, you probably expected to see 1 ICX

$ tbears balance cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b

balance in decimal: 0

Wait, something is wrong here. First thing to do in situation like this is to always check the transaction result first

$ tbears txresult 0x0c65bdb4de42cd773e22d07c92d5d80cf4bdaf2b6758535fe299fb40f5381914

which outputs

Transaction result: {..."failure": {
"code": "0x7d64",
"message": "This is not payable"

the SCORE template by default has no payable methods to receive funds, let’s create a function in my_first_score/my_first_score.py

# my_first_score.py...@external
def receive_funds(self) -> None:
Logger.debug(f'{self.msg.value} received from {self.msg.sender}', TAG)

Your file should look something like this

Image for post
Image for post

Then we’ll have to modify the method to call in the send.json file,

# send.json{
"jsonrpc": "2.0",
"method": "icx_sendTransaction",
"params": {
"version": "0x3",
"from": "hxe7af5fcfd8dfc67530a01a0e403882687528dfcb",
"value": "0xde0b6b3a7640000",
"stepLimit": "0x200000",
"nid": "0x3",
"nonce": "0x1",
"to": "cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b",
"dataType": "call",
"data": {
"method": "receive_funds"
"id": 1

Let’s try to send this request again,

$ tbears sendtx -k keystore_test1 send.json

Check the transaction result,

$tbears txresult 0xcbba1562a09adc6d8933812edd3d337ad8130d8cad89b154378a2ef2c1968174

Did it work?

"failure": {
"code": "0x7d00",
"message": "'FirstScore' object has no attribute 'receive_funds'"

Nope, the newly added function ‘receive_funds’ is not found. The reason is because we have already deployed the SCORE to our localhost node, making changes to SCORE definition will require an update to the smart contract (an unique feature in ICON, that is updatable smart contracts on the blockchain). We can do that by the ‘deploy’ command with specified deploy mode

$ tbears deploy my_first_score -m update -o cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b -k keystore_test1

Now the SCORE should be updated with our new receive_funds method, send the request again

$ tbears sendtx -k keystore_test1 send.json

now check the balance again,

$ tbears balance cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b

balance in decimal: 1000000000000000000

In this exercise we decided to implement a payable method to directly receive the funds, and making the JSON RPC API query with the ‘receive_funds’ method. Alternatively, if we do not specify

# send.json"data": {
"method": "receive_funds"

Without providing method to this query means no method is called, in this case, afallback method will be called. This is designed to accept ICX transfers and other purposes (fallback cannot be decorated with @external so it cannot be queried from the outside). We could’ve implemented ‘fallback’ instead like so

# my_first_score.py@payable
def fallback(self):

Now if we make the JSON request without providing the method, this will be called and work similarly to ‘receive_funds’.

That’s it for T-Bears! We’ve learned how to use T-Bears CLI to create and deploy a SCORE, and perform various operations on it. We also learned how to make an ICON JSON-RPC API request to the RPC Server by invoking ICON Service methods. Message queue and block manager perform their operations behind the scenes so we won’t dive into those details in this tutorial. At this point we have all the necessarily tools to start building smart contracts on the ICON network. In the next tutorial, we’ll look into how to write a SCORE, how to deploy it to a remote testnet node(eventually mainnet), syntax, best practices and understand its limitations.

Level Up!

Programming Concepts ★★☆☆☆

Python ★★★☆☆

ICON SDK ★★★★★

T-Bears (Local Emulated ICON Node) ★★★★★

SCORE (Smart Contract) ☆☆☆☆

Follow me on Twitter for most up-to-date ICON related content: https://www.twitter.com/2infiniti

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store