All of that brings us to our post today, where we will study a simple example of a decentralized app, or dapp, and you will implement it yourself on your local machine.

Dapps are applications written using languages such as JavaScript, HTML, and CSS, leveraging smart contracts on the backend to display and change information. They are an important part of a movement from centralized Web2 architectures to a decentralized Web3 architecture.

This post will take you through writing a smart contract to programming a non-fungible token contract in Solidity, the most popular language for writing Ethereum smart contracts. Then, we will connect to this smart contract using a simple React application.

The dapp you will be implementing today is a small spoof of the popular CryptoKitties collectible dapp.

Development Setup

Make sure you have Node, Git, and Metamask installed and set up. Metamask is an extension that allows you to connect to and interact with Ethereum dapps.

Navigate to this GitHub repository to clone the code you’ll need to get started for this workshop.



$ cd dapp-demo $ git clone https://github.com/jp3hou/dapp-demo $ cd dapp-demo

After you’ve cloned the repository, open up the project in a text editor and navigate to the “contracts/KatCoin.sol” file.

Some Ethereum Basics

Ether is the currency used in the Ethereum ecosystem to pay transaction fees.

Ethereum tracks state through accounts, which can be either externally owned accounts or contract accounts. Externally owned accounts belong to a user and are controlled by a private key, whereas contract accounts are controlled by their contract code.

Each account has a 20-byte address and contains four fields:

The nonce, a counter to make sure a transaction is only processed once

The ether balance

The storage

The contract code (if this is a contract account)

Transitions from one state to another occur through transactions, which contain a sender, a recipient, the amount of ether being transferred from the sender to the recipient, and gas limits for this transaction.

“Gas” is used to track how much computing power is used. This prevents DoS attacks by requiring that users pay for every resource they consume and canceling functions once a smart contract reaches the allotted gas limit.

Some Solidity Basics

Solidity is the most popular language used for Ethereum smart contract development. It is:

Statically typed

Object-oriented

Compiled into EVM bytecode before being deployed to the blockchain

An Empty KatCoin Template

pragma solidity ^0.4.24; import './Ownable.sol'; contract KatCoin is Ownable {

event Minted(address owner, uint256 katId, uint256 genes);

event Sent(address from, address to, uint256 katId); struct Kat {

uint32 color;

uint16 generation;

} Kat[] kats; mapping (uint256 => address) private _katOwner;

mapping (address => uint256) private _ownedKatsCount; constructor() public {

for (uint32 i = 0; i < 30; i++) {

kats.push(Kat((i + 3) * 32, 0));

_mint(address(this), kats.length - 1, i * 32);

}

} function balanceOf(address owner) public view returns (uint256) {

// FILL ME OUT

} function ownerOf(uint256 katId) public view returns (address) {

// FILL ME OUT

} function getKat(uint256 katId) public view returns (

uint32 color,

uint16 generation

) {

// FILL ME OUT

} function purchase(uint256 katId) public payable {

// FILL ME OUT

} function _transferFrom(address from, address to, uint256 katId) internal {

// FILL ME OUT

} function _mint(

address to,

uint256 katId,

uint256 genes

) internal {

// FILL ME OUT

} function _addKatTo(address to, uint256 katId) internal {

// FILL ME OUT

} function _removeKatFrom(address from, uint256 katId) internal {

// FILL ME OUT

}

}

You’ll see in this file that we are deriving our KatCoin contract from an Ownable contract in the same directory, which defines some basic auth control and user permission features that our KatCoin inherits. Smart contracts unlock the opportunity to reimagine the architecture of current applications and systems from the ground up, with user data and privacy in mind.

Things to note:

The “pragma” call in the beginning of the file defines which Solidity compiler version to use

Events can be defined and thrown/caught. This can be useful for debugging or logging purposes

We are defining private state variables to keep track of which address owns which KatCoin and how many KatCoins an address owns. The ‘private’ designation ensures that these variables are accessible only to the current smart contract, not any of its descendants

The constructor will mint 30 KatCoins upon initialization of this contract and assign the owner of each token to the smart contract itself

Filling out the balanceOf and ownerOf functions

function balanceOf(address owner) public view returns (uint256) {

require(owner != address(0));

return _ownedKatsCount[owner];

} function ownerOf(uint256 katId) public view returns (address) {

address owner = _katOwner[katId];

require(owner != address(0));

return owner;

}

Things to note:

“require” is a built-in Solidity function that throws an exception if the given condition returns false

Public functions are part of a contract’s interface, and can be called either internally from within the contract or externally from other contracts/addresses

View functions are read-only functions, meaning they do not modify the state of a smart contract

Filling out the getKat function

function getKat(uint256 katId) public view returns (

uint32 color,

uint16 generation

) {

Kat storage kat = kats[katId]; color = kat.color;

generation = kat.generation;

}

Things to note:

A struct type is assigned to a local variable with data location ‘storage’, which stores a reference to the struct

Filling out the _addKatTo and _removeKatFrom functions

function _addKatTo(address to, uint256 katId) internal {

require(_katOwner[katId] == address(0));

_katOwner[katId] = to;

_ownedKatsCount[to]++;

} function _removeKatFrom(address from, uint256 katId) internal {

require(ownerOf(katId) == from);

_ownedKatsCount[from]--;

_katOwner[katId] = address(0);

}

Things to note:

Internal functions can only be accessed by the current contract and by its descendants

_addKatTo validates that the KatCoin does not currently belong to anyone before assigning it to an owner

_removeKatFrom validates that the owner of the KatCoin is the only person who can transfer ownership

These functions change the contract state variables to reflect the updated ownership of these KatCoins

Filling out the _mint function

function _mint(address to, uint256 katId, uint256 genes) internal {

require(to != address(0));

_addKatTo(to, katId);

emit Minted(to, katId, genes);

}

Things to note:

_mint validates that the token is to be assigned to a valid address

This function leverages our previously implemented internal function to assign ownership of that KatCoin to the given address

Emits the “Minted” event

Filling out the _transferFrom function

function _transferFrom(

address from,

address to,

uint256 katId

)

internal {

require(from == _katOwner[katId]);

require(to != address(0)); _removeKatFrom(from, katId);

_addKatTo(to, katId); emit Sent(from, to, katId);

}

Things to note:

_transferFrom validates proper token ownership and valid address

Leverages our previously implemented internal _removeKatFrom and _addKatTo functions to reassign ownership from the given ‘from’ address to the ‘to’ address

Emits the “Sent” event

Filling out the purchase function

function purchase(uint256 katId) public payable {

require(katId >= 0 && katId < 30);

require(address(this) == _katOwner[katId]);

_transferFrom(address(this), msg.sender, katId);

owner.transfer(msg.value);

}

Things to note:

Validates that the token is one of the 30 tokens initialized in the constructor

Validates that the smart contract is the current owner of the token about to be purchased

The ‘payable’ modifier indicates that this function can receive ether

Leverages our previously implemented _transferFrom function to change ownership of this token

Credit the owner the value of the message sent into the function

Time to compile and deploy!

Open up a terminal window and run the following:

$ npm run blockchain

This will start a local blockchain server on your computer running on port 8545. Make sure to pay attention to the output, as it will spin up a set of test accounts seeded with 100 test ether each for you to work with.

Copy one of the private keys given in the output, open up your Metamask extension, select the “Import Account” option and paste in that private key in order to import that account to interact with your dapp. Make sure you’ve selected your local network (Localhost 8545) to connect to, not the Ethereum mainnet.

Note: These Truffle-generated private keys are intended for development only! Treat the private keys you own for your main accounts with care.

Next, open up a new terminal window and run:

$ npm run migrate

This will compile your code to Ethereum Virtual Machine (EVM) bytecode and deploy your smart contracts onto your blockchain server.

Pay attention to the output of this command, it will tell you how much ether was consumed as part of the gas fee for deploying these contracts onto your blockchain server and also what the contract address for your KatCoin smart contract is.

Copy the “contract address” value in the third line of the KatCoin deploy output

With the contract address that you’ve copied from the last step, open up the “src/App.js” file in your project and replace the comment that says “CONTRACT ADDRESS GOES HERE” with a string containing that address value. The final results should look like this:

constructor() {

super();

const Kats = window.web3.eth.contract(KatCoin.abi); this.state = {

kats: Kats.at("0x8d37b292DE9c41c7A9969461718b1cA8008F421f"),

loading: true,

cats: []

};

}

Run the frontend!

In your terminal, run:

$ npm run start

This will spin up your react app at http://localhost:3000. You should see the 30 KatCoins that the smart contract has minted, with their ids and their owner addresses.

Click on one of the KatCoins to try to purchase it (which should try to transfer it from the smart contract to the account in your Metamask wallet), and you ought to see a popup in your Metamask extension asking for confirmation for this purchase transaction.

Confirm this transaction to complete your KatCoin purchase!

Congratulations! You should now see that the owner address has changed from the smart contract to your account, and you should be able to see your transaction history in Metamask.

Purchasing a KatCoin and confirming the transaction with Metamask

Some Security Concerns

Keep in mind that smart contracts deal with real world valuemoney, and this might put user funds at risk. Bugs in smart contracts will literally cost you.

Smart contracts are immutable once deployed to the blockchain. However, you can program self-destruct functionality into them beforehand if you wish. Check out CryptoFin’s security auditing checklist for an example of some guidelines to follow.