Calling blockchain smart contracts from Android

Learn how easy it can be to interact with a blockchain using a library

Luiz Milfont
Jan 21 · 10 min read

Introduction

In my last article, I wrote about calling a blockchain deployed smart contract from Java using Eclipse. Now it’s time to go mobile! This tutorial is going to show how to call smart contracts directly from Android development environment — Android Studio.

Requirements

We are going to use the Tezos blockchain. And, to make our Android Java code interact with the existing, deployed smart contract, we will need the TezosJ library from TezosRio. There are two versions of this library, one written specifically for generic Java applications made with Eclipse IDE (TezosJ_plainJava), and the other one intended to Android apps development (TezosJ_SDK), which we are going to use here. There is no need to download the whole library source code. We just have to insert a single line in the dependencies section of our build.gradle file and voilà, we’re ready to go. As our development IDE, Android Studio will be the choice.

The target

The contract we are going to call is a simple customers base register that can be found deployed on Tezos testnet under the following address:

KT18pK2MGrnTZqyTafUe1sWp2ubJ75eYT86t

You may easily check the contract’s details through one of Tezos block explorers, like Tezblock. Or by using the better-call.dev tool. Still, another option is to check the contract with the SmartPy explorer. Remember, thought, to specify a testnet server, like https://tezos-dev.cryptonomic-infra.tech.

Our smart contract is called “Customers” and has four entrypoints (or methods): addCustomer, removeCustomer, transfer and updateBalance. Basically, insert, delete, transfer funds and update. Nothing much fancy. This was created for educational purposes only. What it does is to maintain a list of “customers” in Tezos blockchain’s storage.

The call

TezosJ is probably one of the easiest ways available nowadays to call a Tezos smart contract. Basically our call can be done in just one command:

JSONObject jsonObject = wallet.callContractEntryPoint(wallet.getPublicKeyHash(), "KT18pK2MGrnTZqyTafUe1sWp2ubJ75eYT86t", amount, fee, "", "", "addCustomer", new String[]{"1000000", "123456789","Bob","98769985"});

The code above adds a new customer to the base, Bob, with a 10ꜩ balance, 123456789 as his ID and 98769985 as his phone number. This is done by calling the callContractEntryPoint method that basically requires the same parameters then a common Tezos .send() method, but has some extras: contract entrypoint name, and a String Array containing the expected entrypoint’s parameters (that should be known before making the call).

Step-by-step, please!

If you haven’t done it yet, download and install Android Studio. Open it. First step is to create a new Android Studio project, like this:

Choose Phone and Tablet as your project type, then click on Empty Activity:

Name your project smartContractCall and change the Language to Java. Then, click finish:

First thing we’ve got to do is to delete the android:allowBackup=”true” from the application tag in AndroidManifest.xml file. This is crucial and if we skip this step, TezosJ_SDK won’t work:

Now it’s time to add TezosJ_SDK to our project. Open your build.gradle (Module: app) file. Go to the dependencies section and add: implementation ‘com.milfont.tezos:tezosj_android:0.9.998’ . Then, click Sync Now (in the upper right corner of the window):

Regarding configuration, we’re almost done. The last step is to change the minSdkVersion to 26, in this same file. Then, Sync again:

All done! And now that we’ve got everything prepared, lets first build a Tezos wallet. On MainActivity file, onCreate method, put a declaration, like below (don’t forget the try-catch block):

Now add some Logcat output to check if our wallet is being created successfully. We are going to print some information about it: the wallet’s public key hash (Tezos address), its mnemonic words and current balance:

After adding the Log.i() commands, run the project and you will get something like this (check the Logcat):

We were able to generate a new wallet and get its mnemonic words. But we’ve got an error while trying to get the wallet balance from blockchain. That’s because we tried to access the Internet directly in MainActivity class, which is prohibited in Android environment. For this tutorial, lets just use a small workaround. Put the method below inside your MainActivity class:

public void enableStrictMode()
{
StrictMode.ThreadPolicy policy = new
StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}

And call it from onCreate method:

enableStrictMode();

The use of strict mode here is for ilustrative purposes only. It’s not advised to access Internet through the MainActivity class directly (do it inside a new thread):

Run the project again and now it will all work properly and you will be able to see correctly the wallet balance on Logcat:

Ok! We now have a functional Tezos wallet and we are able to do a call to the Customers Tezos smart contract.

A little bit about the contract

The Customers smart contract we are going to use was created and deployed on Tezos blockchain testnet using SmartPy.io online IDE. Check the contract source code below:

# Imports the SmartPy library.
import smartpy as sp# Defines the Customer class and its constructor.
class Customer(sp.Contract):
def __init__(self):
self.init(customers = sp.map())

# Defines the addCustomer entry point.
@sp.entry_point
def addCustomer(self, params):
# Verifies if mandatory fields have values.
sp.verify(params.id != "")
sp.verify(params.name != "")
sp.verify(params.phoneNumber > 0)
# Declare the parameter types.
sp.set_type(params.id, sp.TString)
sp.set_type(params.name, sp.TString)
sp.set_type(params.phoneNumber, sp.TNat)
sp.set_type(params.balance, sp.TMutez)

# Defines a customer record, so we can add to a Map.
customer = sp.record(name=params.name, phoneNumber=params.phoneNumber, balance=params.balance)

# Adds the new customer record to a Map (that will reside in the contract's storage).
self.data.customers[params.id] = customer
# Defines the removeCustomer entry point.
@sp.entry_point
def removeCustomer(self, params):
# Verifies if mandatory fields have values.
sp.verify(params.id != "")
# Declare the parameter types.
sp.set_type(params.id, sp.TString)
# Remove the customer from the Map.
del self.data.customers[params.id]
# Defines the updateBalance entry point.
@sp.entry_point
def updateBalance(self, params):
# Verifies if mandatory fields have values.
sp.verify(params.id != "")
# Declare the parameter types.
sp.set_type(params.id, sp.TString)
sp.set_type(params.amount, sp.TMutez)
# Updates the balance.
self.data.customers[params.id].balance = params.amount

# Defines the transfer entry point.
@sp.entry_point
def transfer(self, params):
# Verifies if mandatory fields have values.
sp.verify(params.idFrom != "")
sp.verify(params.idTo != "")
sp.verify(params.amount > sp.mutez(0) )
# Verifies if customer has enough funds to be transfered.
sp.verify(params.amount <= self.data.customers[params.idFrom].balance )
# Declare the parameter types.
sp.set_type(params.idFrom, sp.TString)
sp.set_type(params.idTo, sp.TString)
sp.set_type(params.amount, sp.TMutez)
# Updates the balance.
self.data.customers[params.idFrom].balance = self.data.customers[params.idFrom].balance - params.amount
self.data.customers[params.idTo].balance = self.data.customers[params.idTo].balance + params.amount
# Creates the test scenario, to simulate a contract call in SmartPy.io IDE.
@sp.add_test(name = "Customers")
def test():
# Instantiate a contract inherited from the Customer Class.
myCustomersContract = Customer()

# Defines a test scenario.
scenario = sp.test_scenario()

# Adds the contract to the test scenario.
scenario += myCustomersContract

# Inserts the customers, calling the contract's addCustomer entry point.
# This customers will reside in the contract's storage.
scenario += myCustomersContract.addCustomer(id="123456",name="Daniel",phoneNumber=99984537,balance=sp.mutez(0))
scenario += myCustomersContract.addCustomer(id="345678",name="Eleonor",phoneNumber=85375677,balance=sp.mutez(0))
scenario += myCustomersContract.addCustomer(id="678905",name="Fabian",phoneNumber=78655567,balance=sp.mutez(0))
# Removes a customer through its id number.
scenario += myCustomersContract.removeCustomer(id="678905")

# Updates a customer's balance.
scenario += myCustomersContract.updateBalance(id="123456",amount=sp.mutez(10000000))
# Transfers funds from a customer to another.
scenario += myCustomersContract.transfer(idFrom="123456", idTo="345678", amount=sp.mutez(5000000))

The SmartPy contract’s source code was shown here for illustrative purposes only and we do not need to compile and deploy it again. It already resides on the Tezos blockchain testnet, under the address:

KT18pK2MGrnTZqyTafUe1sWp2ubJ75eYT86t

And… Action!

Now we have all that is needed to call the contract from Android. First, we are going to add a new customer using the contract’s “addCustomer” entrypoint. Lets consider the name of the customer “Bob”and his id will be “98765” and phone number “99876787”. Bob will have a balance of 10ꜩ (10.000.000 mutez). So our contract call command will be like this:

JSONObject jsonObject = wallet.callContractEntryPoint(wallet.getPublicKeyHash(), "KT18pK2MGrnTZqyTafUe1sWp2ubJ75eYT86t", amount, fee, "", "", "addCustomer", new String[]{"1000000", "98765","Bob","99876787"});

First of all, we need to inform TezosJ that we will be using the Tezos testnet (where the contract is actually published). We also need to set an amount for the transaction and a fee. This is done with the Java code below:

// Change wallet provider to use testnet.
wallet.setProvider(“https://tezos-dev.cryptonomic-infra.tech");

// Sets amount and fee for the transaction.
BigDecimal amount = new BigDecimal(“0”);
BigDecimal fee = new BigDecimal(“0.1”);

// Calls the contract entry point.
JSONObject jsonObject = wallet.callContractEntryPoint(wallet.getPublicKeyHash(), “KT18pK2MGrnTZqyTafUe1sWp2ubJ75eYT86t”, amount, fee, “”, “”, “addCustomer”, new String[]{“1000000”, “98765”,”Bob”,”99876787"});
// Prints the operation hash in the console.
String opHash = (String) jsonObject.get("result");
Log.i("output", "OpHash : " + opHash);

Copy the code and paste into our MainActivity class, so it stays like this:

Note that for this call to work, you first need to fund and reveal your Tezos account. Or else you may get an error like this:

There were errors: kind ‘branch’ id ‘proto.005-PsBabyM1.implicit.empty_implicit_contract’

Run the project! If everything was set ok, you will get as result a transaction hash (here onuvzSRu9GiUBtPxEVf958jPUkG8NTxo7UHCYvFXKB8Chtci3Jm) shown in the LogCat:

This indicates that we have successfully sent a callContract transaction to the Tezos testnet blockchain (as we’ve got an operation hash). Now, lets check if the transaction was accepted by the Tezos blockchain. We will use the TezBlock explorer to do it. Open https://tezblock.io in your prefered browser and, first of all, in the upper right corner of the screen, change from “mainnet”to “balylonnet” (that is the same of testnet). Then, paste the operation hash that we’ve got from our Java execution on the search box and hit “enter” key:

This will show the results of our contract call, with the details of the operation. By clicking in “show”, you will be able to check the parameters that were passed from our Java code to the Tezos blockchain:

Parameters:

Left (Left (Left (Pair (Pair (Pair 1000000 "98765") "Bob") 99876787)))

This is the Micheline formatted parameters that our call from the Android app generated. Tezos smart contracts when deployed are written in Michelson language and they expect entry parameters sent in this format. One of the benefits of using TezosJ library is that it generates Micheline formatted parameters on-the-fly and you don’t have to worry about how it is done.

Le grand finale

By now we can assure that the contract was correctly called, the parameters were passed and the operation was accepted by the Tezos blockchain. Now, the last thing we’ve got to do is check wether the new customer, Bob, was inserted in the contract’s storage. For this, we will use the better-call.dev tool. Just open your browser and point it to https://better-call.dev. Enter our customer contract’s address (KT18pK2MGrnTZqyTafUe1sWp2ubJ75eYT86t) int the search field, change the network to babylonnet and press enter:

The result will be a list of all operations ever sent to the contract’s address as well as the current state of the contract’s storage. Here you will be to check the parameters you’ve passed from Java and if they are correctly saved in the storage:

Going further

You may try the contract’s other entrypoints, like removeCustomer, transfer and updateBalance. Remember to always check the number of parameters that each entrypoint requires.

When running the code, if you ever get a Java error, it means the transaction was not sent. In this case you have to re-check your parameters, its order, your account balance, fee and which network (mainnet or testnet) you are working in. Also, check your gasLimit and storageLimit parameters.

On the other hand, if you ever get an operation hash, it means your Android app has successfully sent the transaction to the blockchain. But, yet, you have to check for possible blockchain related errors. In which case, when you check with TezBlock explorer, it will show errors with a salmon colored background, meaning something wrong happened in the blockchain context (like wrong number of parameters passed, wrong parameter Micheline formatting, or even contract unaccepted conditions — for example: you called the transfer entrypoint but the “from” customer doesn’t have enough funds to send).

Conclusion

It has never been so straightforward for developers to work with blockchain smart contracts. Tezos, in particular, offers a lot of great tools and a huge myriad of computer language libraries that facilitates integration with most systems. TezosJ library innovates by offering an easy-to-use callContractEntryPoint method that creates Micheline formatted parameters on-the-fly. It’s got all the tools Android developers need to thrive in app development that interacts seamlessly with the blockchain.

Download the complete code

You may download here the complete Android Studio project shown in this article.

The Startup

Medium's largest active publication, followed by +588K people. Follow to join our community.

Luiz Milfont

Written by

IT Developer

The Startup

Medium's largest active publication, followed by +588K people. Follow to join our community.

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