A token can serve as a form of currency, with a value determined through private trade.
A token can represent a resource earned or produced in a sharing economy or resource-sharing environment; for example, a storage or CPU token representing resources that can be shared over a network.
A token can represent ownership of an intrinsic or extrinsic, tangible or intangible asset; for example, gold, real estate, a car, oil, energy, MMOG items, etc.
A token can represent access rights and grant access to a digital or physical property, such as a discussion forum, an exclusive website, a hotel room, or a rental car.
A token can represent shareholder equity in a digital organization (e.g., a DAO) or legal entity (e.g., a corporation).
A token can represent voting rights in a digital or legal system.
A token can represent a digital collectible (e.g., CryptoPunks) or physical collectible (e.g., a painting).
A token can represent a digital identity (e.g., avatar) or legal identity (e.g., national ID).
A token can represent a certification or attestation of fact by some authority or by a decentralized reputation system (e.g., marriage record, birth certificate, college degree).
A token can be used to access or pay for a service.
totalSupplyReturns the total units of this token that currently exist. ERC20 tokens can have a fixed or a variable supply.
balanceOfGiven an address, returns the token balance of that address.
transferGiven an address and amount, transfers that amount of tokens to that address, from the balance of the address that executed the transfer.
transferFromGiven a sender, recipient, and amount, transfers tokens from one account to another. Used in combination with approve.
approveGiven a recipient address and amount, authorizes that address to execute several transfers up to that amount, from the account that issued the approval.
allowanceGiven an owner address and a spender address, returns the remaining amount that the spender is approved to withdraw from the owner.
TransferEvent triggered upon a successful transfer (call to transfer or transferFrom) (even for zero-value transfers).
ApprovalEvent logged upon a successful call to approve.
nameReturns the human-readable name (e.g., “US Dollars”) of the token.
symbolReturns a human-readable symbol (e.g., “USD”) for the token.
decimalsReturns the number of decimals used to divide token amounts. For example, if decimals is 2, then the token amount is divided by 100 to get its user representation.
contractERC20{functiontotalSupply()constantreturns(uinttheTotalSupply);functionbalanceOf(address_owner)constantreturns(uintbalance);functiontransfer(address_to,uint_value)returns(boolsuccess);functiontransferFrom(address_from,address_to,uint_value)returns(boolsuccess);functionapprove(address_spender,uint_value)returns(boolsuccess);functionallowance(address_owner,address_spender)constantreturns(uintremaining);eventTransfer(addressindexed_from,addressindexed_to,uint_value);eventApproval(addressindexed_owner,addressindexed_spender,uint_value);}
mapping(address=>uint256)balances;
mapping(address=>mapping(address=>uint256))publicallowed;
A simple and easy-to-read implementation of an ERC20-compatible token.
This implementation is ERC20-compatible, with additional security precautions. It forms the basis of OpenZeppelin libraries implementing more complex ERC20-compatible tokens with fundraising caps, auctions, vesting schedules, and other features.
$ mkdir METoken $ cd METoken METoken $ truffle init METoken $ npm init
METoken/ +---- contracts | `---- Migrations.sol +---- migrations | `---- 1_initial_migration.js +---- package.json +---- test +---- truffle-config.js `---- truffle.js
METoken/ +---- contracts | `---- Migrations.sol +---- migrations | `---- 1_initial_migration.js +---- package.json +---- test +---- truffle-config.js +---- truffle.js `---- .env *new file*
$ npm install openzeppelin-solidity@1.12.0 + openzeppelin-solidity@1.12.0 added 1 package from 1 contributor and audited 2381 packages in 4.074s
1pragma solidity^0.4.21;2 3import'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol';4 5contractMETokenisStandardToken{6stringpublicconstantname='Mastering Ethereum Token';7stringpublicconstantsymbol='MET';8uint8publicconstantdecimals=2;9uintconstant_initial_supply=2100000000;10 11functionMEToken()public{12totalSupply_=_initial_supply;13balances[msg.sender]=_initial_supply;14emitTransfer(address(0),msg.sender,_initial_supply);15}16}
$ truffle compile Compiling ./contracts/METoken.sol... Compiling ./contracts/Migrations.sol... Compiling openzeppelin-solidity/contracts/math/SafeMath.sol... Compiling openzeppelin-solidity/contracts/token/ERC20/BasicToken.sol... Compiling openzeppelin-solidity/contracts/token/ERC20/ERC20.sol... Compiling openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol... Compiling openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol...
1varMEToken=artifacts.require("METoken");2 3module.exports=function(deployer){4// Deploy the METoken contract as our only task5deployer.deploy(METoken);6};
$ truffle migrate --network ganache Using network 'ganache'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0xb2e90a056dc6ad8e654683921fc613c796a03b89df6760ec1db1084ea4a084eb Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0 Saving successful migration to network... ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying METoken... ... 0xbe9290d59678b412e60ed6aefedb17364f4ad2977cfb2076b9b8ad415c5dc9f0 METoken: 0x345ca3e014aaf5dca488057592ee47305d9b3e10 Saving successful migration to network... ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0 Saving artifacts...
$ truffle console --network ganache truffle(ganache)>
truffle(ganache)> METoken
{ [Function: TruffleContract]
_static_methods:
[...]
currentProvider:
HttpProvider {
host: 'http://localhost:7545',
timeout: 0,
user: undefined,
password: undefined,
headers: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '5777' }
truffle(ganache)> METoken.address '0x345ca3e014aaf5dca488057592ee47305d9b3e10'
truffle(ganache)> METoken.deployed().then(instance => instance.totalSupply())
BigNumber { s: 1, e: 9, c: [ 2100000000 ] }
truffle(ganache)> let accounts
undefined
truffle(ganache)> web3.eth.getAccounts((err,res) => { accounts = res })
undefined
truffle(ganache)> accounts[0]
'0x627306090abab3a6e1400e9345bc60c78a8bef57'
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.balanceOf(accounts[0]).then(console.log) })
undefined
truffle(ganache)> BigNumber { s: 1, e: 9, c: [ 2100000000 ] }
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.transfer(accounts[1], 100000) })
undefined
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.balanceOf(accounts[0]).then(console.log) })
undefined
truffle(ganache)> BigNumber { s: 1, e: 9, c: [ 2099900000 ] }
undefined
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.balanceOf(accounts[1]).then(console.log) })
undefined
truffle(ganache)> BigNumber { s: 1, e: 5, c: [ 100000 ] }
METoken/ +---- contracts | +---- Faucet.sol | +---- METoken.sol | `---- Migrations.sol
varFaucet=artifacts.require("Faucet");module.exports=function(deployer){// Deploy the Faucet contract as our only taskdeployer.deploy(Faucet);};
$ truffle console --network ganache truffle(ganache)> compile Compiling ./contracts/Faucet.sol... Writing artifacts to ./build/contracts truffle(ganache)> migrate Using network 'ganache'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x89f6a7bd2a596829c60a483ec99665c7af71e68c77a417fab503c394fcd7a0c9 Migrations: 0xa1ccce36fb823810e729dce293b75f40fb6ea9c9 Saving artifacts... Running migration: 2_deploy_contracts.js Replacing METoken... ... 0x28d0da26f48765f67e133e99dd275fac6a25fdfec6594060fd1a0e09a99b44ba METoken: 0x7d6bf9d5914d37bcba9d46df7107e71c59f3791f Saving artifacts... Running migration: 3_deploy_faucet.js Deploying Faucet... ... 0x6fbf283bcc97d7c52d92fd91f6ac02d565f5fded483a6a0f824f66edc6fa90c3 Faucet: 0xb18a42e9468f7f1342fa3c329ec339f254bc7524 Saving artifacts...
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.transfer(Faucet.address, 100000) })
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.balanceOf(Faucet.address).then(console.log)})
truffle(ganache)> BigNumber { s: 1, e: 5, c: [ 100000 ] }
1// Version of Solidity compiler this program was written for2pragma solidity^0.4.19;3 4import'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol';5 6 7// A faucet for ERC20 token MET8contractMETFaucet{9 10StandardTokenpublicMEToken;11addresspublicMETOwner;12 13// METFaucet constructor, provide the address of METoken contract and14// the owner address we will be approved to transferFrom15functionMETFaucet(address_METoken,address_METOwner)public{16 17// Initialize the METoken from the address provided18METoken=StandardToken(_METoken);19METOwner=_METOwner;20}21 22functionwithdraw(uintwithdraw_amount)public{23 24// Limit withdrawal amount to 10 MET25require(withdraw_amount<=1000);26 27// Use the transferFrom function of METoken28METoken.transferFrom(METOwner,msg.sender,withdraw_amount);29}30 31// REJECT any incoming ether32function()publicpayable{revert();}33 34}
StandardTokenpublicMEToken;addresspublicMETOwner;
// METFaucet constructor - provide the address of the METoken contract and// the owner address we will be approved to transferFromfunctionMETFaucet(address_METoken,address_METOwner)public{// Initialize the METoken from the address providedMEToken=StandardToken(_METoken);METOwner=_METOwner;}
// Use the transferFrom function of METokenMEToken.transferFrom(METOwner,msg.sender,withdraw_amount);
// REJECT any incoming etherfunction()publicpayable{revert();}
varMEToken=artifacts.require("METoken");varMETFaucet=artifacts.require("METFaucet");varowner=web3.eth.accounts[0];module.exports=function(deployer){// Deploy the METoken contract firstdeployer.deploy(METoken,{from:owner}).then(function(){// Then deploy METFaucet and pass the address of METoken and the// address of the owner of all the MET who will approve METFaucetreturndeployer.deploy(METFaucet,METoken.address,owner);});}
$ truffle console --network ganache
truffle(ganache)> migrate
Using network 'ganache'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x79352b43e18cc46b023a779e9a0d16b30f127bfa40266c02f9871d63c26542c7
Migrations: 0xaa588d3737b611bafd7bd713445b314bd453a5c8
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing METoken...
... 0xc42a57f22cddf95f6f8c19d794c8af3b2491f568b38b96fef15b13b6e8bfff21
METoken: 0xf204a4ef082f5c04bb89f7d5e6568b796096735a
Replacing METFaucet...
... 0xd9615cae2fa4f1e8a377de87f86162832cf4d31098779e6e00df1ae7f1b7f864
METFaucet: 0x75c35c980c0d37ef46df04d31a140b65503c0eed
Saving artifacts...
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.approve(METFaucet.address, 100000) })
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.balanceOf(web3.eth.accounts[1]).then(console.log) })
truffle(ganache)> BigNumber { s: 1, e: 0, c: [ 0 ] }
truffle(ganache)> METFaucet.deployed().then(instance =>
{ instance.withdraw(1000, {from:web3.eth.accounts[1]}) } )
truffle(ganache)> METoken.deployed().then(instance =>
{ instance.balanceOf(web3.eth.accounts[1]).then(console.log) })
truffle(ganache)> BigNumber { s: 1, e: 3, c: [ 1000 ] }
To detect whether the destination address is a contract, the ERC223 reference implementation uses a small segment of inline bytecode in a rather creative way:
functionisContract(address_addr)privateviewreturns(boolis_contract){uintlength;assembly{// retrieve the size of the code on target address; this needs assemblylength:=extcodesize(_addr)}return(length>0);}
The ERC223 contract interface specification is:
interfaceERC223Token{uintpublictotalSupply;functionbalanceOf(addresswho)publicviewreturns(uint);functionname()publicviewreturns(string_name);functionsymbol()publicviewreturns(string_symbol);functiondecimals()publicviewreturns(uint8_decimals);functiontotalSupply()publicviewreturns(uint256_supply);functiontransfer(addressto,uintvalue)publicreturns(boolok);functiontransfer(addressto,uintvalue,bytesdata)publicreturns(boolok);functiontransfer(addressto,uintvalue,bytesdata,stringcustom_fallback)publicreturns(boolok);eventTransfer(addressindexedfrom,addressindexedto,uintvalue,bytesindexeddata);}
To offer an ERC20-compatible interface
To transfer tokens using a send function, similar to ether transfers
To be compatible with ERC820 for token contract registration
To allow contracts and addresses to control which tokens they send through a tokensToSend function that is called prior to sending
To enable contracts and addresses to be notified of the tokens’ receipt by calling a tokensReceived function in the recipient, and to reduce the probability of tokens being locked into contracts by requiring contracts to provide a tokensReceived function
To allow existing contracts to use proxy contracts for the tokensToSend and tokensReceived functions
To operate in the same way whether sending to a contract or an EOA
To provide specific events for the minting and burning of tokens
To enable operators (trusted third parties, intended to be verified contracts) to move tokens on behalf of a token holder
To provide metadata on token transfer transactions in userData and operatorData fields
The ERC777 contract interface specification is:
interfaceERC777Token{functionname()publicconstantreturns(string);functionsymbol()publicconstantreturns(string);functiontotalSupply()publicconstantreturns(uint256);functiongranularity()publicconstantreturns(uint256);functionbalanceOf(addressowner)publicconstantreturns(uint256);functionsend(addressto,uint256amount,bytesuserData)public;functionauthorizeOperator(addressoperator)public;functionrevokeOperator(addressoperator)public;functionisOperatorFor(addressoperator,addresstokenHolder)publicconstantreturns(bool);functionoperatorSend(addressfrom,addressto,uint256amount,bytesuserData,bytesoperatorData)public;eventSent(addressindexedoperator,addressindexedfrom,addressindexedto,uint256amount,bytesuserData,bytesoperatorData);eventMinted(addressindexedoperator,addressindexedto,uint256amount,bytesoperatorData);eventBurned(addressindexedoperator,addressindexedfrom,uint256amount,bytesuserData,bytesoperatorData);eventAuthorizedOperator(addressindexedoperator,addressindexedtokenHolder);eventRevokedOperator(addressindexedoperator,addressindexedtokenHolder);}
The ERC777 tokens sender hook specification is:
interfaceERC777TokensSender{functiontokensToSend(addressoperator,addressfrom,addressto,uintvalue,bytesuserData,bytesoperatorData)public;}
The ERC777 tokens recipient hook specification is:
interfaceERC777TokensRecipient{functiontokensReceived(addressoperator,addressfrom,addressto,uintamount,bytesuserData,bytesoperatorData)public;}
deed: A legal document that is signed and delivered, especially one regarding the ownership of property or legal rights.
To grasp the basic difference between ERC20 and ERC721, it is sufficient to look at the internal data structure used in ERC721:
// Mapping from deed ID to ownermapping(uint256=>address)privatedeedOwner;
The ERC721 contract interface specification is:
interfaceERC721/* is ERC165 */{eventTransfer(addressindexed_from,addressindexed_to,uint256_deedId);eventApproval(addressindexed_owner,addressindexed_approved,uint256_deedId);eventApprovalForAll(addressindexed_owner,addressindexed_operator,bool_approved);functionbalanceOf(address_owner)externalviewreturns(uint256_balance);functionownerOf(uint256_deedId)externalviewreturns(address_owner);functiontransfer(address_to,uint256_deedId)externalpayable;functiontransferFrom(address_from,address_to,uint256_deedId)externalpayable;functionapprove(address_approved,uint256_deedId)externalpayable;functionsetApprovalForAll(address_operateor,boolean_approved)payable;functionsupportsInterface(bytes4interfaceID)externalviewreturns(bool);}
The ERC721 optional interface for metadata is:
interfaceERC721Metadata/* is ERC721 */{functionname()externalpurereturns(string_name);functionsymbol()externalpurereturns(string_symbol);functiondeedUri(uint256_deedId)externalviewreturns(string_deedUri);}
The ERC721 optional interface for enumeration is:
interfaceERC721Enumerable/* is ERC721 */{functiontotalSupply()externalviewreturns(uint256_count);functiondeedByIndex(uint256_index)externalviewreturns(uint256_deedId);functioncountOfOwners()externalviewreturns(uint256_count);functionownerByIndex(uint256_index)externalviewreturns(address_owner);functiondeedOfOwnerByIndex(address_owner,uint256_index)externalviewreturns(uint256_deedId);}
The ability to give specific addresses, or sets of addresses (i.e., multisignature schemes), special capabilities, such as blacklisting, whitelisting, minting, recovery, etc.
The ability to deliberately destroy (“burn”) tokens by transferring them to an unspendable address or by erasing a balance and reducing the supply.
The ability to add to the total supply of tokens, at a predictable rate or by “fiat” of the creator of the token.
The ability to offer tokens for sale, for example through an auction, market sale, reverse auction, etc.
The ability to set predefined and immutable limits on the total supply (the opposite of the “minting” feature).
Functions to recover funds, reverse transfers, or dismantle the token that can be activated by a designated address or set of addresses.
The ability to restrict actions (such as token transfers) to specific addresses. Most commonly used to offer tokens to “accredited investors” after vetting by the rules of different jurisdictions. There is usually a mechanism for updating the whitelist.
The ability to restrict token transfers by disallowing specific addresses. There is usually a function for updating the blacklist.