ethereum / EIPs

The Ethereum Improvement Proposal repository

Home Page:https://eips.ethereum.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ERC: Token standard

frozeman opened this issue · comments

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:

recent discussion from https://gist.github.com/frozeman/090ae32041bcfe120824

@vbuterin said:
Yeah, createCheque and cashCheque as above, plus transferCheque(address _from, address _to, uint256 _value) sounds good. In that case, we should probably remove the _to argument from cashCheque; 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 in cashCheque 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:

function createCheque(address _for, uint256 _maxValue)
function cashCheque(address _from)
function getChequeValue(address _from, address _for)

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, like createCheque(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 and approveOnce, 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. Regarding isApprovedFor, it would have to return the approved amount if we do add _maxValue, just as isApprovedOnceFor does.

decimals() doesn't seem needed. The Token itself represents an indivisible unit. A higher-level, like SubCurrency, should use Token, SubCurrency is where decimals() or other things like symbol() could be implemented.

In all method descriptions, let's also remove "coin", eg "Get the total coin supply" -> "Get the total token supply"

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)

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).

Created it here #22

@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.

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

You mean function approve(address _address, uint256 _withdraws, uint256 _max)

@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.

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 /100steps as usual, thats not hard to guess.

But it surely makes a difference if a user accidentally sends 10000 vs 100.00 token units ;)

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?

I think its best, when somebody makes a change proposal he list nicely formatted exactly what he would change. In the same as above in the first comment, then these changes are easier to understand, and later move into the actual proposal. Simply:

function myFunc(address _address, ...) returns (bool)

etc.

Agreed @frozeman. wrt to this method.

function approve(address _address, uint256 _withdraws, uint256 _max) returns (bool success)

It's still slightly clunky as opposed to true or false (as you mentioned previously @niran). ie having to specify 2**100. We could add a helper function that replicates this internally? ie

function approveAll(address _address) returns (bool success)

it internally calls approve(_address, 2**100, 2**100)

Any other comments from others on this method?

Is there a use case for specifying the number of withdrawals? If we're sticking with the approval language, I think the function signatures we started with were fine. You're always granting access to a certain amount of funds, and I don't see a case for caring about the increments those funds are withdrawn in.

Display information about tokens, like where to put the decimal point when displaying the native integer value, belongs in a registry. Changing a token contract is hard. Changing a registry entry is easy. decimals wouldn't change how the token contract works, nor how any calling contract would work. It's not necessary, and the UI problem will still be solved.

@ethers decimals, name and symbol are important for displaying to the end user. If Ether was a token then the name would be "ether" with 8 decimal cases, and internally everything would be wei. Just like we do currently.

Regarding the approve/cheque discussion, I feel that we should always use focus on paving cow paths: implement what everyone is on absolute consensus as the basic "standard" and then allow real world usage dictate how to better define more advanced use cases.

@niran true... If you have a subscription, then you someone can anyway withdraw all 12 months in 12 transactions (for example). A future layer could perhaps limit based on timestamps when new withdraws can be made. But let's leave that for later.

I agree with @alexvandesande. Let's keep it simple.

So we thus we only have 1 approve function?

function approve(address _for, uint256 _value) returns (bool success)

address _for can withdraw any amount up to _value. If approve is called again, it is not reset, it is simply added to that amount.

The transfer is difficult in this scenario, since it doesn't follow the cheque tango. Would it simple mean transferring ALL outstanding value to another custodian?

Perhaps we should leave that out for now & "pave the cowpaths" (love this saying). Find the "desire paths". :)


I understand why decimals are useful. It just won't matter in a substantial subset of tokens (you won't see them in wallet really). Thus it should be optional.

Just struck me, it would be great to have some kind of standardized system to pay miners with tokens.

You could imagine an user who holds a wallet of various coins, but doesn't know what Ether is. Rather than having to purchase Ether to make a transfer, there could be a _sendToMiner option in each function which pays the miner a fee in that token.

@simondlr I like this minimal viable approval function and also agree we should see what people build with it before making too broad a standard API

I think tokens created from the wallets (like what @frozeman demo-ed at Devcon) can include decimals. Because it will be needed in that context (for example), which "paves the cowpaths". :)

decimals, name and symbol are important for displaying to the end user

Agree. Are we going to add them to EIP20, or leave them in EIP22?

I would add them to 20, but mark as optional

@simondlr i add your suggestion and also changed the order and names for isApprovedFor so it matches the function name:

function isApprovedFor(address _allowed, address _for) constant returns (bool success)

e.g. _allowed isApprovedFor _for

And this should probably return the still allowed value, e.g. if you already used up a part.
We should then probably rename this one too

^^ it's still ambiguous:

_allowed ( has allowed or is an approved holder for ) isApprovedFor _for

or

_allowed (is allowed or is an approved spender for ) isApprovedFor _for

Maybe holder and spender is more clear: function isApprovedFor(address _spender, address _holder) constant returns (bool _success)

Yes, I would say isApprovedFor should return uint256 value, not bool anymore. I like @nmushegian's suggestion of holder & spender.

For Gnosis it would make sense to add an optional identifier, to handle multiple tokens within one contract. In our use case each outcome for each event represents one type of token. Without an identifier, one contract has to be created for each outcome. A lot of token contracts replicating the same functionality would be written on chain. We think there are many use cases for contracts controlling multiple tokens. E.g. also Weifund has to create a new contract for each campaign. Knowing that there are many use cases for both, we should define a standard for both. Since Solidity allows method overloading we can still use the same function names:

function transfer(address _to, uint256 _value) returns (bool _success)

function transfer(address _to, uint256 _value, bytes32 _identifier) returns (bool _success)

What do you think?

@Georgi87 +1 we would need to do the same for balanceOf

You'll need to add identifiers for each function method. How are we going to work with optional, but useful standards? ie decimals, naming & identifiers? We should emphasise this as separate.

Lets discuss this more.

I changed isApprovedFor to allowance:

function allowance(address _address, address _spender) constant returns (uint256 remaining)

// Returns true if `_spender ` is allowed to direct debit from `_address`

And renamed every occourance to _spender to make all variables consistent.

I also renamed the event, please @ALL take a look

@frozeman +1 for allowance. Much more clear. Although description should be:

"Returns the remaining allowance that _spender is allowed to withdraw from _address"?

fixed, right?

fixed, right?

👍

I support the demand for an token identifier in the standard.
Lets think of tokens as tickets to a concert, m^3 freight in a ship, the right to consume an amount of energy at a specific place in a specific point of time.

In all these examples you have different tokens (different concert, different ships, different place) but other than that all others rules are the same and it makes a lot of sense to handle them in one contract. And very important - this is not only for gas saving reasons.

As a responsible DAPP creator that would allow other tokens to be used in the DAPP you want to check that the tokens do what they promise - it will not be enough that someone self registered the token at a registry. Therefore DAPPs would most likely have a whitelist of (token) contracts that have been reviewed manually.

Just for the pure fun I created a market on it: http://groupgnosis.com/dapp/#/markets/0x149b4a227ef92585d61211bd0a518b2e/0x1878ace41a67160c97a419b01f63b2b094d67ae54

If we had a good metric for the "success" of the token standard we could set of a futarchy. But I guess this is difficult to come up with - ether price is clearly not the right metric for such a minor decision.

We should avoid optional parts of EIPs as a design goal. They make it much harder to convey what an implementer has actually implemented. If we separate optional components into separate EIPs, some tokens will claim to support EIP N, while others will say they support EIP N and EIP N+1. Everything becomes much clearer.

I still don't understand the opposition to using registries for names and decimal places. Separating this cosmetic data from the token itself allows dapps to make their own decisions about what data about the token is required to display in their interface. It also lets dapps add new requirements we didn't think of in the future. A token can specify an admin who can set registry entries on its behalf, ideally by forwarding transactions through the token contract so registries can blindly allow entries to be set for msg.sender.

Consider the Bitcoin community's desire to switch to "bits" as the common denomination. With decimals baked into the token contract, this is impossible without just ignoring what the token contract says. With decimals in registries, the admin can update the name of the BitcoinToken unit to "bits" and the decimal from 8 to 2. If people don't like admin control of this, they will use registries with different access control policies.

We should let this problem solve itself by keeping it out of the token contracts and letting dapp authors do whatever they want, starting with an admin-controlled token registry. TokenContract.decimals will be ignored by clients eventually. It is inevitable. We shouldn't include it here.

Identifiers should make it in this EIP so contracts that consume dynamic tokens don't have to implement separate code paths to implement a separate identifier EIP. They should not be optional: either all calls should have identifiers or we should create an identifier EIP. But more importantly, we should wrap this thing up. I don't think it's the end of the world if identifiers don't make it in.

I'm in favor of leaving multi-token controllers out of this proposal. I think registries and multi-asset controllers are fundamentally related and can be tackled simultaneously once we see some real use cases.

once we see some real use cases.

Gnosis is already real and Augur soon will be. Those tokens from this markets can be used for all kind of APPs since they are basically a payment that will be done if a specific event happens. In principal you could do a crowdfunding campaign with "Hillary Clinton Tokens" from a prediction market. This would mean: you contribute to this campaign but only under the condition that Hillary gets elected.

I am 100% sure there are more meaningful uses of prediction market tokens.

I don't think it's the end of the world if identifiers don't make it in.

Of course not. But why defining a standard that from the beginning excludes one of the very few already existing DAPPs.

@koeppelmann +1
If we know a use case already we should try to find a standard for it. Not every app has to implement the full standard anyways. Apps implementing the identifier can still work with tokens without the identifier. Our crowdfunding contract allwos to use tokens with identifier but still works with tokens without identifier without additional code, because Solidity ignores additional function arguments.

I agree with the points @niran articulated:
#20 (comment)

Since there are several things being discussed (eg what should be optional, should there be separate EIPs, adding identifiers), does this type of "poll" help to clarify where "consensus" might be?
https://github.com/ethereum/wiki/wiki/Poll-for-token-proposal-EIP-20

Our crowdfunding contract allwos to use tokens with identifier but still works with tokens without identifier without additional code, because Solidity ignores additional function arguments.

The function signatures would be different so it wouldn't work, you need to implement the functions directly

The function signatures would be different so it wouldn't work, you need to implement the functions directly

If you have a token contract implementing the token interface without identifier and another contract is using this token contract with identifier, the identifier will be ignored by the token contract.

@Georgi87 No, it will route to a different function signature:

import 'dapple/test.sol';

contract WithIdentifier {
    bool public transferred;
    bool public missed;
    function transfer( address to, uint amount, bytes32 id ) returns (bool ok) {
        transferred = true;
        return true;
    }
    function() {
        missed = true;
    }
}

contract NoIdentifier {
    function transfer( address to, uint amount ) returns (bool ok);
}


contract SignatureBehaviorTest is Test {
    WithIdentifier wid;
    function setUp() {
        wid = new WithIdentifier();
    }
    function testHypothesis() {
        NoIdentifier(wid).transfer( address(this), 100 );
        assertTrue( wid.transferred() );
    }
    function testReality() {
        NoIdentifier(wid).transfer(address(this), 100);
        assertTrue( wid.missed() );
        assertFalse( wid.transferred() );
        wid.transfer( address(this), 100, "identifier" );
        assertTrue( wid.transferred() );
    }
}
Testing SignatureBehaviorTest
  testHypothesis
    LOG: assertTrue was false
    Fail!
  testReality

Thank you for posting this test @nmushegian. There was an error in my test case.

@Georgi87 I submitted the behavior you were expecting as a feature request: ethereum/solidity#240

@Georgi87
function transfer(address _to, uint256 _value) returns (bool _success) function transfer(address _to, uint256 _value, bytes32 _identifier) returns (bool _success)

It'd also be great if it were implemented for transferFrom too

If we know a use case already we should try to find a standard for it.

At Augur we have a use case here: https://www.reddit.com/r/ethereum/comments/3peeyp/subcurrency_functions/

Of course, you can do it without a standard (although it's a bit convoluted) as proposed here: https://www.reddit.com/r/ethereum/comments/3peeyp/subcurrency_functions/cw6vcrr

I am 100% sure there are more meaningful uses of prediction market tokens.

Absolutely. Imagine being able to use shares in one market as the currency in another (of course, you can do this, or pretty much any of this stuff in a convoluted manner, but a standard would allow for a much simpler implementation).

+1 to adding that identifier to the standard. It's an incredibly simple addition yet would be quite beneficial for us at Augur, as well as, it sounds like, Gnosis. I imagine there are other dapps that haven't been invented yet that would find this useful as well. i.e. I suspect we're just the first two to have run into wanting this :)

I'm neutral on identifiers, perhaps leaning slightly more to the side of NOT including them in this standard.

I'd like to give some reasons against identifiers:

  1. It's premature optimisation. The current gas limit is indeed a visible issue wrt this, especially in prediction market scenarios where you would want to create multiple token types. And it would not then fit into the current gas limit. You could save significant gas costs by 1) using libraries & 2) using a proxy per token type (that follows non-identifier standards). It's not cheaper than identifiers. But should be cheaper than creating a contract per token. In the future with scaling & potentially cheaper gas costs, it would make sense again to separate concerns neatly & have a contract per token without sacrificing too much. @vbuterin can add on this, if this is reasonable.

  2. What's the default if there's only one token per contract? If token.transfer(to, value) is called without identifier, does it then forward it internally to token.transfer(to, 0, value)? Where '0' is the default identifier? Should it perhaps be the contract address? Should non-identifier calls even be possible? Should other dapps know what address & identifier to call? Why should one token contracts "pay extra" for this? Which leads onto the next point:

  3. If we are talking about saving gas costs: for tokens that are supposed to last indefinitely (company shares, personal coins, etc), then over time, if identifiers are the standard, it will cost more even if the token is referenced by an identifier. An extra few amounts of gas will add up over time. This cost will be shoved to the users.

  1. using libraries & 2) using a proxy per token type

We have already shown that it is not possible to save gas significantly using libraries in this scenario, because a wrapper function has to be added for each token function in the library. One contract per token type has to be added or am I missing something? Solidity may add features in the future to reduce gas costs further though.

I am now in favor of using one signature: token.transfer(to, 0, value) with default identifier 0. I would put the identifier at the last position. Maybe solidity will offer default parameters in the future and token contracts not using the identifier could just omit it.

How much gas does a lookup in a hashmap actually cost? I guess it is super cheap.

How can we come to a conclusion? Should we do a voting?

How much gas does a lookup in a hashmap actually cost?

50 gas or at current ether prices ~$2.415e-06

I'm leaning against multi-asset. If you have a multi-asset controller, and you want its tokens to appear in a simple wallet, you can make it into a factory that will generate a controller contract per symbol on demand.

Anyone who wants identifiers, want to draft the EIP (proposal for Multi-Token Standard) on it? By seeing it, it may influence / lead to whether identifiers should be included in this EIP20.

Here's a potential middle ground. We encourage token authors to code to this standard without identifiers. We encourage client contract authors to code to a forthcoming token system EIP that includes identifiers. Simple tokens can be used via the token system API by using a token system proxy that looks like this sketch:

contract ProxyTokenSystem is TokenSystem {// Copy the approval logic from StandardToken, but store approvals by token
  // address as well. Dapps approve TokenIdentifierProxy for unlimited amounts
  // for the tokens that are used, so the proxy adds its own access control
  // for transferFrom.
  mapping (address => mapping (address => mapping (address => uint256))) approved;event Approval(address indexed _tokenAddress, address indexed _address, address indexed _spender, uint256 _value);

  function transfer(address _to, uint256 _value, uint256 _tokenAddressInt) returns (bool _success) {
    TokenContract Token = TokenContract(address(_tokenAddressInt));
    Token.transferFrom(msg.sender, _to, _value);
  }function transferFrom(address _from, address _to, uint256 _value, uint256 _tokenAddressInt) returns (bool _success) {
    address _tokenAddress = address(_tokenAddressInt);
    // Take the body of StandardToken.transferFrom, but add _tokenAddress as the
    // first key to look up in each mapping.
    ...
  }function approve(address _spender, uint256 _value, uint256 _tokenAddressInt) returns (bool success) {
    address _tokenAddress = address(_tokenAddressInt);
    // Take the body of StandardToken.approve, but add _tokenAddress as the
    // first key to look up in each mapping.
    approved[_tokenAddress][msg.sender][_address] = _value;
    Approval(_tokenAddress, msg.sender, _address, _value);
    return true;
  }// allowance, unapprove, balanceOf, and totalSupply should be straightforward.}contract FakeEtherEx {mapping (address => mapping (uint256 => mapping (address => uint256))) balances;// Before calling deposit, the dapp client will send a transaction approving
  // FakeEtherEx to transfer from the token system. If the token system is the
  // proxy, it will send another transaction to approve ProxyTokenSystem for
  // unlimited amounts of the underlying token if the user has never done so.
  // Alternatively, new simple tokens can whitelist the ProxyTokenSystem after
  // it has been vetted and published to the chain. The dapp can then deposit
  // successfully.
  function deposit(uint256 _value, address _tokenSystemAddress, uint256 _tokenIdentifier) {
    TokenSystemContract TokenSystem = TokenSystem(_tokenSystemAddress);
    TokenSystem.transferFrom(msg.sender, address(this), _value, _tokenIdentifier);
    balances[_tokenSystemAddress][_tokenIdentifier][msg.sender] += _value;
  }
}

Tokens within systems would be addressable by (system address, identifier). Tokens outside of systems would be addressable by (proxy system address, token address). Client contracts would only need to care about one universal API. Token authors would only need to care about one simple API.

This only has a benefit if we succeed in convincing client contracts to use the token system API, not this API. If there's consensus on this, we can bake this strong recommendation into both EIPs. We'll also want to recommend that token authors whitelist the proxy contract once it's published so users don't have to pay to approve the proxy contract.

@nmushegian i agree here. The problem with the multiple tokens is that any wallet etc. will anyway have a hard time displaying these tokens, as it would need to know which tokens exist in this contract (identifiers), so this would create a new problem. Do we put those then in the registry or not, then the registry has to adopt as well.

I think adding a proxy contract layer for this type of custom multi token contracts should work fine, and then any wallet only need to add the proxy contract (where you also can clearly define which token from the market to use) @Georgi87 is that ok?

If we support both standards, then it means that we have overhead in dapps (requiring most developer to write code to support both).

A work-around is to have proxies on either sides. Either from simple token to multi-token (or vice versa). @niran's proposal is such that the dapp authors only write with identifiers in mind, but then have a proxy (one proxy router for ALL). This does have a small overhead/trade-off. Requiring simple tokens to approve the proxy. And then the proxy should be added to dapps (not the actual token contract) (along with keeping both proxy & token address saved).

My gut feel is that we should just have 2 separate standards, and let token creators canvas dapp creators to add them. If wallets & exchanges don't want to add multi-token support, then there will be others who swoop in to add it. If they don't, then for people like Gnosis & Augur, whose tokens (usually) have a limited shelf-life, they can (hopefully) switch out to another system in the future (pending scaling changes).

Currently, however, Gnosis & Augur WILL be creating plenty of tokens and currently have a visible problem wrt gas limits. Using identifiers is pretty much required. Using proxies to interface with the rest of the ecosystem will cost more, which is a trade-off.

There's plenty of trade-offs to consider here.

  1. Usability from a user's perspective.
  2. Cost to developers.
  3. Actual network constraints.
  4. Future proofing the design.

What's the most important considerations. Ultimately, we should cater to number 1. As developers we should probably cover costs for proxies in the face of network constraints.

Having an exchange or wallet where a user needs to also specify an identifier OR requiring a user to themselves authorise a proxy ("What's a proxy?") is worse UX.

Thoughts?

I think two standards is the way to go. I want the API's to be simple and clean. All this proxy stuff sounds already way to complicated, for both the developer and users.

I may as well put in my two $TOKEN_UNITS in, re: approve/cheques/allowance.

It's possible to atomically transfer tokens with a wallet contract.

  1. The DApp-to-be-paid has two address, a collection address and a holding address.
  2. The wallet contract transfers the token to the collection address
  3. The DApp-to-be-paid moves the token from the collection address to the holding address, and then does its stuff.

Should something break along the line, the whole transaction is reverted, and there isn't a cheque lying around needing to be canceled. Of course, this does require wallet contracts and two-address systems, which are not so simple.

I do think cheques as distinct, transferable (sub)tokens have their advantage with more complex tokens. Say something like Freicoin, and the coins only decrease after the cheque is cashed.

EDIT: And you could use the exact same system above by having an approval last only for the transaction. You still need a wallet contract, though.

I do think cheques as distinct, transferable (sub)tokens have their advantage with more complex tokens. Say something like Freicoin, and the coins only decrease after the cheque is cashed.

EDIT: And you could use the exact same system above by having an approval last only for the transaction. You still need a wallet contract, though.

These two use cases are what led to work with 'withdrawal'-based delegation instead of approval-based. In this system any contract can withdrawn "to" the transaction, and any unused funds an the end of the transaction are (virtually) refunded to "the session initiatior". I think cheques achieve the same goals in a cleaner way.

Cheques and multi-token controllers both can be implemented as "standard extensions", they could work seamlessly with simpler controller contracts, especially if you keep your data separated or have a admin action "backdoor"

Having an exchange or wallet where a user needs to also specify an identifier OR requiring a user to themselves authorise a proxy ("What's a proxy?") is worse UX.

What's a token address? No sane UI will expose these details to a user when you can just search a token registry by name or check a subset of all tokens for balances to display. Similarly, the proxy approval is either unnecessary because the token author followed our advice and whitelisted it, invisible because the user is using a normal browser with local or remote RPC, or par for the course in Mist and Metamask since every transaction must be approved and users won't understand most of them.

Coding to the TokenSystem API doesn't make anything harder for dapp developers. Finding identifiers isn't a problem because they will use a token system registry that includes identifiers. The only changes from the status quo are treating token system registries as the primary registries instead of token registries, adding an extra argument to the call, and sending an approval transaction if the proxy doesn't have an allowance for the token in use. This is not difficult.

Coding to the Token API makes things harder for token system developers by forcing them to find a way to publish token contracts for each of their tokens. This is actually difficult! None of the other problems we've discussed are.

Anyway, I think steering dapp developers to the TokenSystem API while allowing token developers to use this ERC's Token API is the better approach, but there are suboptimal workarounds if we tell everyone to code to the Token API instead. The functionality will be the same at the end of the day.

I think the only other issue left was decimals. Let's remove that in favor of registries and move forward with this.

commented

I vote for skipping decimals in this low-level standard. Decimals are a higher-level abstraction IMO.

@christianlundkvist agreed, the need for decimals shows itself either when you are creating a GUI or when you are dealing with prices, in both cases you have to define a registry since you have more than one token anyway.

I've filled your name in at the poll:
https://github.com/ethereum/wiki/wiki/Poll-for-token-proposal-EIP-20

If I got your "position" wrong, feel free to update ;)

small naming improvement: in allowance context, change (address, spender) to (owner, spender)

Just to be clear, is the argument to Approvel supposed to be the latest total allowance? Not individual approval calls, which would require you to scan events to determine the latest approval, and doesn't have anything you could use for unapprove

@nmushegian: for approve, are you asking about the value argument? it's not the latest total allowance, it gets added to any existing allowance:
"If this function is called twice it would add value which _spender is approved for."
I see something less clear about unapprove as you mention...

@frozeman: for unapprove I think this part should be deleted "Must reset all approved values." since "all approved values" is not as clear.

@ethers No, I'm asking about the Approval event. When you add value to the approved amount via approve, do you emit Approval(value) or Approval(allowance(...)) ?

You would emit the current added value, as you got before Approval events for the previous allowances. So you can scan the history to see all the approval events.

We could add a _value to this method:

function unapprove(address _spender) returns (bool success)

But this would then be tedious to track whats is still the left allowance and could end up being unclear. E.g. spender has still 132 token units left, but you unapprove then for 200, will it go negative :)

There is thinks its better to leave it as is.

Yeah @frozeman. I'm wondering there are scenarios where you would want to only remove increments? Either you could just unapprove and re-approve for the correct amount?

@niran Do you mean that a token author explicitly, hardcodes a proxy so that individual accounts don't have to approve it?

@nmushegian: got you, for some reason was reading what you wrote as Approve1 instead of Approval.

@frozeman: for Approval event, I suggest we should update the documentation to make it clearer, something like: "Triggered when _owner approves _spender to direct debit an additional _value from its account." Also, I think "withdraw" is simpler terminology than "direct debit".
Another suggestion is: "Triggered whenever an _owner calls approve(address _spender, uint256 _value)."
For unapprove I see the description was updated to "This will disallow all before approved values reset." That's even more unclear. Suggestion: "This will reset the allowance of _spender to zero."

I agree we don't want unapprove to get more complicated by adding a _value.

I updated according to you suggestions

@simondlr Yes. It's equivalent to telling token authors to all use the TokenSystem API, but by approving the deployed proxy and considering it part of their codebase, they don't have to.

@frozeman then how do you detect an Approval(0) emitted by an unapprove() vs an approve(0)? You either need to add a new event, change the semantics, or assume you can track "events" by function signature...

If the Approval event emitted the last total approved value you can infer all you need. It might be named LatesetAllowance.

@nmushegian good point!

Currently, would vote for your option. LatestAllowance(address indexed _owner, address indexed _spender, uint256 _totalAllowed).

Only potential issue is that you need to go back and find the previous event to figure out how much it increased. If your dapp does this call you'll have that info around. But if a tx not in your control does it, your dapp will need to go back and look when the previous event was. Another option is to when the event is fired, get/keep the allowance around just from before it was fired. Not entirely sure how resource expensive these options are.

Perhaps there's another option?

I suggest keep Approval and add new Unapproval event, which is more clear and simple mapping to functions.

+1: keep Approval and add new Unapproval event

We really don't need an Unapproval event, it is simply an Approval event of 0. And the Approval event should emit the value of the approve call, not the new total approved value; that's what allowance returns.

@caktux Then we are missing an event to detect when allowance changes to 0 - the point is to avoid having to call allowance() each block. See discussion above.

@nmushegian Read the discussion twice and thought it through before answering, thank you. Maybe you should have done the same with my reply. But let me explain it more clearly. There would never be a need to call approve(0) in the first place; a dApp would only be wasting a transaction (or gas if part of another call) doing so. Therefore an Approval event of 0 is an unapproval. Remember that events are logs, not return values themselves, and a dApp should not base important actions on the values of events (think of chain reorgs) but rather use them to list previous actions or calls, and to trigger new calls to validate results and take further actions if needed. Back to the point, you would not have to call allowance each block, but only after detecting an Approval event. And it needs to return the approved value (not the new total) for the sake of granularity and to prevent the issue described by @simondlr. Calling allowance after detecting the event (and again, obviously not at every block) is both cheap and the logical way to get the newly allowed total or to confirm an unapproval.

There would never be a need to call approve(0) in the first place; a dApp would only be wasting a transaction (or gas if part of another call) doing so.

Makes sense. But could approve(_spender, 0) be used by a malicious dApp E to confuse another dApp F, that _spender has been unapproved?
Seems things are fine as long as dApps such as F follow best practices as mentioned in these important points:
"Remember that events are logs, not return values themselves, and a dApp should not base important actions on the values of events"
"Calling allowance after detecting the event", before taking an action, is a requirement and not optional.

We shouldn't add events unnecessarily and bloat the standard because of possible dApp Fs; it wouldn't be doing them a favor either to make it easier to use event values directly and implying it's the correct way to use events when it's not. "Calling allowance after detecting the event" would only be required if taking any further action, basic good practice, but not if you're only listing past approvals, in which case you'd watch or filter on past events (including the one from that pesky approve(0)) and call allowance only once to show the latest total.

I thought the point of logging was you can take a minimal set of logs plus proof they came from the chain, and rebuild your dapp's off-chain state. You're saying I need to rescan the chain any time I rebuild any off-chain databases.

but not if you're only listing past approvals, in which case you'd watch or filter on past events

But not if you want to list past allowances or actually list past approvals unambiguously, in which case you have to get the whole history and do a scan from one end or the other.

What are events supposed to be for then and why do we need an approval event at all? Are the use cases just "get alerts when you need to call the chain to update an approval value" and "list past approval-like events"?

use event values directly

This is incidental, probably the ideal solution would be for the event to record the diff, not the value. Too bad we've used up all the bits in the value arg. Anybody think adding bool is_negative to Approval would be less offensive than adding a separate event?

Therefore an Approval event of 0 is an unapproval.

I feel this is still too ambiguous. All token implementations must then put in functionality to disallow approvals of zero in the approve() function. Wouldn't these extra checks not cost more in gas than just having a different event?

I'd still vote for keeping approve as it is currently: only the recent approval (not the total) and have a separate event for `event Unapproval(address indexed _owner, address indexed _spender)``.

Can we take a short detour to address the VM error check issue: Functions for which 0 is a valid return value should contain an extra bool _success argument, or there needs to be a separate discussion about a better convention for handling these edge cases.

function balanceOf(address _who) returns (uint _balance, bool _success);
function allowance(address _owner, address _spender) returns (uint _allowance, bool _success);

This became a high priority for me again because I learned we can't implement the pattern we like (throw vs extra success argument) and then retrofit the more common one because Solidity won't let you define both:
ethereum/solidity#259

@nmushegian there are several other ways to distinguish functions apart from their return value types. You can e.g. change the name to indicate whether it will throw or not and you can also add another parameter (bool _throwOnError).

And by the way, function overload resolution does not only apply to Solidity, but to any framework that uses the ABI. This includes serpent but also web3.js: Currently, web3.js does not even support function overloads in general.

Suggestion: should we move from uint256 to int256? There are some occasions that a token might want to allow the creation of a debt and I don't see why that should be prevented in the standard. The only downside is that the maximum balance allowed will be halved, but considering the balance allowed by 256 bits is measured in the uncountable quattuorvigintillions, I doubt it would be a problem.

@frozeman last event is missing a "t". :)

commented

Thank you all for your discussion I usually just watch/study from a distance, forgive the last minute interjection, but I feel as though it is time to speak up on a particular issue being over looked.

Question: Why is decimal / percentage representation a lower order priority?

In my humble opininon this is one of the important use cases partaining to token manipulation...some resulting personal paralysis as to how to implement in that area...

The Base Unit specification is FAR from clear and gives room to isometric misrepresentation between clients and contract, not to talk of dfferent incoherent client implementations that might not follow standards ...

What if I have a base unit of 3 of total units of 100? how is the fraction represented in contract? 33.3333 does it mean that I am now to normalize the representation to allow users send fractions of 33.3333 ? How would you represent more granular decimals in the contract as a result of user to user interactions?

Maybe I am confused, in anycase I am just asking about the state of consensus on this particular issue that seems overlooked, someone please shed light about the direction / conclusion on this before the gavel comes down. Thanks for all your hard work!

Hey @innovator256. It's because not all token systems will deal with decimals. Even if it was, it is still being decided where registry information like this should live (which will probably be discussed in a future EIP).

commented

@simondlr Thank you I understand, not all require decimals, but a vast majority most probably will even if its not apparent right now...So is the verdict that decimal is a client display issue only? How do contracts deal with fractions from the client since there are no floating point numbers yet?

@innovator256 It could be in client display only, in the contract itself, or in a registry. It's just not ubiquitous enough that it warrants to be in the low-level standard. :)

How do contracts deal with fractions from the client since there are no floating point numbers yet?

There are some work-arounds, like having a multiplier and then dividing. Your granularity is based on how high you want the multiplier to be. Clients then have to know how to interpret it as well (knowing what the multiplier is). This is a basic hack I used in Ujo's pricefeed (for example).

A token contract should never be provided a fractional value. Tokens aren't infinitely divisible. Their contracts represent the smallest unit of the token. A dollar contract would track cents. A Bitcoin contract would track satoshis. An Ether contract would track wei. Fractional cents, satoshis and wei don't exist. Clients can use fractional cents as an accounting fiction that they resolve somehow, but the contracts only know about whole units.

commented

@simondlr Got ya, I guess that works, thought about similar scheme just didnt know if there was some acceptable official standard we should be aiming for or just got confused. Either way thanks, Ujo's very cool by the way!

commented

@niran true by today standards, but by definition contracts can be anything you define them to be, they are limited right now due to design decisions in terms of whole units, this may very well change in the future...

What if I have a base unit of 3 of total units of 100? how is the fraction represented in contract? 33.3333

You're saying "if I use a token with low resolution it will show its discrete behavior in common scenarios". Ethers have finite granularity too and somehow we talk about 1/3rd of an ETH, 0.9 USD/ETH, etc.

Concerning the Approval(0) event.
I'm personally against it because:

  1. The main point of an API is to give and understandable interface, ideally so intuitively that you don't even need to read the docs to start using it
  2. The Approval event is fired with the current amount which was approved, not the total amount. If one want to know this he can either gather all previous Approval logs, or simple call function allowance().
  3. Logs are in general only for dapps, so they can take actions, but ideally a dapp reads from the state and only uses these logs as triggers. They aren't absolutely necessary for the token to work, nor for other contracts to interact with them.

As much as i wanted to have a community discussion, it quite drags and sometimes doesn't get more efficient or smarter...

APIs need to be guessable and understandable straight away. We sure can make things smart and more efficient, but this is mostly in the cost of understandability. Which ends ups in repeated frustration of newcomers and repeated explanation of why we did it this odd way..
And thats why the main point of an API in my opinion is simplicity and intuitivity.

Note: i changed the name from Approval to Approved and added the Unapproved event.
This should make it a bit more clear.

@simondlr what you mean with missuing "t"?

+1. Agreed @frozeman.

wrt missing "t". Just a typo:

event Approved(address indexed _owner, address indexed _spender, uin256 _value)

should be (see uint256):

event Approved(address indexed _owner, address indexed _spender, uint256 _value)

Approved and Unapproved sound great and take care of any ambiguity. The important part is to make it clear that Approved fires with the amount passed to approve() and not the total.