ethereum / EIPs

The Ethereum Improvement Proposal repository

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ERC223 token standard

Dexaran opened this issue · comments

The below is an old draft of the proposal. The up-to-date proposal can be found at https://eips.ethereum.org/EIPS/eip-223

Discussion should occur at https://ethereum-magicians.org/t/erc-223-token-standard/12894


ERC: 223
Title: Token standard
Author: Dexaran, dexaran@ethereumclassic.org
Status: Draft
Type: ERC
Created: 5-03.2017
Reference implementation: https://github.com/Dexaran/ERC223-token-standard


Abstract

The following describes standard functions a token contract and contract working with specified token can implement to prevent accidentally sends of tokens to contracts and make token transactions behave like ether transactions.

Motivation

Here is a description of the ERC20 token standard problem that is solved by ERC223:

ERC20 token standard is leading to money losses for end users. The main problem is lack of possibility to handle incoming ERC20 transactions, that were performed via transfer function of ERC20 token.

If you send 100 ETH to a contract that is not intended to work with Ether, then it will reject a transaction and nothing bad will happen. If you will send 100 ERC20 tokens to a contract that is not intended to work with ERC20 tokens, then it will not reject tokens because it cant recognize an incoming transaction. As the result, your tokens will get stuck at the contracts balance.

How much ERC20 tokens are currently lost (27 Dec, 2017):

  1. QTUM, $1,204,273 lost. watch on Etherscan

  2. EOS, $1,015,131 lost. watch on Etherscan

  3. GNT, $249,627 lost. watch on Etherscan

  4. STORJ, $217,477 lost. watch on Etherscan

  5. Tronix , $201,232 lost. watch on Etherscan

  6. DGD, $151,826 lost. watch on Etherscan

  7. OMG, $149,941 lost. watch on Etherscan

NOTE: These are only 8 token contracts that I know. Each Ethereum contract is a potential token trap for ERC20 tokens, thus, there are much more losses than I showed at this example.

Another disadvantages of ERC20 that ERC223 will solve:

  1. Lack of transfer handling possibility.
  2. Loss of tokens.
  3. Token-transactions should match Ethereum ideology of uniformity. When a user wants to transfer tokens, he should always call transfer. It doesn't matter if the user is depositing to a contract or sending to an externally owned account.

Those will allow contracts to handle incoming token transactions and prevent accidentally sent tokens from being accepted by contracts (and stuck at contract's balance).

For example decentralized exchange will no more need to require users to call approve then call deposit (which is internally calling transferFrom to withdraw approved tokens). Token transaction will automatically be handled at the exchange contract.

The most important here is a call of tokenReceived when performing a transaction to a contract.

Specification

Token
Contracts that works with tokens

Methods

NOTE: An important point is that contract developers must implement tokenReceived if they want their contracts to work with the specified tokens.

If the receiver does not implement the tokenReceived function, consider the contract is not designed to work with tokens, then the transaction must fail and no tokens will be transferred. An analogy with an Ether transaction that is failing when trying to send Ether to a contract that did not implement function() payable.

totalSupply

function totalSupply() constant returns (uint256 totalSupply)

Get the total token supply

name

function name() constant returns (string _name)

Get the name of token

symbol

function symbol() constant returns (bytes32 _symbol)

Get the symbol of token

decimals

function decimals() constant returns (uint8 _decimals)

Get decimals of token

standard

function standard() constant returns (string _standard)

Get the standard of token contract. For some services it is important to know how to treat this particular token. If token supports ERC223 standard then it must explicitly tell that it does.

This function MUST return "erc223" for this token standard. If no "standard()" function is implemented in the contract then the contract must be considered to be ERC20.

balanceOf

function balanceOf(address _owner) constant returns (uint256 balance)

Get the account balance of another account with address _owner

transfer(address, uint)

function transfer(address _to, uint _value) returns (bool)

Needed due to backwards compatibility reasons because of ERC20 transfer function doesn't have bytes parameter. This function must transfer tokens and invoke the function tokenReceived(address, uint256, bytes calldata) in _to, if _to is a contract. If the tokenReceived function is not implemented in _to (receiver contract), then the transaction must fail and the transfer of tokens should be reverted.

transfer(address, uint, bytes)

function transfer(address _to, uint _value, bytes calldata _data) returns (bool)

function that is always called when someone wants to transfer tokens.
This function must transfer tokens and invoke the function tokenReceived (address, uint256, bytes) in _to, if _to is a contract. If the tokenReceived function is not implemented in _to (receiver contract), then the transaction must fail and the transfer of tokens should not occur.
If _to is an externally owned address, then the transaction must be sent without trying to execute tokenReceived in _to.
_data can be attached to this token transaction and it will stay in blockchain forever (requires more gas). _data can be empty.

NOTE: The recommended way to check whether the _to is a contract or an address is to assemble the code of _to. If there is no code in _to, then this is an externally owned address, otherwise it's a contract.

Events

Transfer

event Transfer(address indexed _from, address indexed _to, uint256 _value)

Triggered when tokens are transferred. Compatible with ERC20 Transfer event.

TransferData

event TransferData(bytes _data)

Triggered when tokens are transferred and logs transaction metadata. This is implemented as a separate event to keep Transfer(address, address, uint256) ERC20-compatible.

Contract to work with tokens

function tokenReceived(address _from, uint _value, bytes calldata _data)

A function for handling token transfers, which is called from the token contract, when a token holder sends tokens. _from is the address of the sender of the token, _value is the amount of incoming tokens, and _data is attached data similar to msg.data of Ether transactions. It works by analogy with the fallback function of Ether transactions and returns nothing.

NOTE: since solidity version 0.6.0+ there is a new reveive() function to handle plain Ether transfers - therefore the function tokenFallback was renamed to tokenReceived to keep the token behavior more intuitive and compatible with Ether behavior.

NOTE: msg.sender will be a token-contract inside the tokenReceived function. It may be important to filter which tokens are sent (by token-contract address). The token sender (the person who initiated the token transaction) will be _from inside the tokenReceived function.

IMPORTANT: This function must be named tokenReceived and take parameters address, uint256, bytes to match the function signature 0xc0ee0b8a.

Recommended implementation

This is highly recommended implementation of ERC 223 token: https://github.com/Dexaran/ERC223-token-standard/tree/development/token/ERC223

Have you considered allowing this feature set by extending ERC20 with approveAndCall? Inside the "charging" contract you'll have one function call in any case (in this case transferFrom), but on the caller side entering is now an atomic operation, similar to transferToContract

The main goals of my proposal were:

  1. Prevent accidentally sent tokens from being held by contract.
  2. Allow users to deposit their tokens in contract the way simple as ether by a single transaction (i.e. transferToContract( ) call) with no need to call approval than call transferFrom.
  3. Make token transactions behave same as Ether transactions.

approveAndCall assumes that target contract will call transferFrom and its not like Ether transactions do. There is no need to allow target contract to withdraw tokens from someone (tx initiator). There is no need to fire Approval event also. From a logical point of view, we should just notify target contract that transaction appears. Also fire Transfer event with no approvals.

Updated my ERC23 token code with a function assembling receiver address to ensure token contract if the receiver is a contract or an address.

Ok I think I see where you are coming from...

What about, devs generally seem to want to replace the "execute on transfer" for ETH with a hard coded token interface with no execute on transfer, potentially just ERC20?

Is this a bad side effect or a naturally good thing? Doesn't exec-on-transfer make the simplest use cases more complex and maybe more dangerous, while not allowing anything really new?

exec-on-transfer make the simplest use cases more complex and maybe more dangerous

for contract developers you mean. From users point of view we just need to call 'transfer token' on MEW or transfer directly in contract and dont care any more about what is going on instead of calling approval then calling deposit or something like this with a chance of mistake that will cause a loss of tokens.

for contract developers you mean. From users point of view we just need to call 'transfer token' on MEW or transfer directly in contract and dont care any more about what is going on instead of calling approval then calling deposit or something like this with a chance of mistake that will cause a loss of tokens.

Contract devs are not being unsympathetic to user experience by nitpicking the semantics of contract code... I understand that approve + doSomething is not optimal UX, but these are things you can abstract away at the user interface level, and this abstraction honestly has very few side effects, while making composing contracts more safe ("what stuff could happen if I transfer this token here?")

(personally I think the real base abstraction is a binary per-address approve but that is another side thread)

Well designed token contract assumes you need to trust only contract and ethereum EVM. UI level abstraction assumes you need to trust UI developers. It also requires some things to be done by UI devs. I dont see any abstraction that is required already done. So what reason is to make a lot of requirements and dependencies between contract developers,UI developers and users when there is a way to avoid it.
At the other hand the main problem of every cryptocurrency is network bandwidth right now. Transferring of ERC20 token to the contract is a couple of two different transactions in fact. While transferring ERC23 token to a contract is a single transaction.
ERC20 transfer to contract also fires Approval event then fires Transfer event. Such irrational use of blockchain can cause extra bloating. ERC23 transfer fires only Transfer event.

We were thinking about doing a similar proposal from Aragon while working on economic abstraction for companies. We finally decided approve and transferFrom was a simpler interface and more secure.

Simpler and more secure? What reasons do you have thinking so? I named my reasons.
Easier usage. Better optimization. Less requirements.

In the current implementation, if a contract doesn't implement the receiver protocol, the transfer of tokens to an address that happens to be a contract will throw https://github.com/Dexaran/ERC23-tokens/blob/master/ERC23_token.sol#L56

Also I see the problem that the receiver needs to keep a list of what tokens it supports.

Also I didn't know about approveAndCall, but it seems the way to go for me.

My two cents, I would be happy to see approveAndCall be part of the standard, but your current solution, while cool, I think it would bring too much overhead to an already very simple and versatile protocol.

There could be a standard to approveAndCall data payload that calls the similar to your so called fallback in your proposal, and the this fallback doing some delegatecall magic could actually call a function in the contract passing the sender and the value as function params. In the fallback function you could do your token accounting and then call whatever function the caller wanted.

Sorry for the ramblings, I think I will actually come up with a parallel proposal for this.

In the current implementation, if a contract doesn't implement the receiver protocol, the transfer of tokens to an address that happens to be a contract will throw

As I said earlier it's done to prevent accidentally transactions of tokens to contract address where tokens will not be accessible any more.
Contracts are throwing accidentally ether transactions if no fallback payable function is implemented. The same mechanism for accidentally token transactions not exists so Im suggesting to implement it now.

Also I didn't know about approveAndCall, but it seems the way to go for me.

approveAndCall may be a good thing or may be not but now it is not implemented in ERC20 and will not solve accidentally token transactions between address and contract so Im not suggesting to implement approveAndCall.

My proposal solves a number of problems:

  1. Accidentally transactions between address and contract that is not supporting this token will no longer cause a loss of tokens.
  2. For users: Allows not to care about contract logic of work. To transfer tokens wherever you want, you always need to call only the transfer function.
  3. For contract developers: Allows to handle incoming token transactions similar to incoming ether transaction.
  4. It also optimizes blockchain usage.

If you found my proposal too complex to accept, then I found this increase in complexity a reasonable price, which should be paid for preventing accidental loss of tokens in the entire future.

Also I see the problem that the receiver needs to keep a list of what tokens it supports.

I don't see it is a problem but if this is the only thing that prevents the token standard from being accepted I designed a light version of contract-receiver. It will accept every incoming ERC23 token and do nothing with it. It can be called "token-trap" contract.
You can browse it here: https://github.com/Dexaran/ERC23-tokens/tree/light_version

I do like it, in fact i would also suggest for wallet contracts that the fallback func fires an standard event on the receiving contract called TokenTransfer(address _from, uint _value)
Tho i would rename the fallback func to t(...) or tokenFallback(...), sounds better.

@Dexaran those are all good points indeed. Sorry if the feedback seemed harsh, I'm indeed really interested in getting closer to economic abstraction so contracts can operate with tokens in a similar way the can with ether.

What do you think about the fallback function being something liket(address tokenSender, uint value, bytes data) and then the fallback function can do a delegatecall to itself with this data to simulate a payable function?

How you mean simulate a payable function?

Btw i don't fully understand why a contract should add supported tokens? As long as a contract fires the fallback function, it will be ERC 20 + 23

Btw i don't fully understand why a contract should add supported tokens?

Contract that is working with a specified tokens may contrain a mapping of supported tokens.For example if we are working with only Unicorns and incoming Token123 transaction appears we should reject transaction of not supported Token123.
Supported token may also be hardcoded or set inside receiving contract in any way you prefer. I just recommended addToken and removeToken functions to be in contract but they are not required.

Im not sure if i would make that into the standard, i find the fallback function useful, but as long as they are ERC 20 the contract should be able to deal with it. If he wants to reject certain tokens, than thats something they can do, but it doesn't need to be part of this standard.

By simulate a payable function I mean that after the token fallback is called, a specific function in the contract is called.

A rough, pseudo-Solidity implementation would be:

contract TokenReceiver {
  function t(address tokenSender, uint value, bytes payload) {
     if (_data) {
         delegatecall(this, payload)
     }
  }

  function foo() {}
}

So you could do transfer(contractAddress, value, '0xc2985578') in the token and have it call the function foo() in the receiving contract by sending value with a token. Maybe a couple of parameters should be added to the foo() like functions so they get the sender and the amount of tokens received.

The only thing that would need to be included in the standard is the case in which transfer has a payload parameter, as the way you handle it in the fallback could be up to every contract to decide how to do it.

Also regarding supported tokens, I think if a specific contract wants to only support a set of tokens or blacklist a specific one they can do it as part of their implementation, but I support the idea @that it shouldn't be included in the standard.

@frozeman I would recommend to "reject everything that is not marked as supported" but not "accept everything that is not marked to be rejected" because of when contract like the dao-refund is written it shouldn't accept any of incoming token except DAO. If DAO-token is not ERC23 so there is no way to accept anything ERC23 and we should place function tokenFallback(address _from, uint _amount) { throw; } inside dao-refund to make every ERC23 be rejected.
But if we need to make a DAO23 refund contract to accept ERC23 standard based tokens (DAO23) we should specify DAO23 as allowed. So any other ERC23 token will still be rejected.
Token standard is a recomendation to token developers how to develop their tokens in the best way. So I found it important not only recommend how tokens should be developed but also how token transactions should be handled.
Do you think it is not needed to be included in token standard?

What do you think about the fallback function being something liket(address tokenSender, uint value, bytes data)

@izqui as I understand it you are recommending to make token transactions behaving one step more similar to Ether transactions.
Where t(address tokenSender, uint value, bytes payload) means
address tokenSender == msg.sender
uint value == msg.value
bytes payload == msg.data

Exactly. And the actual msg.sender of t(...) is the token being used to transfer value.

So extending your comparison you would have:

address tokenSender == msg.sender
uint value == msg.value
bytes payload == msg.data
msg.sender == ether

Of course. tx.origin is now an address who is sending tokens, msg.sender is contract of sent token, msg.data is data signifying what function to call inside the token contract and payload is data signifying what function to call inside contract-reveiver of tokens.
Also tokenSender is address who is sending tokens too when only contract of tokens and contract-receiver are involved and no other contracts are called.
I found it awesome idea but I need to do some tests. So I cant say anything more specific right now.

Something to keep in mind regarding tx.origin is that in the case of usingtransferFrom(), tx.origin will be the authorized address to make the transaction and not the former token owner, which tbh I'm not really sure which one of the two should be accounted as the sender in this case.

I see we are 1 step away from creating Token-based Ethereum inside Ether-based Ethereum.
According to your idea token fallback function should handle incoming token transactions only in this manner:

function tokenFallback(address _from, uint _value, bytes payload){
  if(_data){
    delegatecall(this, payload);
   } else {
    //tokenFallback code here
  }
}

Because of Ether fallback function handles only transactions of value (ETH).
And as @frozeman said its not a part of token standard already. I found your idea cool but as for me I dont really know. So I want to get more feedback and do some more tests.
And of course there is always a possability just to ignore incoming data and handle only _from and _value as I suggested earlier.

I decided that token transaction must contain bytes data.

It's not a solution of any of the problems I'm aiming to solve but it may be needed for future use. As long as there is a way to attach data to Ether transactions I think there should be a way to do the same with token transactions too.
I don't care how exactly this will be used to attach HEX messages to token transactions or to encode inner functions execution but the way to attach data to the transaction (token or Ether) must exist.
As the result of this transfer function is changed and now contains bytes _data.

Now ERC23 is 100% backwards compatible with ERC20 and will work with every old contract designed to work with ERC20 tokens.

Nobody wants to lose its token by mistake.
There are hundreds of transactions to 0x0 address though.
So in our token contract we added :

  1. A test rejecting every sent to the 0x0 address.
  2. A specific burn address letting the user to burn explicitly some token if needed.
  3. the supply function returns initialSupply - balanceOf[burnAddress];

I don't know if it should be included in the standard, but all our futur token contracts will include these points.

@eburgel you should implement a function like this
function (address _contract, uint _amount) {
ERC20 token = ERC20(_contract);
token.transfer(msg.sender, _amount);
}
if you want to use ERC20 token standard because of you may want to refund someone who sends tokens to your contract. (or even to claim his tokens and sell them) You don't know what tokens it will be.
But there is an easier solution: just use ERC23. ERC23 tokens will not be accepted by a random contract that is not designed to work with them.

IMHO this should be handled by the UI, not at the core level. Doing a code check on every transfer places a gas burden on every token holder.

Agree with @aakilfernandes. I personally would never use this because it's way too much gas to handle errors that IMO should be handled by an application's second layer.

I do really like the idea of sending an ERC20 token value with a function call but that seems like a protocol level change.

this should be handled by the UI, not at the core level

@aakilfernandes there is no code check for wrong transfer. There is a tokenFallback function execution. It is solving a number of described problems including all accidentally transfers to contracts.
Also UI will never allow you to handle incoming token transactions.
I dont see any UI level protection already done. But I see $10000 already lost so Im proposing a token standard that will solve it once and forever with no need to placing a duty on every UI developer.

Agree with @aakilfernandes. I personally would never use this because it's way too much gas to handle errors
Doing a code check on every transfer places a gas burden on every token holder

@aakilfernandes @alex-miller-0
I dont realize what are you talking about. Did you browsed my code? ERC20 token transfer consumes 36500 gas. ERC23 token transfer consumes 38000 gas. How much transactions should happened to cover lost $10000 with 1500wei each?
approve than transferFrom consumes at least 60000 gas while transfer to contract with fallback function execution consumes 38000 gas (transferFrom and tokenFallback functions are doing nothing at this example) so ERC23 is about 2 times cheaper.

If you are interest in gas usage optimization you should look at this.
Imagine you have a token exchange contract where tokens can be deposited and than exchanged and withdrawn.
ERC20 token exchange pattern:

  1. approve token1
  2. deposit token1
  3. approve token2
  4. deposit and exchange token2 to token1 and withdraw token1 and send token2 to exchange order placer.

Each point is a transaction.
ERC23 exchange pattern:

  1. Deposit token1
  2. Deposit and exchange token2 to token1 and withdraw token1 and send token2 to exchange order placer.

You can deposit, exchange and re-send tokens with a single transaction.
I wrote this contract example and you can watch it here: https://github.com/Dexaran/dataPayload/blob/master/PayloadExchange_example/DEXchange.sol
This cotract is deployed here on Ropsten: https://testnet.etherscan.io/address/0x3BAD1B198bAC2dE458B5BCeE1ec0c99733B03cF2

This is the first token deposit: https://testnet.etherscan.io/tx/0x6e99ff5b628fc3fceaa959499d8488a761558feee73ba1fe7c067c1ece6440de

This is a token transaction that calls exchange and transfer of token1 and token2 to their buyers: https://testnet.etherscan.io/tx/0xf41529fb61de85a3e1ea45682f285ce7e23ea0c9f5283c392a4ca445eb14c92b

@Dexaran With approveAndCall patern we let the choice to the contract to accept or not the full stack of token.

For example, in an exchange you may want to buy 3 "things" for 1000 tokens each. You don't know if the price will go up or down duing the future block so you approve 3100 tokens. If the price goes up a little you will still be able to buy the thing, avoiding you to guess the future price.

However it doesn't consume the given allowance so I don't know if it's a valid thought

Edit : approvance can let you transfer token directly from an account to another, avoiding to put the token on a contract and then transfering them elsewhere. This may not be a big gas economy but it seems more logical for the usecase of contracts

I'll add another thing about approveAndCall and receiveApproval : Some token contracts already have them because it was already a proposition during last summer while ERC20 was in construction.

Anyway, the option for the user to transfer directly to an address without knowing if this is a contracts and the logic of everything seems cool to me. But in the real world the user use app and never call directly the transfer function with code. All of this can be layered in the app

With approveAndCall patern we let the choice to the contract to accept or not the full stack of token.

A real exchange is filling orders. If you are trying to fill orders that are already filled you will get your funds back and no trade will happened. If you want to fill orders even if their price will be higher than last trade price you should set a price higher.

When you are trying to buy ETH on Poloniex and ETH price is 0.038BTC you can set a price to 0.05 and send a buy request. So 0.038 orders will be filled then 0.039 will be filled when there will be no more 0.038 orders then 0.040 orders will be filled etc.
If you need to buy 3 "things" not depending on "thing" price you should send more tokens in your transaction. You will buy your 3 "things" then extra tokens will be send back to your address.
But its an exchange logic already.
ERC23 allows you to do it with no problems.

approvance can let you transfer token directly from an account to another, avoiding to put the token on a contract and then transfering them elsewhere.

Yes. But approveAndCall exchange execution is:
tx 1. approve of token1.
tx 2. deposit token1 intor exchange contract.
tx 3. approveAndCall -> transferFrom(token1) -> transferFrom(token2).
So there are: Approval(token1), Transfer(token1), Approval(token2), Transfer(token1), Transfer(token2) events.
In case of ERC23 there will be:
tx 1. Deposit token1.
tx 2. Deposit token2 -> send(token2) -> send(token1).
There are: Transfer(token1), Transfer(token2), Transfer(token1), Transfer(token2) events.
I dont see any real difference here.

For those who are saying:

IMHO this should be handled by the UI

You should keep in mind that every error-prone interface will lead to a loss of money. Who can guarantee that every future UI developer will never make a mistake?
I found it important not only to abstract users from contracts logic but to abstract UI developers from contracts inner logic too.

Also this should be handled by the UI since 2015 but still it is not done so I'm suggesting a solution of the existing issue.

I see separate use-cases for:

  1. having a smart-contract act on your behalf and
  2. having a smart-contract own tokens.

The former seems to be best served by approveAndCall and the later by tokenFallback. (Eventhough technically any solution using one can be converted into a solution using the other).

I'm currently working on a dual implementation. The specification is not clear on how tokenFallback should be used in transferFrom. The reference implementation does not call it at all. This appears to me as going against the intend: suppose contract C does not want tokens T. If A transfers to C directly, it will fail. If A transfer to C via intermediary B, it will succeed. If we take transferFrom to mean act-on-behalf-of, the correct behaviour should be as if the intermediary was not there, so tokenFallback should be called with A as _from. For completeness' sake there should also be a transferFrom(address _from, address _to, uint _value, bytes _data).

PS: I would suggest removing the private functions from the specification. Specifications should be about the external interface, not implementation details. These can go in a reference implementation

The specification is not clear on how tokenFallback should be used in transferFrom

In no way.
approve and transferFrom functions are supported only due to backwards compatibility reasons.
Every contract that is designed to work with ERC20 will work with ERC23 with no problems.
As for me Im suggesting to never use approve or transferFrom and use transfer every time if it is possible.

contract C does not want tokens T. If A transfers to C directly, it will fail. If A transfer to C via intermediary B, it will succeed

You are wrong. Let me explain why:
as C doesnt support ERC23 tokens T, C should implement this code:
function tokenFallback(address _from, uint _value, bytes _data) {
if(msg.sender==T) { throw; }
}
so when A is transferring ERC23 token T to C contract, A is calling T-contract asking him to transfer tokens to C and call fokenFallback at T with this args msg.sender=T (token contract is calling fallbackToken), _from=A, _value=amount of T.
when A is transferring tokens T via intermediary B this will happen:
A is calling T-contract to transfer tokens to B and execute tokenFallback on B --> then B is receiving tokens T and executing tokenFallback and transferring tokens to C (B is calling T-contract asking him to transfer T to C and execute Cs tokenFallback). tokenFallback will be executed at C with this args: msg.sender=T, _from=B, _value=amount of T.

As you can see you should filter incoming tokens by msg.sender where msg.sender will always be a token contract address.

PS: removed private functions. Also removed a ton of commented examples code from contractReceiver example.

I forked @Dexaran original repo and worked a bit on a proposed implementation with some tests and refactoring the code a bit.

Also I used @OpenZeppelin heavily used and tested token implementations as the base for all ERC23 interfaces and implementations, no need to reinvent the wheel here. I think it is helpful to define ERC23 as a superset of ERC20, rather than a complete different standard.

Here is the repo: https://github.com/AragonOne/ERC23

The core ideas are the same as we have been discussing here, but the StandardReceiver provides for the receiver a tkn struct that mimics the msg struct for traditional transactions.

The now working provisional API looks like this for a receiver:

contract ExampleReceiver is StandardReceiver {
  function foo() tokenPayable {
    LogTokenPayable(tkn.addr, tkn.sender, tkn.value);
  }

  function () tokenPayable {
    LogTokenPayable(tkn.addr, tkn.sender, tkn.value);
  }

  event LogTokenPayable(address token, address sender, uint value);
}

I added Zeppelin's Standard Token tests to the repo to prove that it is completely backwards compatible and the current ERC20 transfers keep working as expected. On top of that I added really simple tests that test ERC23 basic functionality.

There are more details in the readme of the repo.

Let me know what you think!

There is a vulnurability in approve function so I'm thinking on removing it from ERC23 standard as it will never be needed any more because of tokenFallback implementation.

vulnerability is described here

I'm asking for more feedback about is approve needed and in what cases. I'm about to remove approve and transferFrom at all.

@Dexaran I'd like to explain my token standard idea that depends on a TRXID opcode before you go all-in on this: #222

The key is that his opcode enables you to revert transactions that don't balance without needing to enter an arbitrary contract's scope to do "approve-and-call".

Can you join dapphub.chat and PM me (@ nikolai)?

when A is transferring tokens T via intermediary B this will happen:
A is calling T-contract to transfer tokens to B and execute tokenFallback on B --> then B is receiving tokens T and executing tokenFallback and transferring tokens to C (B is calling T-contract asking him to transfer T to C and execute Cs tokenFallback). tokenFallback will be executed at C with this args: msg.sender=T, _from=B, _value=amount of T.

Correct, but that is if the B supports ERC23. I'm talking about a scenario where B is a legacy non-ERC23 contract. B would be using the ERC20 approve and transferFrom interface:

  1. A: T.approve(B, 100)
  2. A: B.doMagic(C)
  3. B: T.transferFrom(B, C, 100)

Now C owns token T, despite both C and T implementing ERC23 and C explicitly revoking tokens.

The solution would appear to be to call tokenFallback from transferFrom. But then it would fail if C does not support ERC23. (What we really need is something like #165 to be universally adopted.)

approve and transferFrom are implemented due to backwards compatibility reasons only.
If something is working with ERC23 tokens it will always receive tokens via transfer.
If something is not working with ERC23 tokens but working with ERC20 it may receive tokens via transferFrom but I suggest to never use approve any more.
So there is no need to implement tokenFallback call in transferFrom because of ERC23 assumes you will not use transferFrom. It may be needed only if you are going to work with contracts designed to work with ERC20.

@Dexaran i updated your ERC to be more readable, please check that i didnt change anything, as the transferToContract had no example?!

This is not ERC23; EIP/ERC numbers are assigned by the editors. Please rename per EIP 0.

I named my token standard ERC23 because of it will be token standard for EthereumClassic too. And there were no ECIP20/ECIP21/ECIP22 in EthereumClassic but ERC20 token standard is well-known.
I called it ERC23 to avoid name mismatch, because this would be the standard for contract developers on ETC and ETH. I'm aiming to prevent token losses on both chains.
I'm not involved in politics with regard to splitting the chain. Interested in development only.
If naming problem is so important for you I can discuss it as ERC223 (issue number) here.

I think the main lesson from the first standard attempt was that we should let standards emerge instead of treating it as land to be claimed for glory

I start adapting MiniMe token to be an ERC223 compatible token.
Here you can see the PR to adapt it.
https://github.com/Giveth/minime/pull/11/files
Here are some issues/questions that I found in the standard during adaption:
1.- isContract, transferToContract, transferToAddress are internal, so they should not be part of the standard. This is just a way to implement the standard.
2.- What about the Transfer event? Do we add the data param breaking the ERC20 standard? In my case, I create two events one for ERC20 and the other for ERC223 with the same name and an extra data param.

I would love to hear comments.

@jbaylina

  1. I want to standardize part of the internal logic of the contract, not just its interface.
    I'm solving a problem: people are losing money because of inaccurate implementations of token contracts.
    I want to prevent this, and it seemed to me important to standardize these methods (especially check for bytecode and perform a fallback function).

  2. I don't think it's important to write token-data into blockchain via Transfer event. It will break backwards compatibility without any sufficient benefits.
    Token data will be available as part of transaction data.

Token data will be available as part of transaction data

Unless the token transfer with data is performed by a contract, and then it becomes an 'internal transaction' which is non-trivial to fetch.

@izqui is right; you absolutely shouldn't rely on expecting that calls to your contract are top-level transactions. This is exactly why events/logs exist.

If you're looking for ways to identify whether or not a contract supports a function, you might consider requiring EIP 165 - or at least ,the version implemented at the end of the discussion, which is the one implemented by ENS.

@izqui I want to remove approveAndCall from token contracts.
The existence of "approveAndCall" confuses users / developers and leads to a loss of money.

I think the only way to prevent a scenario with invoking an invalid function to send tokens is to allow only one function to transfer tokens. I mean only transfer function must transfer tokens. Doesn't matter whether the receiver is a contract or not - only transfer should be executed to send a token transaction always.

If you need to do something after the incoming token transaction occurs, this is the standard situation where event handling is required.
For this reason, the tokenFallback function is implemented.

@Arachnid @izqui
I found data logging reasonable too.
Do you think that it's important enough to break backwards compatibility?

You could log both the old event and a new one, to avoid breaking backward compatibility.

@Arachnid The problem is if you want to send tokens to a standard Multisig for example. They should be able to hold tokens. They don't implement the EIP-165, but they should be able to receive tokens.

So may be the best way to implement is to try to call supportsInterface, but if this call throw or return false, then call the transferToAddress and if return true the call transferToContract.

But doing this, does not prevent the 'lose money' situation that wants to avoid this standard.

@jbaylina I think that described problem is a problem of 'standard multisig' itself. Multisig is a contract designed to work with tokens. Every contract that is designed to work with tokens should implement ReceiverInterface (tokenFallback function).
If you want to create a multisig wallet and want it to receive tokens - you should implement tokenFallback.
Otherwise, this is a bad "standard" for multisig, if its implementation leads to a loss of money.

Every contract that is designed to receive Ether is implementing fallback function with payable modifier. No one is complaining about it.

When you design a multisig, you generally want it to work for any present and future standards and contracts. The code should be decoupled from any other contract. Every time a new standard comes, you should not have to upgrade the multisig.

So I don’t like to have to change the code of the multisigs to work with this standard. (But I see the good points of doing it).

If we expect users to update their multisig wallets, this standard is not going to go anywhere.

@jbaylina When you design a multisig, you should artificially implement function() payable { ... } called fallback. It is needed to allow multisig to receive Ether. No one is complaining about it.

If you want your multisig to receive tokens you should artificially implement tokenFallback function. It should be the second default function. This is necessary because there is no alternative mechanism for handling events on Ethereum.

The main problem is: fallback function is not accepting arguments. If fallback accepts bytes data, I could use it not only for Ether transactions, but also for token transactions. I found it impossible.

If we expect users to update their multisig wallets, this standard is not going to go anywhere.

I think that if we expect that users will remain in danger of choosing the wrong function that will lead to a loss of money, then entire Ethereum token system will not go anywhere.

If someone has any ideas for solving these problems in any other possible way, feel free to offer your suggestions.
I am ready to update this standard with better implementations, if it can still solve a number of problems that I have named.

@Arachnid I don't think that firing two Transfer events is a good idea.
It will log two transfers to the blockchain, while in fact only one transfer takes place.

Also I don't see any contracts with pseudo introspection implementation yet.

I think the most reasonable solution would be to requiretokenFallback on each receiver (that contain bytecode) and assume that if tokenFallback is not implemented, then the receiver can not receive tokens and the transaction should fail.

Again, if you have any alternative solutions / suggestions / arguments - feel free to name them.

Just another question, I would recommend that the name of the method transfer be changed for transferTo in order to distinguish it from the transfer ERC20 method. Overloading methods is supported in solidity, but some clients might get confused on calling it because they do not support it in their native language. That's the case of javascript for example.

@jbaylina I don't understand what are you talking about.
It's a code of calling a contract from javascript:

    var rawTx = {
      nonce: '0x'+txNonce,
      gasPrice: '0x'+txGasPrice,
      gasLimit: '0x'+txGasLimit,
      to: toAddress,
      value: '0x'+txValue,
      data: ''+txData
    };
    var tx = new Tx(rawTx);
    tx.sign(privateKey);

    web3.eth.sendRawTransaction(tx);

I do not see any requirements for javascript to know what functions will be called inside EVM. Javascript is calling something like this:

0xf86581ad8504a817c8008252089401000b5fe61411c466b70631d7ff070187179bbf80801ca0a8dd9b1887d79b40cddbf42c34472518156c990c25ea74cad63a6b07e3fac534a02f4bc6bbd23913e5a2d7dc6b286646a2209778e46c2504df30a9239d753f41ec

It's raw signed transaction.

Also there is no difference for the external services (such as MEW, ClassicEtherWallet, DexNS etc. ) what tokens we will send (ERC20 or ERC223). They always call transfer (address, uint256) when the user wants to send tokens.
I see no reason to create additional work and overhead for them while there are no real benefits.

I think that we should keep a minimal difference from ERC20, until this difference will be necessary to solve the previously mentioned problems.

@Dexaran It's not an ethereum/solidity issue, it's more about the wrappers. In javascript web3, it's normal to have a class with its methods mapped to the solidity method.
So if for example if you have a contract with methods transfer and balanceOf method, this maps to an object in javascript with methods transfer and balanceOf .
This works ok, except when the contract have overloaded methods. Because javascript does not support overloaded methods. Of course you can always construct the raw transaction by hand, but this is not comfortable for the user, and I believe it's better to avoid it if it's possible.

In the other side, having a transferTo method may also act as a signal that you are using a ERC223 and not ERC20 token.

I started implementing this standard with the idea of making a token ERC20 and ERC223 compatible.

I'm opening my mind and I'm evaluating if it's better idea to implement an ERC223 only compatible token. (This is going to be a hard sale, but I see some advantages).

I would like to hear thoughts on that.

If ERC223 want to be a full token standard, then I would add the standard methods: balanceOf, totalSupply may be also the name, symbol, decimals and version in the specification. The Transferor TransferTo event should be added too. Also, the EIP165 would be nice to be used.

In javascript web3, it's normal to have a class with its methods mapped to the solidity method.

If this is not a token standard issue, then I will gladly discuss it in another EIP about web3/javascript/solidity.

In the other side, having a transferTo method may also act as a signal that you are using a ERC223 and not ERC20 token.

The idea of ERC 223 was to change transfer function logic without changing its signature. This will allow users to call send tokens from the UI, as they did before. There will be no difference between ERC20 and ERC 223 for ordinary users and UI services that already exists.

I believe that tokens must be transferred by transfer function only. Any other implementation that leaves execution of any other transferring functions without handling (without executing tokenFallback on the receiver) will result in a loss of money!

I started implementing this standard with the idea of making a token ERC20 and ERC223 compatible.

Again, ERC223 is not only a contract interface standard. I'm not trying to create an additional function that will perform transactions differently. I'm aiming to standardize token-contract logic that will not result in a money losses. I'm trying to change the existing transfer function to make its signature ERC20-compatible, but with the updated logic.

Any other implementation that will leave older functions without changes will not solve the problem of lost money.

If ERC223 want to be a full token standard, then I would add the standard methods: balanceOf, totalSupply

Thank you. I have added this functions to the standard.

Transfer or TransferTo event should be added too. Also, the EIP165 would be nice to be used.

Added Transfer event.
I think TransferTo event is not needed because of there is no function that is calling this event.

Melonport was launched recently but $1100 are already lost on MLN tokens. https://etherscan.io/token/Melon?a=0xBEB9eF514a379B997e0798FDcC901Ee474B6D9A1+

@Arachnid I updated Transfer event signature for ERC 223.
ERC 223 Transfer event must contain bytes _data to log it in blockchain.
I agree it is important to implement.

At the moment of writing this article there were only about $10 000 lost in ERC20 tokens.

Less than 2 months passed and now there are about $60 000 lost.
We are discussing a new standard for tokens, and people are losing money just right now.

Watch this transaction. This guy lost a lot of money ( ~ $42000) 4 days 5 hours ago.
You can check how often such transactions occur.

I think the problem is extremely relevant!

@Arachnid I don't think that firing two Transfer events is a good idea.
It will log two transfers to the blockchain, while in fact only one transfer takes place.

No, it will log two different events annotating the same transfer. Those two events have different signatures.

Also I don't see any contracts with pseudo introspection implementation yet.

ENS uses it extensively.

@Arachnid I updated Transfer event signature for ERC 223.
ERC 223 Transfer event must contain bytes _data to log it in blockchain.

I thought you wanted to maintain backwards compatibility?

@Arachnid

ENS uses it extensively.

As far as I know ENS is not yet working and your previous version of ENS failed with two critical errors. I don't think ENS is a good example of already working contract.

I understand that presudo introspection can solve the problem. On the other hand, it will be too difficult to teach all future contract developers what things they need to implement to work with tokens.

Fallback calls are the standard method of event handling in programming. This method will be similar to the handling of Ether transactions.

Implementing the pseudointrospection interface is another matter.

No, it will log two different events annotating the same transfer. Those two events have different signatures.

It's true, but I don't think logging two events for a single transfer is a good idea. Also it seems confusing for third party developers who will work with this events.

I thought you wanted to maintain backwards compatibility?

I would like to keep backwards compatibility if it is possible.
Backwards compatibility means interoperability with input designed for older version.
Since the signature of the transfer function has not changed, ERC 223 transaction will work with an input destined for the ERC 20 without problems. It means there will be no difference for users. Also third party UI and services wouldn't need a special upgrade to work with ERC223 tokens.

As far as I know ENS is not yet working and your previous version of ENS failed with two critical errors. I don't think ENS is a good example of already working contract.

ENS has been deployed on Ropsten for months, and goes live on mainnet on the 4th of May. The issues with the first launch were all related to the auction registrar, not the core system itself, and have nothing at all to do with implementsInterface.

It's true, but I don't think logging two events for a single transfer is a good idea. Also it seems confusing for third party developers who will work with this events.

Why not? It's clear what each one means, and nobody is going to accidentally scoop up both different events and treat them as two separate things.

Since the signature of the transfer function has not changed, ERC 223 transaction will work with an input destined for the ERC 20 without problems. It means there will be no difference for users. Also third party UI and services wouldn't need a special upgrade to work with ERC223 tokens.

The change you're making changes the signature for the transfer event, which will break all existing code that listens for transfer events.

@Arachnid
transfer and approve+tranferFrom are two different methods of transferring tokens and it's clear what each one means, and nobody is going to accidentally use wrong one but $60000 are already lost.
I can conclude that illogical behavior will lead to an incorrect implementation and loss of money.
I understand your position, but for me it does not seem like a good solution. I'm aiming to create a good standard for future developers and make it as clear as possible.

The change you're making changes the signature for the transfer event, which will break all existing code that listens for transfer events.

I would say yes. And I'm ready to personally write a patch to every blockchain explorer that will suffer troubles because of this standard implementation with my own hands. I think its a reasonable price that should be paid to prevent accidentally token losses in the entire future.

Sorry I deleted my comment because i didnt think it added enough to the discussion. It originally said that blocking transfers to the token contract itself, and the 0x0 address, as is done in the MiniMe token contract would be enough to mitigate the loss of funds.

@GriffGreen it's not enough.
The main problem is that there is no way to handle the incoming transaction. There is no way to reject accidentally sent transactions also.

Let's imagine decentralized exchange that is controlled by contract. Can you guarantee that users will never choose the wrong function for depositing to a decentralized exchange? And what happens if they choose the wrong function? The answer is: no handling will occur. There will be the same situation as with token-contracts themselves accepting their token deposits.

TokenFallback is almost required to allow tokens to be traded on decentralized exchanges.

Every third party contract that will work with tokens requires event handling. Otherwise, each third party contract that potentially can work with tokens will become a trap for tokens, where a lot of tokens will get stuck as a result of the transfer performed without handling.

@Dexaran I agree that allowing a contract to handle incoming token transfers is valuable and I am happy to see an alternative method mitigating the approve race condition. I am very glad this standard is being written, and hope it can be the same for both ETC and ETH :-D

I would say yes. And I'm ready to personally write a patch to every blockchain explorer that will suffer troubles because of this standard implementation with my own hands. I think its a reasonable price that should be paid to prevent accidentally token losses in the entire future.

I still do not understand your objection to emitting both the legacy event and the new one. Under what situation would they cause confusion?

If you're prepared to throw backwards compatibility out the window, there are a lot of other improvements you could make too.

@Arachnid

I still do not understand your objection to emitting both the legacy event and the new one. Under what situation would they cause confusion?

I found it illogical. When one transfer occurs in fact there is no need to fire two events. I can not name examples now, but I think that when ERC 20 was developed, nobody could name the situation when tokens will be sent to the token-contract itself.

In general, I tend to accept your suggestion. I need to learn more about possible downsides and I want to receive more feedback about firing two events.

If you're prepared to throw backwards compatibility out the window, there are a lot of other improvements you could make too.

What else improvements can you suggest?

@Dexaran i thought about your idea a lot and i think a tokenFallback function is a must, to make complex token and contract interactions possible.

Some questions:

  • What happens when a contract does not implement the tokenFallback, won't the tokens be transferred, or they will by default, and a contract has to throw to prevent transfer?
  • Will the transfer function fail when trying to execute tokenFallback and the contract doesn't have it?
  • Whats the reason for sending the bytes _data, besides that a receiving contract could act on something?

I would also suggest to make the transferToContract and transferToAddress either private with _transferTo.. to not part of the standard. The logic should be exemplified, but this functions don't need to be exposed in the standard IMO.

@frozeman

What happens when a contract does not implement the tokenFallback, won't the tokens be transferred, or they will by default, and a contract has to throw to prevent transfer?

If the contract does not implement tokenFallback and someone tries to transfer tokens to this contract, then the transaction must fail. No token transfer occurs.
When you are trying to send Ether to contract that does not implement function() payable { } transaction is failing.
I'm sure this default behavior should be the same for Ether transactions and tokens transactions.

Will the transfer function fail when trying to execute tokenFallback and the contract doesn't have it?

The transfer function will fail. I would say must fail. This mechanism prevents the receipt of tokens when they are transferred to a contract that is not intended to receive tokens.
Ether transaction is failing when no function() payable { } is implemented too. In this case, the transfer of the Ether (tokens) should not occur.

Whats the reason for sending the bytes _data, besides that a receiving contract could act on something?

For the same reasons that the Ether transaction contains msg.data

The logic should be exemplified, but this functions don't need to be exposed in the standard

I'm taking into account experience of ERC20. There is no errors in ERC20.
At the other hand a special function like this should be implemented to extract all accidentally sent tokens from token-contract itsel:
function Perform(address _to, uint256 _val, bytes _data) payable {
_to.call(_val)(_data);
}

Since this was not part of the standard, this function was not exposed in the standard, and this results in a loss of $ 70,000.

I wrote an article about it but nothing changes.
For example Melonport was launched and now we can enjoy $1200 already lost in MLN contract.

I found it important to standardize not only contract interface but contract logic too. I want it to be highlighted as highly recommended implementation.
Otherwise, it will not solve the problem of lost money, because this problem is caused by an incorrect implementation, and not an error or a mistake in the standard.

Again, I want it to be a highly recommended implementation. Not only a standard interface that third party developers will work with.

I agree with your statements. It would make sense to add this expected behaviour to the standard itself. To clarify.

Also what should the tokenFallback return??

@frozeman
I can exclude these functions from this standard, but then we will need to check whether the recipient is a contract or not inside the transfer function.

I want to standardize this logic:

  function transfer(address _to, uint _value, bytes _data) returns (bool success) {
    uint codeLength;
    assembly {
        //retrieve the size of the code on target address, this needs assembly
        codeLength := extcodesize(_to)
    }
    if(codeLength>0) {
        balances[msg.sender] -= _value;
        balances[_to] += _value;
        ContractReceiver reciever = ContractReceiver(_to);
        reciever.tokenFallback(msg.sender, _value, _data);
        Transfer(msg.sender, _to, _value, _data);
    }
    else {
        balances[msg.sender] -= _value;
        balances[_to] += _value;
        Transfer(msg.sender, _to, _value, _data);
    }
    return true;
}

I agree it can be confusing to have three private functions in a standard.

I'm afraid that there will be a possibility to create a contract that will match ERC 223 interface, but will not contain the execution of "tokenFallback" when transferring to a contract.

tokenFallback is returning true when executed successfully.

Analogue to address.send(_amount); that is returning true in Solidity.
send(uint256) is a function that is executing fallback and returning true when Ether transaction occurs.
I made it returning true when executed successfully but now I see that function transfer should return true but not tokenFallback.

I think tokenFallback should return nothing because of function() payable { } is returning nothing.

You should state that clearly in your ERC

I'm going to make tokenFallback returning nothing.

@frozeman @Arachnid there is a number of points that I'd like to make clear.
I found it a good idea to log _data of token transaction via Transfer event but ERC20-compatible Transfer event is not containing bytes _data field. @Arachnid suggests to fire both (Transfer without data and TransferERC23 with data) events to log _data without breaking of backwards compatibility.

@vbuterin said that "This is not a very clever idea to divide transfers to transferToContract and transferToAddress because of we are planning to make each account a contract in EIP86" in this speech.
I do not see any changes to EIP # 86 that are associated with Externally Owned Accounts, but only the idea of implementing a special contract that will be assigned to account. If the previous version of addresses (without bytecode) will be preserved, there will be no problems with ERC 223.

If there will be a contract that can be assigned to the account address, then it will be required to artificially add the function function() payable {} to allow it to receive Ether. In case of ERC 223 it will be also required to artificially add the function tokenFallback(address,uit256,bytes) { } to allow this contract to receive tokens too.

I would like to receive more feedback/suggestions about:

  1. Transfer events and backwards compatibility.
  2. ERC 223 adaptation for Metropolis.

It would be good if tokenFallback when returning false, or throwing will cancel the transfer.
I might want to have a logic which checks certain parameters and then allows or denies the transfer.
I guess throw should take care of that? If so please make that clear in the specs.

As the specs should tell what should happen, but not how to implement it.

commented

I think is very important that the tokenFallback should return a bool. We need know that what we call has ended with success.
I work currently on a token which is connected with other contracts. I use for every extern call bool success for return.
My token has a second return statement: uint256 back . Which declares how much token has the contract used. (same as send ether back)

function tokenFallback(address _from, uint _amount, bytes _data) external returns (bool success, uint256 back)

@iFA88 TokenFallback is an analog of a fallback function that is automatically executed when Ether transaction to a contract occurs. fallback function is returning nothing.
transfer function should return bool when executed. tokenFallback will fail (when not implemented by receiver / something goes wrong / throw was executed) or submit.

IMO generally, returning false to indicate failure is an anti-pattern and should be avoided; it's far too easy to ignore return values, and even if you don't it's easy to forget some part of necessary cleanup. Throwing solves both these issues.

@frozeman Each executed throw stops the transfer (the entire transaction will fail and no changes will be applied to the blockchain).
No false returns are needed. tokenFallback will fail or not fail. If it will fail then the transaction will be canceled and no balances changes will appear. The only case for which balances changes will occur is the correct execution of all functions including all tokenFallbacks which are executed in this transaction.
Also, the transaction will automatically fail if the tokenFallback function is not implemented, but it is called.

Actually, that's not true; a throw only rolls back the current call. The caller then gets a return value of 0 from CALL, and can choose what to do; Solidity automatically throws again if the call was made using an ABI or send. The end result is that in Solidity, one throw generally rolls back the whole transaction, but that doesn't have to be the case.

Good, i was not sure if there was a way to implement it that if external calls "throw" will not halt the current function. But until not using contract.send(), this shouldn't be a problem.

@frozeman @Arachnid
Have you any ideas about this?

  1. Transfer events and backwards compatibility.
  2. ERC 223 adaptation for Metropolis as I wrote earlier.

I'm going to update current implementation and this points are important.

Concerning

  1. I think it is possible to have the same named Events twice but different signatures, right @chriseth?
    If so you could name it the same and let people choose which ones to watch, or both.
    But given that its a new standard, i might just drop the old version and only allow the new version. Interfaces can watch both, and just wont receive any event on the old signature, or the new, depending wether its a ERC20 or ERC223.

With time people will tend to use one or the other standard.
Better to keep the standard clean and the UI can take the choice.

  1. @vbuterin you should help out here, i can't answer this question.

I updated specifications and divided standard contract from recommended implementation version.

ERC20-compatible version, where the mechanism of approve and two events Transfer are supported.
Recommended implementation where isContract / transferToAddress / transferToContract functions are implemented.
ERC 223 base standard contract.

The opening comment has:

The most important here are, transferToContract and tokenFallback.

but there is no description of transferToContract... Please update

@ethers I have corrected the comment.
I removedtransferToContract from the standard itself and left it in the implementation example recently.