Learn what tokens are, how to create them by using more-advanced Solidity data structures, and how to secure them by using third-party libraries like OpenZeppelin.
By the end of this post, readers will be able to:
Let’s start off by introducing the idea of tokenomics, the nature of tokens on the blockchain and what gives them value, the relationship between tokens and smart contracts, the loose definitions of coins and tokens, and several examples.
Tokenomics, or the economics of tokens, refers to how blockchain tokens get conceptualized, produced, valued, distributed, traded, and used. A blockchain token represents an asset or utility on a blockchain platform. Essentially, it’s a symbol of value.
On a blockchain, an asset can be tokenized, or represented as a token. Commodities, like gold and silver, and currencies, like the US dollar, can all be represented as tokens. Other types of goods and assets can also be tokenized. These include real-estate properties, cars, and even works of art. In fact, virtually anything that holds value can be represented as a token on a blockchain.
We create tokens on the Ethereum blockchain by using smart contracts. The token itself is the symbol of value. The ownership of a token by a blockchain participant (and thus their ownership of the value that the token symbolizes) gets represented in the participant’s wallet. The rules and logic that create and maintain the token get encoded in a token smart contract.
Imagine a digital wallet that contains records of all the assets that an individual owns, both digital and physical, in a single place. They can manage their home ownership, car payments, artwork, investments, video game items, and more with math that cryptographically proves their ownership of these items and that verifies any transactions associated with them.
Some people in the blockchain community use the terms token and coin interchangeably. However, they represent two similar but distinct concepts.
Think of a mapping as an association between two variables. For instance, we can map account balances to account addresses. The developer can thus associate a balance with a specific address and then retrieve the current balance for that account at any time.
In Solidity, a mapping is a variable type that we define by using the following syntax:
`mapping(_KeyType => _ValueType)`
Mappings are flexible data types that allow us to efficiently link pieces of data together.
pragma solidity ^0.5.0;
contract ArcadeToken {
address payable owner = msg.sender;
string public symbol = "ARCD";
uint public exchange_rate = 100;
mapping(address => uint) balances;
function balance() public view returns(uint) {
return balances[msg.sender];
}
function transfer(address recipient, uint value) public {
balances[msg.sender] -= value;
balances[recipient] += value;
}
function purchase() public payable {
uint amount = msg.value * exchange_rate;
balances[msg.sender] += amount;
owner.transfer(msg.value);
}
function mint(address recipient, uint value) public {
require(msg.sender == owner, "You do not have permission to mint tokens!");
balances[recipient] += value;
}
}
All the functions included in the token contract above gets displayed and accessed in the Remix IDE.
For testing purposes after deployment, use two addresses in the Remix IDE to test both the transfer of tokens and the require
function that’s associated with the mint
function.
Call the balance
function several times to show how the balances of the different accounts change as different contract functions get used.
We can use third-party libraries to make our smart contracts both more secure and more efficient to write.
OpenZeppelin is a company that specializes in developing secure smart contracts that use Ethereum community standards. They provide several libraries for smart contract development.
The OpenZeppelin project includes many standardized smart contracts that the blockchain development community can adapt, customize, and build from. Developers can thus write more-secure and more-efficient Solidity code.
We could use the OpenZeppelin SafeMath library to help secure our smart contracts, but please note: SafeMath was used for compiler versions earlier than 0.8.0. After that compiler version, it is no longer needed as integer overflow and integer underflow are automatically taken care of in newer compiler versions.
Only the Remix IDE supports importing libraries directly from GitHub. Other IDEs require a different method of importing libraries, such as copying the code from the file that’s hosted on GitHub into the new contract file before creating your contract.
To import OpenZeppelin contracts from GitHub, follow the code example below:
pragma solidity ^0.5.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
Writing smart contracts (like many programming activities) heavily relies on the object-oriented programming (OOP) principles of inheritance and composition.
There are two core targets that we want to achieve by using these principles:
In OOP, we use inheritance when a particular type of relationship exists between classes. Specifically, this happens when one class is a specialized version of the other, more-general class.
In OOP, we use composition for cases where a class or object has elements of another class or object.
As the name suggests, composition emphasizes the composition of a class or object based on elements of other types. By contrast, inheritance emphasizes being of a certain type. Composition contains elements of other classes that provide the desired functionality.
Both approaches have their strengths and work better or worse for certain use cases. Inheritance makes sense when a large overlap (like 80%) exists between two classes—and one class is a specialized case of the more-general class. By contrast, composition is more flexible. With composition, we can choose the components that we want to use to construct a new class.
Until next time, here’s a twitter thread summary of this post: