Minting an NFT on an android app (Part 1)
Tags: android, web3, ipfs, blockchain
Published: 2022-12-02 13:36:37

## Introduction

Non-fungible means that something is unique and cannot be replaced by something else. Blockchains like Ethereum, polygon, and Iota act as a source of truth and help keep track of who's holding and trading NFTs

NFT's have multiple use cases, from Music, Fashion, Gaming, Luxury goods, Metaverse, Supply chain, and Ticket Sales. Nike recently partnered with Polygon to launch a web3 platform for NFTs in the form of digital shoes https://decrypt.co/114494/nike-swoosh-web3-platform-polygon-nfts

Most online blogs demonstrate how to mint an NFT using a web application. In this blog, we will be minting an NFT via an android app. The mobile app will be partly Java and, Kotlin. 

The full working code can be found [here](https://github.com/peterokwara/lemur-nft)

## Prerequisite

The requirements are:

* **Web3j**: Lightweight Java and Android library for integration with Ethereum clients. https://www.web3labs.com/web3j-sdk
* **Android Studio**: Official integrated development environment for Google's Android operating system. https://developer.android.com/studio 
* **Truffle**: Smart contract development tool. https://trufflesuite.com
* **Ngrok**: A reverse proxy service for fronting your web service. https://ngrok.com/

### 1. Create the smart contract

Create a new truffle project by running.

```console
truffle init
```
Install openzeppelin within the truffle project by running.

```console
npm install --save-dev @openzeppelin/contracts
```
Create a smart contract in the contract/ directory called [lemurNFT.sol](https://github.com/peterokwara/lemur-nft/blob/master/contract/contracts/lemurNFT.sol). 

```solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol";
import "openzeppelin-solidity/contracts/utils/Counters.sol";
import "openzeppelin-solidity/contracts/token/ERC721/extensions/RRERC721URIStorage.sol";

contract lemurNFT is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
 
    constructor() public ERC721("lemurNFT", "LMR") {}
 
    function mintNFT(address recipient, string memory tokenURI)
        public
        returns (uint256)
    {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);
 
        return newItemId;
    }
 
```
Within the smart contract:

* **_tokenIds.increment()**: Increase the counter by 1, thus enabling the code to generate a  unique Token ID.
* **uint256 newItemId = _tokenIds.current()** assigns the current count number to a new instance of __tokenIds_ named _newItemId_.
* **_safeMint(recipient, newItemId)** mints _newItemId_ and assigns it to recipient (an address).
* **_setTokenURI(newItemId, tokenURI)** sets _tokenURI_ of the NFTs metadata file, that we created earlier.

### 2. Set up the development environment

Configure the [_truffle-config.js_](https://github.com/peterokwara/lemur-nft/blob/7d660142b9a2815857a3a081724918825cf0f731/contract/truffle-config.js#L44) file to use the development environment by adding or commenting out the following section. Ensure WebSockets are enabled. This is because we will connect to our node via WebSockets.

```javascript
  development: {
      host: "127.0.0.1", // Localhost (default: none)
      port: 8545, // Standard Ethereum port (default: none)
      network_id: "*", // Any network (default: none)
      websocket: true, // Enable EventEmitter interface for web3 (default: false)
    }
```

### 3. Deploy the smart contract

To compile the smart contract, run

```console
truffle compile
```
The output should look something similar to
```console
Compiling your contracts...
===========================
> Compiling .\contracts\Migrations.sol
> Compiling .\contracts\lemurNFT.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\ERC721.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\IERC721.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\IERC721Receiver.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\extensions\ERC721URIStorage.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\extensions\IERC721Metadata.sol
> Compiling openzeppelin-solidity\contracts\utils\Address.sol
> Compiling openzeppelin-solidity\contracts\utils\Context.sol
> Compiling openzeppelin-solidity\contracts\utils\Counters.sol
> Compiling openzeppelin-solidity\contracts\utils\Strings.sol
> Compiling openzeppelin-solidity\contracts\utils\introspection\ERC165.sol
> Compiling openzeppelin-solidity\contracts\utils\introspection\IERC165.sol
> Compilation warnings encountered:

    Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.
  --> project:/contracts/lemurNFT.sol:12:5:
   |
12 |     constructor() public ERC721("lemurNFT", "LMR") {}
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


> Artifacts written to C:\Users\peter\Documents\GitHub\lemur-nft\contract\build\contracts
> Compiled successfully using:
   - solc: 0.8.13+commit.abaa5c0e.Emscripten.clang
```
Run a local node by running
```console
ganache
```
The output should look something similar to
```console
ganache v7.5.0 (@ganache/cli: 0.6.0, @ganache/core: 0.6.0)
Starting RPC server

Available Accounts
==================
(0) 0x42703e1F2CCB08583088D96d71d4549Be08b52a7 (1000 ETH)
(1) 0x859e7f120E751C505EB14C942A192eC3448a12a1 (1000 ETH)
(2) 0x2864E9c62dE39c44a97866D31546069fD86DF71E (1000 ETH)
(3) 0x656d2d16C6ad06677A9A87e105e786E9f0420d5c (1000 ETH)
(4) 0x70BDc8926bb41e3a778e3F812Fe18db181aa2602 (1000 ETH)
(5) 0x05A2e3a72a63D2e0A85c7259E3aD6A5b9773cb79 (1000 ETH)
(6) 0x135A6788B85973890d11AdED97a76BfD5acd9F37 (1000 ETH)
(7) 0x19fCd2F3F957b9761e52C5583A14CE2669bbF257 (1000 ETH)
(8) 0xaf47CA6B33E5b895E68D937fDb2CaEEB4e462845 (1000 ETH)
(9) 0x61cDEbBCc8579560A79aaA41936fc553E22615E2 (1000 ETH)

Private Keys
==================
(0) 0xbea5ebe59d051534239ec8e81018b9d2f8458eee5864584d9d520a0c4307de90
(1) 0x5c0fd2fc115d1ab0c8e0ab3f367ac68dcb6e8e9c037a7de791cfba3d50dff993
(2) 0x3d0f2dc97eb7203213c42363f2f2fad34aa4bd0d1029347e72dfad41f900bc2c
(3) 0xa697dc1f7e4d08e899bdbdef17b303048d425bb7bfa712653071f4270fef6a7b
(4) 0x9dd1cccefa2486212ac11cd451d36de3a956ae2be9d4740783b55f94053fa837
(5) 0x1044bba3f188578d08a5c36aae4d4f0bacbfaa60e31e2b96791e5c2b099278a6
(6) 0x02314898ed8983c1c3598e74757bb3e933d4506e4d238706938479f756da8b94
(7) 0x7a7988d32c9a7ab50a9d4d4a9bf5ddf05db49cf2a0f5e196b5fb076d183f4bec
(8) 0xaac1f5cfc3c8ad7c2ed6eb843e0412668e00b4f87e82341bd6090239acb561fe
(9) 0x5b072abfa5a17d7a88fe456867789d0ba97c625e1c969485921765301c916b90

HD Wallet
==================
Mnemonic:      truth manual elephant border predict castle payment suspect mimic insect wish acoustic
Base HD Path:  m/44'/60'/0'/0/{account_index}

Default Gas Price
==================
2000000000

BlockGas Limit
==================
30000000

Call Gas Limit
==================
50000000

Chain Id
==================
1337

RPC Listening on 127.0.0.1:8545
```
Make sure you keep the process running. Store the seed in a secure place, it will be needed later for the android app. Don�t use the private keys or seed anywhere else or you risk being hacked.

The mnemonic (_truth manual elephant border predict castle payment suspect mimic insect wish acoustic_) will be used when setting up the android configuration.

We will also use the first public address _0x42703e1F2CCB08583088D96d71d4549Be08b52a7_ as the wallet address.  

Run the development command to deploy the smart contract.
```console
truffle migrate --network development
```
If successful, it should show

```console
Compiling your contracts...
===========================
> Compiling .\contracts\Migrations.sol
> Compiling .\contracts\lemurNFT.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\ERC721.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\IERC721.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\IERC721Receiver.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\extensions\ERC721URIStorage.sol
> Compiling openzeppelin-solidity\contracts\token\ERC721\extensions\IERC721Metadata.sol
> Compiling openzeppelin-solidity\contracts\utils\Address.sol
> Compiling openzeppelin-solidity\contracts\utils\Context.sol
> Compiling openzeppelin-solidity\contracts\utils\Counters.sol
> Compiling openzeppelin-solidity\contracts\utils\Strings.sol
> Compiling openzeppelin-solidity\contracts\utils\introspection\ERC165.sol
> Compiling openzeppelin-solidity\contracts\utils\introspection\IERC165.sol
> Compilation warnings encountered:

    Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.
  --> project:/contracts/lemurNFT.sol:12:5:
   |
12 |     constructor() public ERC721("lemurNFT", "LMR") {}
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


> Artifacts written to C:\Users\peter\Documents\GitHub\lemur-nft\contract\build\contracts
> Compiled successfully using:
   - solc: 0.8.13+commit.abaa5c0e.Emscripten.clang


Starting migrations...
======================
> Network name:    'development'
> Network id:      1668618749782
> Block gas limit: 30000000 (0x1c9c380)


1_deploy_contract.js
====================

   Deploying 'lemurNFT'
   --------------------
   > transaction hash:    0xbe14c78e8b59c002e2537a3cbc9a4f616fbbddafd94f4722a59c719d42137860
   > Blocks: 0            Seconds: 0
   > contract address:    0x129Ff5b9D7C128527F3Be5ca5fb4F2E7A991482d
   > block number:        1
   > block timestamp:     1668618761
   > account:             0x7b2D85916C1fc56BBa4706DF0cE1D86532fB3839
   > balance:             999.99153599275
   > gas used:            2507854 (0x26444e)
   > gas price:           3.375 gwei
   > value sent:          0 ETH
   > total cost:          0.00846400725 ETH

   > Saving artifacts
   -------------------------------------
   > Total cost:       0.00846400725 ETH


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x7d4912cdabd080e6d49d0b244a88f86de933f61d491c76ef2847c133626e2d11
   > Blocks: 0            Seconds: 0
   > contract address:    0x12365fE3BA0F866A6E392BD3614B4322D73244C3
   > block number:        2
   > block timestamp:     1668618762
   > account:             0x7b2D85916C1fc56BBa4706DF0cE1D86532fB3839
   > balance:             999.990714509168638856
   > gas used:            250154 (0x3d12a)
   > gas price:           3.283911436 gwei
   > value sent:          0 ETH
   > total cost:          0.000821483581361144 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:     0.000821483581361144 ETH

Summary
=======
> Total deployments:   2
> Final cost:          0.009285490831361144 ETH
```

Ensure that you store the contract address (in this example _0x129Ff5b9D7C128527F3Be5ca5fb4F2E7A991482d_), located under _1_deploy_contracts_ since we will use this as the contract address when setting up the configurations for android.

### 4. Setting up a WebSocket

Before you perform this step, ensure that ganache is running. Once ganache is running, run the command:
```console
ngrok http 8545
```
Ngrok will act as a forward proxy. It will expose the local network 127.0.0.1:8085 and give you a web address you can use to connect your android app to. The response should look like this:
```console

ngrok

Add Okta or Azure to protect your ngrok dashboard with SSO: <https://ngrok.com/dashSSO>

Session Status:         online
Account:                P.Okwara (Plan: Free)
Version:                3.1.0
Region:                 United States (us)
Latency:                265ms
Web Interface:          http://127.0.0.1:4040
Forwarding:             <https://550b-105-163-1-231.ngrok.io> -> http://localhost:8545
Connections:            ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00

```
We will use the forwarding address _https://550b-105-163-1-231.ngrok.io_ as the websocket url. When adding to the config, we will change https: to wss so that the URL we will use in the end will look like this:

_wss://550b-105-163-1-231.ngrok.io_

## 5. Configure the metadata of your NFT using IPFS

Our lemurNFT smart contract function takes in a _tokenURI_ parameter that should resolve to a JSON document describing the NFTs metadata, which is really what brings the NFT to life, allowing it to have configurable properties, such as a name, description, image, and other attributes.

The Interplanetary File System (IPFS) is a decentralized protocol and peer-to-peer network for storing and sharing data in a distributed file system.

We will use [Pinata](https://pinata.cloud/), a convenient IPFS API and toolkit, to store our NFT asset and metadata and ensure that our NFT is truly decentralized. If you don't have a Pinata account, sign up for a free account [here](https://pinata.cloud/signup).

Once you've created an account:
* Navigate to the _Pinata Upload_ button on the top right
* Upload an image to pinata - this will be the image asset for your NFT. Feel free to name the asset whatever you wish
* After you upload, at the top of the page, there should be a green popup that allows you to view the hash of your upload ---> Copy that hashcode. You can view your upload at: https://gateway.pinata.cloud/ipfs/<hash-code>[https://gateway.pinata.cloud/ipfs/<](https://gateway.pinata.cloud/ipfs/QmarPqdEuzh5RsWpyH2hZ3qSXBCzC5RyK3ZHnFkAsk7u2f)

In your root directory, make a new file called nft-metadata.json and add the following json code:
[nft-metadata.json](https://github.com/peterokwara/lemur-nft/blob/master/contract/lemur-nft.json)

```console
{
    "attributes" : [ {
      "trait_type" : "Breed",
      "value" : "Maltipoo"
    }, {
      "trait_type" : "Eye color",
      "value" : "Mocha"
    } ],
    "description" : "The world's most adorable and sensitive pup.",
    "image" : "https://gateway.pinata.cloud/ipfs/QmWmvTJmJU3pozR9ZHFmQC2DNDwi2XJtf3QGyYiiagFSWb",
    "name" : "Ramses"
}
```

Feel free to change the data in the json. You can add or remove attributes. Most importantly, make sure the image field points to the location of your IPFS image otherwise, your NFT will not include a photo.

Once you're done editing the json file, save it and upload it to Pinata, following the same steps we did for uploading the image.

Remember the _metadata.json_ you uploaded to Pinata? Get the hashcode from Pinata. We will use this hash and the url for the next step.

{% embed https://dev.to/peterokwara/minting-an-nft-on-an-android-app-part-2-454o %}