Minting an NFT on an android app (Part 2)

peterokwara

Peter Okwara

Posted on December 2, 2022

Minting an NFT on an android app (Part 2)

6. Set up the config

Create a new blank android project. Within the home directory, create a file called apikey.properties. We will then add the values obtained from the previous steps.

WALLET_ADDRESS="0x42703e1F2CCB08583088D96d71d4549Be08b52a7" 
CONTRACT_ADDRESS="0x129Ff5b9D7C128527F3Be5ca5fb4F2E7A991482d" 
WEB_SOCKET_URL="[wss://550b-105-163-1-231.ngrok.io](https://550b-105-163-1-231.ngrok.io)"
SEED="truth manual elephant border predict castle payment suspect mimic insect wish acoustic"
TOKEN_URI=""
Enter fullscreen mode Exit fullscreen mode

For a recap:

  • We obtained the WALLET_ADDRESS by taking the first public key after running the ganache command.
  • We obtained the CONTRACT_ADDRESS after running truffle migrate --network development and taking the contract address from the 1_deploy_contract.js section.
  • We obtained the SEED after running the ganache command and obtaining the seed generated.

Within the build.gradle (module) under dependencies, add the _web3j _library.

// Add support for web3j
    implementation 'org.web3j:core:4.8.7-android' 
Enter fullscreen mode Exit fullscreen mode

Add the configurations within the defaultConfig section of the build.gradle file.

 // Load configuration from the apikey.properties file
 buildConfigField("String", "WALLET_ADDRESS", apikeyProperties['WALLET_ADDRESS'])
 buildConfigField("String", "CONTRACT_ADDRESS", apikeyProperties['CONTRACT_ADDRESS'])
 buildConfigField("String", "WEB_SOCKET_URL", apikeyProperties['WEB_SOCKET_URL'])
 buildConfigField("String", "SEED", apikeyProperties['SEED'])
 buildConfigField("String", "TOKEN_URI", apikeyProperties['TOKEN_URI'])
Enter fullscreen mode Exit fullscreen mode

Do not forget to add internet permissions within the AndroidManifest.xml file by adding the line below to the <application tag.

<uses-permission android:name="android.permission.INTERNET" />
Enter fullscreen mode Exit fullscreen mode

7. Develop the android wrapper

Using the _web3j _library, we can convert our solidity file into a java so that we can call the methods we have written in the solidity file.

web3j generate truffle --truffle-json .\build\contracts\lemurNFT.json -o ..\android\app\src\main\java\ -p com.example.lemur
Enter fullscreen mode Exit fullscreen mode

After running this command successfully, a new java file LemurNFT.java will be generated. Place the file close to the MainActivity.kt file.

8. Create the UI

The user interface is pretty simple, we have a button called MINT. When the button is clicked, it runs the mintNFT _function in the _MainActivity.kt file.

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="mintNft"
    android:text="@string/button_text"
    tools:layout_editor_absoluteX="161dp"
    tools:layout_editor_absoluteY="407dp" />
Enter fullscreen mode Exit fullscreen mode

The whole activity_main.xml file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LemurNFT"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mintNft"
        android:text="@string/button_text"
        tools:layout_editor_absoluteX="161dp"
        tools:layout_editor_absoluteY="407dp" />

</androidx.constraintlayout.widget.ConstraintLayout>
Enter fullscreen mode Exit fullscreen mode

The app should look like this:

Image description

9. Adding global variables

In your MainActivity.kt add the following variables as global.

// inside of any of your application's code
var walletAddress: String = BuildConfig.WALLET_ADDRESS;
var contractAddress: String = BuildConfig.CONTRACT_ADDRESS;
var webSocketUrl: String = BuildConfig.WEB_SOCKET_URL;
var seed: String = BuildConfig.SEED;
var tokenUri: String = BuildConfig.TOKEN_URI;
private lateinit var credentials: Credentials;
Enter fullscreen mode Exit fullscreen mode

This code loads all the configuration files from our _apikey.properties _file and the BuildConfig file.

We also have a credentials variable that will be initialized later on, and that is globally accessible by any function.

10. Load the credentials

Within the MainActivity.kt file add the following line of code.

/***
 * Create credentials from a seed.
 */
fun loadCredentials(){
    val masterKeypair = Bip32ECKeyPair.generateKeyPair(MnemonicUtils.generateSeed(seed, ""))
    val path = intArrayOf(44 or HARDENED_BIT, 60 or HARDENED_BIT, HARDENED_BIT, 0, 0)
    val x = Bip32ECKeyPair.deriveKeyPair(masterKeypair, path)
    this.credentials = Credentials.create(x)
}
Enter fullscreen mode Exit fullscreen mode

This generates a keypair (a public and private key) to sign our transactions.

11. Load the credentials

Within the MainActivity.kt file add the following line of code.

/***
 * Create credentials from a seed.
 */
fun loadCredentials(){
    val masterKeypair = Bip32ECKeyPair.generateKeyPair(MnemonicUtils.generateSeed(seed, ""))
    val path = intArrayOf(44 or HARDENED_BIT, 60 or HARDENED_BIT, HARDENED_BIT, 0, 0)
    val x = Bip32ECKeyPair.deriveKeyPair(masterKeypair, path)
    this.credentials = Credentials.create(x)
}
Enter fullscreen mode Exit fullscreen mode

This generates a keypair (a public and private key) to sign our transactions.

12. Show the response

We need to show the response after posting the transaction to the blockchain. With this, we will use a toast notification. Add the following code to the MainActivity.kt file.

/**
 * Display the response as a toast notification.
 */
private fun showResponse(response: String){

    val duration  = Toast.LENGTH_LONG;
    val toast = Toast.makeText(applicationContext, response, duration);
    toast.show();

}
Enter fullscreen mode Exit fullscreen mode

The function takes in a string and displays it in a toast notification.

13. Mint an NFT

Here the mintNft function is tied to a button in the main user interface. Once the button is clicked:

  • The credentials will be loaded from the loadCredentials() function.
  • A new instance of web3j instance will be created which will be used to connect to the node.
  • We then load the smart contract which is the LemurNFT contract and provide the contract address, the WebSocket connection, the credentials, and the gas for the transactions.
  • We then call the smart contract function to mint an NFT, providing the wallet address to mint an NFT to, and the tokenURI.
  • We then log out the response using Log.e *and *also display the response through a toast notification.
/**
 * Mint an nft when the mint button is clicked.
 */
fun mintNft(view: View) {
    try {
        loadCredentials();
        val web3j: Web3j = createWeb3j()

        val nft: LemurNFT = LemurNFT.load(contractAddress, web3j, credentials, DefaultGasProvider())
        val transactionReceipt: TransactionReceipt = nft.mintNFT(walletAddress, tokenUri).send()

        Log.e("Response", transactionReceipt.toString());
        showResponse(transactionReceipt.toString());

    } catch (e: Exception){
        e.printStackTrace();
    }
    return;
}
Enter fullscreen mode Exit fullscreen mode

The MainActivity.kt will then look like this:

package com.example.lemur

import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import org.web3j.crypto.Bip32ECKeyPair
import org.web3j.crypto.Bip32ECKeyPair.HARDENED_BIT
import org.web3j.crypto.Credentials
import org.web3j.crypto.MnemonicUtils
import org.web3j.protocol.Web3j
import org.web3j.protocol.core.DefaultBlockParameterName
import org.web3j.protocol.core.methods.response.EthGetBalance
import org.web3j.protocol.core.methods.response.TransactionReceipt
import org.web3j.protocol.websocket.WebSocketService
import org.web3j.tx.gas.DefaultGasProvider
import java.math.BigInteger
import java.net.ConnectException


class MainActivity : AppCompatActivity() {

    // inside of any of your application's code
    var walletAddress: String = BuildConfig.WALLET_ADDRESS;
    var contractAddress: String = BuildConfig.CONTRACT_ADDRESS;
    var webSocketUrl: String = BuildConfig.WEB_SOCKET_URL;
    var seed: String = BuildConfig.SEED;
    var tokenUri: String = BuildConfig.TOKEN_URI;
    private lateinit var credentials: Credentials;


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    /**
     * Mint an nft when the mint button is clicked.
     */
    fun mintNft(view: View) {
        try {
            loadCredentials();
            val web3j: Web3j = createWeb3j()

            val nft: LemurNFT = LemurNFT.load(contractAddress, web3j, credentials, DefaultGasProvider())
            val transactionReceipt: TransactionReceipt = nft.mintNFT(walletAddress, tokenUri).send()

            Log.e("Response", transactionReceipt.toString());
            showResponse(transactionReceipt.toString());

        } catch (e: Exception){
            e.printStackTrace();
        }
        return;
    }

    /**
     * Display the response as a toast notification.
     */
    private fun showResponse(response: String){

        val duration  = Toast.LENGTH_LONG;
        val toast = Toast.makeText(applicationContext, response, duration);
        toast.show();

    }

    /**
     * Create a web3j web socket instance from the web socket url.
     */
    private fun createWeb3j(): Web3j {
        val webSocketService = WebSocketService(webSocketUrl, true)
        try {
            webSocketService.connect()
        } catch (e: ConnectException) {
            e.printStackTrace()
        }
        return Web3j.build(webSocketService)
    }

    /***
     * Create credentials from a seed.
     */
    private fun loadCredentials(){
        val masterKeypair = Bip32ECKeyPair.generateKeyPair(MnemonicUtils.generateSeed(seed, ""))
        val path = intArrayOf(44 or HARDENED_BIT, 60 or HARDENED_BIT, HARDENED_BIT, 0, 0)
        val x = Bip32ECKeyPair.deriveKeyPair(masterKeypair, path)
        this.credentials = Credentials.create(x)
    }

}
Enter fullscreen mode Exit fullscreen mode

When you click on the mint button, once you compile and run the application, a toast notification should show up with a transaction receipt.

14. Conclusion

Congratulations, you have been able to create an NFT smart contract and interact with it from an android app.

💖 💪 🙅 🚩
peterokwara
Peter Okwara

Posted on December 2, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related