Description
The final standard can be found here: https://eips.ethereum.org/EIPS/eip-20
ERC: 20
Title: Token standard
Status: Draft
Type: Informational
Created: 19-11.2015
Resolution: https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs
Abstract
The following describes standard functions a token contract can implement.
Motivation
Those will allow dapps and wallets to handle tokens across multiple interfaces/dapps.
The most important here are, transfer
, balanceOf
and the Transfer
event.
Specification
Token
Methods
NOTE: An important point is that callers should handle false
from returns (bool success)
. Callers should not assume that false
is never returned!
totalSupply
function totalSupply() constant returns (uint256 totalSupply)
Get the total token supply
balanceOf
function balanceOf(address _owner) constant returns (uint256 balance)
Get the account balance of another account with address _owner
transfer
function transfer(address _to, uint256 _value) returns (bool success)
Send _value
amount of tokens to address _to
transferFrom
function transferFrom(address _from, address _to, uint256 _value) returns (bool success)
Send _value
amount of tokens from address _from
to address _to
The transferFrom
method is used for a withdraw workflow, allowing contracts to send tokens on your behalf, for example to "deposit" to a contract address and/or to charge fees in sub-currencies; the command should fail unless the _from
account has deliberately authorized the sender of the message via some mechanism; we propose these standardized APIs for approval:
approve
function approve(address _spender, uint256 _value) returns (bool success)
Allow _spender to withdraw from your account, multiple times, up to the _value amount. If this function is called again it overwrites the current allowance with _value.
allowance
function allowance(address _owner, address _spender) constant returns (uint256 remaining)
Returns the amount which _spender
is still allowed to withdraw from _owner
Events
Transfer
event Transfer(address indexed _from, address indexed _to, uint256 _value)
Triggered when tokens are transferred.
Approval
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
Triggered whenever approve(address _spender, uint256 _value)
is called.
History
Historical links related to this standard:
- Original proposal from Vitalik Buterin: https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs/499c882f3ec123537fc2fccd57eaa29e6032fe4a
- Reddit discussion: https://www.reddit.com/r/ethereum/comments/3n8fkn/lets_talk_about_the_coin_standard/
Activity
frozeman commentedon Nov 19, 2015
recent discussion from https://gist.github.com/frozeman/090ae32041bcfe120824
@vbuterin said:
Yeah,
createCheque
andcashCheque
as above, plustransferCheque(address _from, address _to, uint256 _value)
sounds good. In that case, we should probably remove the_to
argument fromcashCheque
; generally, you can only cash cheques from your own bank account.We probably also want
getChequeValue(address _from, address _for)
. We then have a choice of whether we want to keep the value argument incashCheque
rather than simply only allowing cashing in 100% of whatever is in there. If we want to fully follow the cheque analogy, this triad seems most intuitive to me:Question: does running createCheque twice add the value of the two cheques together? Are there legitimate use cases for creating a cheque multiple times and then cashing either once or multiple times?
@nmushegian said:
All the functions that return uint should
return (uint, bool)
instead. You can easily make up scenarios where a 0 return value is ambiguous and significant. Is there any other simpler pattern for handling this?@niran said:
I think the value parameter is useful in
cashCheque
. It absolves callers from having to verify that the amount they needed was provided, and from having to refund amounts greater than what was needed. cashCheque would only succeed if the provided value was remaining in the cheque.Also, I think using
createCheque(2**100)
for the approve use case is going to lead to less clear code. It gets better if you make the magic number a constant, likecreateCheque(UNLIMITED_CHEQUE_VALUE)
, but lots of people won't do that. I think it's worth having a createBlankCheque or something for the approve scenario. Most tokens will use the TokenLib to handle all of the cheque logic, so it doesn't really make things worse for token authors.@caktux
I also think there is a problem with the terminology of cheques since they imply one-offs. Cheques are also unique, and here cheques wouldn't return unique IDs or anything; those are merely approval methods for transfers using internal bookkeeping. I think the current approve/transfer terminology is accurate and simple enough, instead of ending up with a mix of transfer and cashCheque. Would we change unapprove to
tearCheque
? There's also that ambiguity of cheques adding up, where approvals more clearly override a previous one.In the use case described by Vitalik of contracts charging fees in subcurrencies, it could easily cost more to have to call approveOnce each time (if we replace the current approve method with it) than the actual fee in subcurrency. For that reason I think we should keep both
approve
andapproveOnce
, but we could add the_maxValue
argument to the former, that way subscriptions or fees could be taken in multiple calls but only up to a certain amount. Another reason to keep the approval terminology, as I think it's much simpler to describe approve and approveOnce than some createMultiCheque and createCheque. RegardingisApprovedFor
, it would have to return the approved amount if we do add_maxValue
, just asisApprovedOnceFor
does.ethers commentedon Nov 19, 2015
decimals()
doesn't seem needed. The Token itself represents an indivisible unit. A higher-level, like SubCurrency, should use Token, SubCurrency is wheredecimals()
or other things likesymbol()
could be implemented.ethers commentedon Nov 19, 2015
In all method descriptions, let's also remove "coin", eg "Get the total coin supply" -> "Get the total token supply"
frozeman commentedon Nov 19, 2015
I disagree, as the token is the currency or whatever and to represent it properly in any kind of dapp you need to know what is the proper way to represent that token. Ideally the user has to add only one contract address and the dapp can derive everything from there. Otherwise you make every dapp implement the low level token, plus some high level currency contract API. And not knowing the decimal points can be dangerous, otherwise one user sends 100.00 and another 100 (equals 1.00)
simondlr commentedon Nov 19, 2015
I'm neither here not there about the terminology. I think "approve" OR "cheque" terminology is good enough.
At the end of the day, it seems we need both blanket approvals & once-offs. Or rather, it seems it would be useful to specify 3 things: 1) Total value that can be withdrawn, 2) How many times they can do that, & 3) How much at a time.
Spitballing another option:
Just one method, called approve (or setCustodian) that takes 2 parameters. How many times they are allowed to withdraw & how much each time?
approve(address _for, uint256 _withdraws, uint256 _max)
?
@frozeman Regarding names & other information. I agree with @ethers here. There will be tokens minted that don't have any requirement for names, symbols or decimals. Like prediction market outcomes or energy meter kwh tokens for example. This should not be at the token layer. All tokens are not automatically a sub-currency or coin (that uses additional information).
ethers commentedon Nov 19, 2015
@frozeman @alexvandesande I think another EIP is needed for "TF Registries" https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs#tf-registries
frozeman commentedon Nov 19, 2015
Created it here #22
frozeman commentedon Nov 19, 2015
@simondlr @ethers i think divisibility belongs to the token contract, as even non currency token can need that. Im fine with putting the name and symbol in the registry tho.
ethers commentedon Nov 19, 2015
approve(address _address, uint256 _withdraws, uint256 _max)
seems quite clean (suggested by @simondlr).May tweak it as
approve(address _address, uint256 _withdrawals, uint256 _maxValue)
(
isApprovedFor
then checks if there's at least 1 withdrawal approved for the amount)EDIT: add address
frozeman commentedon Nov 19, 2015
You mean
function approve(address _address, uint256 _withdraws, uint256 _max)
simondlr commentedon Nov 19, 2015
@frozeman wrt decimals. Neither really here nor there about it. You can have multiple thresholds as is the case with Ether. The base token is still wei at the end of the day. You can't have decimal wei. You can write the wei in Ether, Finney, Shannon, etc. Each with their own decimal position. Should multiple thresholds be specified? Or only one? If so, is the token name specified to the base unit, or where the decimal point starts? ie Ether or Wei? It's a naming/usability convention, not a specification on how the token actually works.
Personally, for most new tokens that will be created, it doesn't really make sense to have them anymore. I do however see scenarios where it can be used. Thus, I'm neutral on this point. Don't mind. Perhaps it should be optional.
frozeman commentedon Nov 19, 2015
I should be optional sure, but for tokens which will be used in the wallet or user facing stuff, its quite necessary, except you don't want them to be divisible.
Concerning multiple steps, i think only one basic step is necessary from there on you can go in
/100
steps as usual, thats not hard to guess.But it surely makes a difference if a user accidentally sends 10000 vs 100.00 token units ;)
ethers commentedon Nov 19, 2015
No one wants many informational EIPs, but from a design perspective is it better to consider EIPs as composable and keep them modular and lean?
For example, an informational EIP for Wallet Tokens could be composed of EIP20, EIP22, EIPX?
Let's not forget https://gist.github.com/frozeman/090ae32041bcfe120824#gistcomment-1623513
As there's also been some discussion about the approve functionality, maybe approve APIs should also be its own EIP?
451 remaining items