DEV Community

Cover image for Minting an NFT on an android app (Part 1)
Peter Okwara
Peter Okwara

Posted on

Minting an NFT on an android app (Part 1)

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

Prerequisite

The requirements are:

1. Create the smart contract

Create a new truffle project by running.

truffle init
Enter fullscreen mode Exit fullscreen mode

Install openzeppelin within the truffle project by running.

npm install --save-dev @openzeppelin/contracts
Enter fullscreen mode Exit fullscreen mode

Create a smart contract in the contract/ directory called lemurNFT.sol.

// 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;
    }

Enter fullscreen mode Exit fullscreen mode

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 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.

  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)
    }
Enter fullscreen mode Exit fullscreen mode

3. Deploy the smart contract

To compile the smart contract, run

truffle compile
Enter fullscreen mode Exit fullscreen mode

The output should look something similar to

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
Enter fullscreen mode Exit fullscreen mode

Run a local node by running

ganache
Enter fullscreen mode Exit fullscreen mode

The output should look something similar to

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
Enter fullscreen mode Exit fullscreen mode

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.

truffle migrate --network development
Enter fullscreen mode Exit fullscreen mode

If successful, it should show

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
Enter fullscreen mode Exit fullscreen mode

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:

ngrok http 8545
Enter fullscreen mode Exit fullscreen mode

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:


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

Enter fullscreen mode Exit fullscreen mode

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, 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.

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/https://gateway.pinata.cloud/ipfs/<

In your root directory, make a new file called nft-metadata.json and add the following json code:
nft-metadata.json

{
    "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"
}
Enter fullscreen mode Exit fullscreen mode

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.

Latest comments (1)

Collapse
 
juyuancai profile image
juyuancai

Hi, when you use IPFS to publish file, You should purchase a VPS to run your IPFS node and also with pin service like pinata to pin your file. Now we have a better solution called Foggie. With Foggie , you can get a 2C4G VPS with pin service together with only the same price as pinata. Even you only use pinata, Foggie provides larger space and larger bandwith to 4T. Also , when you use it, you can get token and NFT rewards. If you want to know more, Here is the link: https://foggie.fogworks.io/?pcode=uZVcLL&cc=1008#/fogworks