The advent of Bitcoin gave developers an opportunity to explore different types of cryptocurrencies. Yet it was Ethereum, a totally new technology, that gave coders the ability to easily create new cryptocurrencies on top of its blockchain, known as tokens. Today, there are tens of thousands of cryptocurrencies, mostly thanks to Ethereum. The Ethereum network sparked the proliferation of the concept of “tokenize everything” via initial coin offerings (ICOs), which allow a project to raise cryptocurrency funds and give investors tokens in exchange. This chapter looks at how that happened. We’ll begin by introducing a few notable examples:
As described in the previous chapter, the beginnings of Ethereum trace back to November 2013, when Vitalik Buterin began emailing around a whitepaper proposing a new protocol based on elements of Bitcoin, Mastercoin, and other projects. This document was disseminated throughout the cryptocurrency community, and developers and backers began to accumulate. Buterin made a public announcement of the Ethereum project in February 2014.
A Swiss-based nonprofit foundation was created to initiate the ICO, and starting in July 2014, for 42 days Ethereum conducted a crowdsale. Approximately 60 million ether tokens were sold, raising some 31,000 BTC (around $18 million at the time). This became the template for many other ICOs in the future.
A decentralized prediction market platform, Gnosis shares some concepts and some personnel with the Augur project, an early Ethereum-based offering that had its ICO in 2015. The Gnosis multisignature wallet is still one of the most widely used in the Ethereum ecosystem, especially for applications such as cold storage of tokens.
The most interesting aspect of the Gnosis project’s ICO was the Dutch-style auctioning system it employed. This novel concept enabled tokens to decline in value over the time of the ICO, encouraging investors to wait until the end to get the best pricing. Most ICOs are conducted the reverse way: cheaper tokens are offered the earlier an investor gets in. However, this proved successful as Gnosis was able to raise over $300 million in 15 minutes while keeping 95% of the cryptocurrency attributed to the project and its founders.
Creating a token allows developers to create a cryptocurrency on the Ethereum network. This enables anyone to issue an asset on a blockchain using one of the most well-known cryptocurrency protocols. The ERC-20 standard on Ethereum is a reference implementation of blockchain assets on the network, paving the way for tokens to have properties that enable their use across many different exchanges, wallets, and other blockchain services. There are other blockchain platforms for issuing tokens. However, issuing an ERC-20 asset on Ethereum is one of the easiest and most secure ways to create a cryptocurrency today.
Outside of technical projects like mobile dapps, distributed computing, or payment mechanisms, tokens have the potential to disrupt existing financial services where bottlenecks still exist. Blockchain and cryptocurrency have the ability to represent something of value in the real world, as long as they can be properly pegged back to a real-world asset.
In complex real estate transactions, for example, tokens on a blockchain could enable better record keeping for owners—the state of Ohio is looking at using blockchain for this purpose. In the future, transfers of assets could be completed more quickly and easily using tokens. Other areas where tokens may prove useful to prove authenticity could include art, cars, and stocks and bonds.
Not all tokens are created equal. One of the most important differentiators when creating tokens is whether they are fungible or nonfungible. Fungible tokens all have the same value and are interchangeable with one another, whereas nonfungible tokens represent something that is unique.
Examples of fungible assets are currencies like the US dollar. One dollar is one dollar, whether it exists in a physical form as coins or a bill or digitally in a bank account or other financial service. Most cryptocurrencies, like bitcoin, ether, and ERC-20 assets, are also fungible.
Items such as cars or houses are nonfungible—each is unique and not interchangeable with any other random car or house. CryptoKitties—digital cats represented on Ethereum as ERC-721 tokens—are another example of a nonfungible asset (we discuss this example more later in this chapter).
There are implications to creating tokens. Cryptocurrency markets are highly volatile. If a token is listed on an exchange and used only for speculation, its price could become highly unstable.
Smart contract development is a nascent area of computer science. It is highly recommended that a third-party auditor (like OpenZeppelin, which contributed background for this chapter) take a look at your code before you bring a token into the wild. Other well-known companies providing such services include Trail of Bits and Chainsecurity.
Ethereum offers many different token types that can be issued. Some popular ones include ERC-20, ERC-721, ERC-223, ERC-777, and ERC-1400. The variety enables developers to create different types of functional cryptocurrencies on top of the Ethereum blockchain.
An exhaustive list of Ethereum token standards is available on GitHub. Some of the tokens listed are currently functional, whereas others are merely proposed ideas.
Thanks to Ethereum’s nature, new types of cryptocurrencies on the Ethereum blockchain may provide real-world benefits. ERC-846, which provides for shared ownership of a token, is a good example of a real-world use case.
One existential question developers should ask when developing blockchain-based solutions is: Is a token necessary? Many tokenization/ICO projects have been developed with a token for fundraising, but with no clear motivation other than to have a cryptocurrency. Although ICOs are a good method of using crypto for fundraising, regulatory pressure is pushing developers to create tokens that have a greater function within projects.
A token may not be useful for a blockchain-based project if it is only for fundraising. In addition, any project looking for stable asset value will not find a token to be a suitable solution, although assets like stablecoins may be. Any processing function that has an asset that is unstable could prove to be problematic in the future. This is an issue already experienced on the blockchain in the form of transaction fees. A fee on the Bitcoin network, for example, can change based on how much network demand there is. The greater the demand, the less space is available in the blocks, which can create a fee market where the highest bidder “wins.” This supply and demand paradigm also exists in Ethereum for gas fees and token prices once they hit exchanges.
As we’ve said, the main way to distribute tokens is via an ICO or similar type of offering. Another alternative is doing an airdrop. Intended to leverage network effects of already existing blockchains, airdrops are free or low-cost disbursements of cryptocurrencies to a large subset of users. The idea is to rapidly give a project a user base from its inception, baking in adoption of an already existing project/cryptocurrency. The largest case so far has been the Stellar Foundation’s $125 million airdrop of its XLM token via the Blockchain.info wallet.
Airdrops may seem like a solution to nascent cryptocurrency adoption, but they’re not without their drawbacks. In particular, there likely are tax implications for users to obtain a cryptocurrency-based asset at zero cost. When sold, there could be a taxable event, depending on jurisdiction. Providing a token at nearly no cost also might not bode well for future value, due to dilution of the underlying cryptocurrency being airdropped. There’s no such thing as a free lunch in economics, so there could be compensating or even extra costs to airdrops.
Different Ethereum tokens have different technical specifications, and they also use different nomenclature depending on how regulators around the world define them. It’s important for developers to understand the various terms being floated regarding how to define tokens:
A security token offering (STO) is an attempt to create an ICO that fits into a regulatory framework. As ICOs mimic some of the qualities of an equity IPO, regulators around the world are increasingly trying to understand how to protect investors from fraud, excessive risk, and theft. The SEC, for example, has published a framework for crypto-based security offerings.
With improving capabilities of the EVM, empowering developers to write better smart contracts, the Ethereum community began creating standards, formalized as Ethereum Requests for Comment (ERCs). These standards are important, as they ensure that apps wanting to interact with Ethereum smart contracts will know which functions and inputs to call. All proposed ERCs start as an Ethereum Improvement Proposal (EIP), which then goes through a vetting process. This is similar to the Bitcoin Improvement Process, discussed in Chapter 3.
The most common ERC standard for Ethereum tokens is ERC-20. Every smart contract that is compliant with the ERC-20 standard will implement the methods shown in Table 5-1.
| Method | Description |
|---|---|
totalSupply() public view returns (uint256 totalSupply) |
Get the total token supply. |
balanceOf(address _owner) public view returns (uint256 balance) |
Get the account balance of another account with address _owner. |
transfer(address _to, uint256 _value) public returns (bool success) |
Send _value amount of tokens from address _from to address _to. Tokens are sent from the address that called the transaction. |
transferFrom(address _from, address _to, uint256 _value) public returns (bool success) |
Send _value amount of tokens from address _from to address _to. |
transferFrom(address _from, address _to, uint256 _value) public returns (bool success) |
Send _value amount of tokens from address _from to address _to. |
approve(address _spender, uint256 _value) public returns (bool success) |
Allow _spender to withdraw from your account, multiple times, up to _value amount. If this function is called again, it overwrites the current allowance with the new _value. |
allowance(address _owner, address _spender) public view returns (uint256 remaining) |
Return the amount which _spender is still allowed to withdraw from _owner. |
Every smart contract that is compliant with the ERC-20 standard will implement the two events shown in Table 5-2. Developers can build applications that listen for these events to be triggered—for example, a cryptocurrency wallet checking to see if any of its Ethereum addresses have received tokens.
| Event | Description |
|---|---|
Transfer(address indexed _from, address indexed _to, uint256 _value) |
Event triggered when tokens are transferred. |
Approval(address indexed _owner, address indexed _spender, uint256 _value) |
Event triggered whenever approve(address _spender, uint256 _value) is called. |
The following is an example of a basic ERC-20 smart contract, Mastering_Blockchain_Token.sol:
pragma solidity ^0.4.21;
contract EIP20Interface {
/// total amount of tokens
uint256 public totalSupply;
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public view returns (uint256 balance);
/// @notice send `_value` tokens to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of tokens to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public returns (bool success);
/// @notice send `_value` tokens to `_to` from `_from` on the condition
/// it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of tokens to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public
returns (bool success);
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public
returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to be spent
function allowance(address _owner, address _spender) public view
returns (uint256 remaining);
// solhint-disable-next-line no-simple-event-func-name
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender,
uint256 _value);
}
contract EIP20 is EIP20Interface {
uint256 constant private MAX_UINT256 = 2**256 - 1;
mapping (address => uint256) public balances;
mapping (address => mapping (address => uint256)) public allowed;
/*
NOTE:
The following variables are OPTIONAL vanities. One does not have to include
them. They allow one to customize the token contract & in no way influence
the core functionality. Some wallets/interfaces might not even bother to look
at this information.
*/
string public name; // Token name: eg Mastering Blockchain Book
uint8 public decimals; // How many decimals to show. Standard is 18.
string public symbol; // An identifier: eg MBB
function EIP20(
uint256 _initialAmount,
string _tokenName,
uint8 _decimalUnits,
string _tokenSymbol
) public {
balances[msg.sender] = _initialAmount; // Give creator initial tokens
totalSupply = _initialAmount; // Update total supply
name = _tokenName; // Set name for display purposes
decimals = _decimalUnits; // Set number of decimals for display
symbol = _tokenSymbol; // Set symbol for display purposes
}
function transfer(address _to, uint256 _value) public returns (bool success)
{
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public
returns (bool success) {
uint256 allowance = allowed[_from][msg.sender];
require(balances[_from] >= _value && allowance >= _value);
balances[_to] += _value;
balances[_from] -= _value;
if (allowance < MAX_UINT256) {
allowed[_from][msg.sender] -= _value;
}
emit Transfer(_from, _to, _value);
return true;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public view
returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}
This token contract was published on the Ropsten testnet and has the following attributes:
Token name: Mastering Blockchain Book
Token symbol: MBB
Token supply: 100 MBB
Token decimal places: 18
To create your own custom token, you can simply copy and paste the preceding code and change these four values in the constructor function:
symbolnamedecimalstotalSupplyERC-721 is a standard for nonfungible tokens. As mentioned earlier, with fungible tokens (like ERC-20 tokens), each token has the exact same attributes. With nonfungible tokens, each token can have different attributes and therefore is unique, which allows for extreme digital scarcity.
Before blockchain, most virtual items could easily be copied. Connecting a virtual good or a real-world item to an ERC-721 token is a way to create a digitally scarce item that cannot be copied or tampered with.
The most famous example of this in the blockchain world is CryptoKitties, virtual cats that are connected to ERC-721 tokens on the Ethereum blockchain. Figure 5-1 shows CryptoKitty #1270015.
Instead of reading the attribute information about CryptoKitty #1270015 from a centralized database, the information is pulled from the CryptoKitty ERC-721 smart contract. Go to function #32 and enter the CryptoKitty ID, which is 1270015. From there it is possible to see the unique attributes for this CryptoKitty stored on the Ethereum blockchain, as shown in Figure 5-2.
getKitty from the main CryptoKitties smart contract responds with data stored in Ethereum about the specific kitty ID 1270015This proposed standard is for the next generation of fungible (ERC-20) tokens. It includes some improvements to the ERC-20 standard, the most important of which is in the way tokens are transferred.
There are two ways that users can move ERC-20 tokens from one address to another:
transfer(address _to, uint256 _value) is a push transaction, where the sender initiates the transfer of tokens.approve(address _spender, uint256 _value) and transferFrom(address _from, address _to, uint256 _value) is a pull transaction, where the sender gives permission to the receiver and then the receiver pulls the tokens out of the sender’s account.If a person sends tokens to a smart contract using a push transaction, then the smart contract will receive the tokens. However, the smart contract will not receive a trigger telling it that it has received the tokens and instructing it to run some code.
This is the reason for a pull transaction—the smart contract receiving the tokens initiates the transaction, and it can recognize when the tokens are received and execute additional code to react to this event. The most common use case for a smart contract receiving and sending tokens is decentralized exchanges, IDEX being the most popular.
Pull transactions work for smart contracts receiving tokens, but their use has led to many tokens accidentally being lost. A common problem is if a user mistakenly sends tokens to a smart contract using a push transaction rather than via the correct pull method; those tokens will be burned and lost forever, because the smart contract will not recognize that it has received the tokens and therefore won’t know to send them to another address at a later time.
ERC-777 proposes to fix this problem by introducing the following improvements:
authorizeOperator(address operator) and revokeOperator(address operator)tokensReceived and tokensToSend hookstokensReceived. In that function, the receiving contract can identify which ERC-777 tokens the contract would like to accept and which it would like to reject (using revert). If an ERC-777 token is received but identified as to be rejected, the token transfer will not complete. It’s like receiving a letter in the mail and sending it back. Similarly, a contract that is requesting a token transfer can receive the tokensToSend hook, and when that hook is called has the option to revert the transaction. It’s less likely that this will happen, because this is the contract that initiated the transfer of tokens—it’s like going to the post office to send a letter, but then changing your mind as you are about to hand it over.send(address to, uint256 amount, bytes data)data field that not only allows the sender to send tokens to a contract, but also can contain specialized logic that triggers a function in the receiving contract. This is similar to how Ethereum transactions are executed.Although the ERC-777 standard is an improvement over ERC-20, it has not been adopted by the industry yet because there is a large switching cost for all stakeholders to move to the new standard. Many projects would have to create new token contracts, and then convince token holders to swap existing tokens for an equivalent amount using the new standard. Exchanges and some dapps would have to update their systems to support the new standard as well.
This standard was designed to track virtual goods in games. For example, the default weapon in a shooting game might be a pistol, but it is possible to purchase the triple-barreled rocket-launching weapon that kills one hundred enemies in one shot.
An ideal token standard for these in-game items would have a mixture of ERC-20 and ERC-721 attributes:
ERC-20 (fungible) so that you can attach a price to the virtual good, and users can then purchase and trade the item
ERC-721 (nonfungible) so that the virtual good can have unique properties—for example, how many rockets it can hold, or how powerful the weapon is.
Like the ERC-777 token standard, this standard includes the concept of an operator, an address that has the authority to move your tokens on your behalf.
Another improvement in this standard is the ability to transfer multiple tokens in one transaction. When transferring ERC-721 tokens, you call the function safeTransferFrom and specify the token to be transferred by its _tokenId:
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)
external payable;
With ERC-1155 tokens, you can call the function safeBatchTransferFrom and specify an array of _ids:
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids,
uint256[] calldata _values, bytes calldata _data) external;
The ability to do batch trades removes another layer of friction, for gamers and game publishers alike.
Enjin, the company that created this standard, is now providing a platform that makes it easy for game publishers to support virtual goods on the blockchain. One of the biggest challenges to gaining widespread adoption is that the Ethereum network is not yet fast enough to support hundreds of thousands of transactions a second, which is a common requirement in large-scale games.
At time of this writing, about 35 games have adopted this standard, and about 100,000 people hold ERC-1155 virtual goods.
Sending funds out of an externally owned account (EOA) wallet in Ethereum only requires one private key. This means if that key is compromised, there’s nothing stopping funds being stolen from the account—it’s a single point of failure. The purpose of a multisignature wallet is to lower the risk of unauthorized removal of funds by requiring multiple private keys to send the funds. This is a similar concept to a bank account that requires multiple signatures to authorize a payment. There isn’t an ERC standard for multisignature wallet contracts, although this type of contract is widely used in the industry.
Every multisignature contract requires M of N signatures to authorize a transaction, where:
N is the number of Ethereum addresses that can authorize a transaction.
M is the minimum number of signatures from among those N unique addresses required for a transaction to be authorized.
Note that M must be less than N. An example would be a 2-of-3 multisignature contract, which means there are three addresses that can authorize a transaction, and only two signatures are required to complete the transaction.
It’s common practice for entities that do ICOs to collect all the funds they raise into multisignature wallets. These ICOs also make their multisignature wallet code transparent and publicly share which addresses can sign a transaction. This transparency increases trust from investors, because at any time investors can audit the funds.
If you audit the multisignature wallet of a well-known company that raised $33 million in its ICO, you can see all the funds that have come in and been sent out. You can also audit the wallet’s M of N:
Calling the function getOwners shows which addresses can authorize transactions. These addresses are called owners. In this case, there are five of them:
Reading the current value of the variable required shows how many signatures are required to execute a transaction. This wallet requires three signatures to execute a transaction.
This wallet that is being audited here is thus a 3-of-5 multisignature wallet.
As you can see in Figure 5-3, you can also audit which addresses signed all the transactions that this wallet has executed.
As this figure shows, the process for executing a multisignature transaction is as follows:
One of the owner addresses (0x00c9…7f69) that is authorized to perform a multisignature transaction calls the submitTransaction function to submit the transaction details. The submitTransaction call performs two events:
It stores the details of the requested transaction.
It adds 0x00c9…7f69 to the list of addresses that confirm the transaction. This address therefore both initiates the transaction and confirms the transaction.
The submitTransaction call occurs in block #7331149.
A second owner (0x0063…ad4f) calls the function confirmTransaction to give the second of the three required signatures. This confirmTransaction call occurs in block #7331154.
A third owner (0x003c…766c) calls the function confirmTransaction to give the third signature. This call leads to two events:
The third owner confirms the transaction.
The contract recognizes that all required signatures have been given and then executes the transaction using the details that were submitted in step 1.
This confirmTransaction call occurs in block #7458500.
Before Ethereum, every cryptocurrency exchange had to be controlled and managed by a company—a centralized authority. Centralized exchanges still exist, with popular examples including Coinbase, Bitstamp, and Gemini. The purpose of an exchange is to act as a trusted platform where two parties can exchange cryptocurrencies securely. To accomplish this, an exchange must do the following for its customers:
Provide a secure place to deposit/withdraw crypto, and hold the funds in escrow.
Provide an order book, so that two parties can agree on a price to trade the crypto at.
Swap the cryptocurrencies between the two parties.
A smart contract has the capability to perform the following three actions:
Send/receive and hold ETH and ERC-20 tokens.
Record price requests from EOA accounts.
If two price requests match, change ownership of the corresponding cryptocurrencies.
A good example of a decentralized exchange on Ethereum is IDEX. Even though the IDEX website looks similar to a centralized exchange, there are significant differences between the two types of exchanges.
All of the code running a centralized exchange is deployed to a web hosting provider—for example, AWS or Azure. Frontend code running on a decentralized exchange is also deployed to a web hosting provider, but the backend code is written into a smart contract and deployed to the Ethereum network. The database is just the Ethereum or some other smart contract blockchain, as illustrated in Figure 5-4.
Advantages of a decentralized exchange include:
Decentralized exchanges also have a few downsides, though. Notably, they are:
One other big difference between the two is that there is no need to ask anyone’s permission to add an ERC-20 token to a decentralized exchange. As soon as an ERC-20 token is created, it can instantly be traded on a decentralized exchange. Depending on whom you speak to, this can be considered a pro or a con. We’ll talk more about decentralized exchanges and how they’re used in Chapter 7.
You can view all the ERC standards online, and OpenZeppelin provides a great library of ERC-compliant smart contracts.
Ethereum is by far the largest blockchain for tokenization today. In a short time frame, any developer can create their own blockchain-based asset. A lot of work has been done to give programmers a framework within Ethereum to operate in, with the different ERC standards providing a plethora of options. Ethereum-based tokenization has enabled the creation of a number of innovative new blockchain-based applications, and there surely will be many more as this technology continues to mature.