Communicating with an Ethereum Smart Contract via Android

— UPDATED OCTOBER 18th, 2022

This article aims to show you how to:

  1. Generate a contracts ABI and binary files from a solidity file.
  2. Use web3j libraries to generate java wrapper classes from a smart contract’s methods.
  3. Use Infura to host an Ethereum node remotely that allows us to complete transactions and interact with the blockchain.
  4. Verify a transaction was made via the Etherscan blockchain explorer.

Necessary Tools

  1. Web3j
    Web3j is a lightweight, highly modular, reactive, type safe Java and Android library for working with Smart Contracts and integrating with clients (nodes) on the Ethereum network. This allows you to work with the Ethereum blockchain, without the additional overhead of having to write your own integration code for the platform. Here is a 30minute video from the creator of the library explaing it’s usecase.
  2. Infura
    Infura allows us to host our very own node on the blockchain remotely. Infura provides the infrastructure so that we can send transactions and communicate with other nodes as if we have the blockchain downloaded on our local machine.
    Note: Infura is not necessary if you are running your own node and not interacting with a smart contract that’s already deployed.
  3. Solidity Command Line Interface (solc)
    Solidity is an object-oriented programming language for writing smart contracts. Solc is the command line compiler we will use to generate our contract binary and ABI.
  4. Android Studio IDE
    Android Studio is the official Integrated Development Environment (IDE) for Android app development, based on IntelliJ IDEA . On top of IntelliJ’s powerful code editor and developer tools, Android Studio offers even more features that enhance your productivity when building Android apps. Android Studio is the recommended way to start developing android apps (as of Feb 2019).

Note: This article does not explain how to set up the Android Development Environment, so make sure you can at least run a basic hello world.

Note: I recommend using terminal when following along, window users are encouraged to use Powershell or something similar.

Lets Start

  1. Check if solidity cli is installed:

solcjs --version

If it’s not installed, install it globally.

npm install -g solc

Then check again.

2. Check if web3j is installed:

web3j -v

If it’s not installed,

Mac:

Install it using this command

curl -L get.web3j.io | sh && source ~/.web3j/source.sh

Note: If any issues occur in regards to openjdk, please download the latest jdk from https://www.oracle.com/java/technologies/javase-downloads.html

Windows:
Download the zip.

https://github.com/web3j/web3j/releases/tag/v4.1.1

3. Find a contract to communicate with, the contract i’m using is my very own Greeter contract which I verified and deployed last article. The public address of my contract is 0x8394cdf176a4a52da5889f7a99c4f7ad2bf59088.
Contract on Etherscan

Contract code:

pragma solidity ^0.4.23;



contract Mortal {
/* Define variable owner of the type address */
address owner;

/* This function is executed at initialization and sets the owner of the contract */
constructor() public { owner = msg.sender; }

/* Function to recover the funds on the contract */
function kill() public {
if (msg.sender == owner)
selfdestruct(owner);
}
}

contract Greeter is Mortal {
/* Define variable greeting of the type string */
string greeting;

/* This runs when the contract is executed */
constructor(string _greeting) public {
greeting = _greeting;
}

/* change greeting */
function changeGreeting(string _greeting) public {
greeting = _greeting;
}

/* Main function */
function greet() public view returns (string) {
return greeting;
}
}

4. Create an account with Infura, in your dashboard:
a) Whitelist your contract address.
b) Specify the correct network (Rinkeby, mainnet, etc.).
c) Copy and save your endpoint address.

Infura Dashboard

Now For the Android Part:

  1. Create a new project and add web3j’s core and infura libraries to your app module build.gradle.
// dependencies for contract interaction
implementation 'org.web3j:infura:3.3.1-android'
implementation 'org.web3j:core:3.1.1-android'

2. Generate your bin and ABI by first copying the solidity file somewhere into your project directory. Type in the following command into your command line.

solc <contract>.sol — bin — abi — optimize -o <output-dir>/

My line of code looked like:

solc Greeter.sol — bin — abi — optimize -o /Users/e/Documents/programming/android/projects/GreeterApp/app/src/main/java/iwd/props/com/greeterapp
IDE after typing commands

The Contract ABI and binary should be generated in the output directory. I have an ABI and Binary for Greeter and Mortal generated because my Greeter.sol code extends Mortal.sol.

Note: If you already have the contract’s binary and ABI, then you don’t need to use web3j to generate it. Just get those values and create the classes, then use them on the next step.

3. Now it’s time to generate your Java code using

web3j solidity generate -b /path/to/<smart-contract>.bin -a /path/to/<smart-contract>.abi -o /path/to/src/main/java -p com.your.organisation.name

My command looks like:

web3j solidity generate -b iwd/props/com/greeterapp/Greeter.bin -a iwd/props/com/greeterapp/Greeter.abi -p iwd.props.com.greeterapp -o /Users/e/Documents/programming/android/projects/GreeterApp/app/src/main/java/iwd/props/com/greeterapp

After running this command, a Greeter.java file is generated that extends Contract.

My full generated class is here.

Note: the following code is written in Kotlin

4. In your kotlin class, define some important variables. You can do this in your MainActivity.

// contract address
val contractAddress = "0x8394cDf176A4A52DA5889f7a99c4f7AD2BF59088"
// endpoint url provided by infura
val url = "https://rinkeby.infura.io/v3/01eb8f7b5e514832af8e827c23784d23"
// web3j infura instance
val web3j = Web3jFactory.build(InfuraHttpService(url))
// gas limit
val gasLimit: BigInteger = BigInteger.valueOf(20_000_000_000L)
// gas price
val gasPrice: BigInteger = BigInteger.valueOf(4300000)
// create credentials w/ your private key
val credentials = Credentials.create("f9319fe162c31947c0ca8fd649a536b7ca311b5f210afdc48b62fd7d18ce53e4")

After these variables are set, you will be able to load your contract with:

val greeter = Greeter.load(contractAddress, web3j, credentials, gasLimit, gasPrice)

5. We are now able to interact with the classes that my Greeter.java class provides (greet(), changeGreeting()). We can could also interact with some default web3j functions like isValid(). This allows us to do a quick sanity check and make sure that our contract was generated correctly.

Check Contract Validity

// check contract validity
Log.d(TAG, " ${greeter.isValid}")

Read from Contract

// read from contract
val greeting: Future<String>? = greeter.greet().sendAsync()
val convertToString: String? = greeting?.get()
Log.d(TAG, "greeting value returned: $convertToString")

Write to Contract

// write to contract
val transactionReceipt: Future<TransactionReceipt>? = greeter.changeGreeting("Greeting changed from an Android App (ಠ_ಠ) ").sendAsync()
val result = "Successful transaction. Gas used: ${transactionReceipt?.get()?.blockNumber} ${transactionReceipt?.get()?.gasUsed}"
Log.d(TAG, result)

Transaction results on etherscan:

https://rinkeby.etherscan.io/tx/0x9e6add7138f7d0b0488be727010b783dd227f8498d9e00fc139e19e89c9f978d#decodetab

--

--