{"id":"ERC-223","name":"Token with Transaction Handling","status":"Final","chain":"both","category":{"id":"transaction-friction","name":"Transaction Friction","description":"Reducing clicks, signatures, and mental overhead"},"journeyStages":[{"id":"executing","name":"Executing Transactions","description":"Performing on-chain actions"}],"uxImpact":"ERC-223 defines a tokenReceived callback for ERC-223 transfers to contracts. A compliant token transfer to a contract without the required handler reverts, while transfers to EOAs proceed without a callback. This can make compatible contract deposits one transaction, but it does not protect arbitrary wrong addresses, other token standards, or a recipient's business logic.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-223","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-223","summary":"ERC-223 defines a tokenReceived callback for ERC-223 transfers to contracts. A compliant token transfer to a contract without the required handler reverts, while transfers to EOAs proceed without a callback. This can make compatible contract deposits one transaction, but it does not protect arbitrary wrong addresses, other token standards, or a recipient's business logic.","applicability":{"whenToUse":["The selected token implements the ERC-223 transfer and receiver path.","A compatible contract deposit can benefit from the callback path instead of a separate approve-and-pull flow.","The product can explain that a handler check applies only to the ERC-223 transfer path and target contract.","You are designing a transfer flow with visible callback, revert, and fallback states."],"whenToAvoid":["The token or recipient does not implement the ERC-223 transfer-handling path.","The product needs a universal safety guarantee for arbitrary addresses or tokens.","The app cannot explain mixed ERC-20 and ERC-223 support."]},"designerTakeaways":["Show when a selected token and contract support the ERC-223 handler path.","Explain that an incompatible contract handler can cause the ERC-223 transfer to revert before the balance changes.","Show the callback payload and the recipient contract before signing when data carries an action.","Keep a standard-compatibility and unknown-support state; do not promise protection for other token standards or arbitrary addresses.","Describe a one-step contract deposit only when the recipient's callback actually credits or performs the intended action."],"problemsSolved":[{"problem":"An ERC-223 deposit can target a contract that does not handle the token","oldWay":"A token transfer to a contract without the required handler may be unrecoverable","newWay":"For the compliant ERC-223 transfer path, a contract without tokenReceived causes the transfer to revert","impact":"critical"},{"problem":"Transfer to contracts requires approve + transferFrom","oldWay":"Two transactions: approve contract, then call contract","newWay":"A compatible receiver can inspect transfer data during the ERC-223 transfer, without implying that every action is atomic or supported","impact":"high"},{"problem":"No way for contracts to react to incoming tokens","oldWay":"Contract has no idea tokens arrived, must be notified separately","newWay":"The tokenReceived callback gives a compatible recipient a transfer notification and payload","impact":"high"},{"problem":"Users may confuse the token contract or another address with a deposit recipient","oldWay":"An ERC-223 handler check does not identify the intended business recipient or token contract","newWay":"Show the target contract and token address, simulate when possible, and explain that ERC-223 handling is scoped to the selected token and path","impact":"medium"},{"problem":"Deposit flows are fragmented across transactions","oldWay":"Approve → Deposit → Contract pulls tokens (3 interactions)","newWay":"A compatible receiver can credit a deposit during the transfer callback when its own checks succeed","impact":"medium"}],"uxPatterns":[{"name":"Safe Transfer with Callback","description":"A compatible receiver can process an ERC-223 transfer callback","userFlow":["User enters deposit amount","UI shows the token and receiver support the callback path","User clicks deposit","Transfer with data triggers vault deposit","Receiver reports the resulting credit or action"]},{"name":"Protected Address Warning","description":"Explain the handler check for contract recipients","userFlow":["User enters contract address","UI detects it's a contract (not EOA)","Shows the token and target contract","Explains the handler requirement and its limits","Transaction may revert if the handler check fails"]},{"name":"Transfer Revert Explanation","description":"Explain a failed ERC-223 handler check without implying universal recovery","userFlow":["Transfer to incompatible contract","Transaction reverts or returns an error","UI explains that the ERC-223 balance update did not complete when the transaction reverted","Shows the token and target contract","Suggests alternatives"]},{"name":"Token Transfer with Payload","description":"Attach data that a compatible receiver can interpret","userFlow":["User clicks buy on NFT","UI shows payment will include data","Explains the callback and data scope","Transfer invokes the compatible receiver","Show the receiver-reported result after confirmation"]}],"uiComponents":[{"name":"ContractCompatibilityCheck","description":"Check whether the selected recipient advertises or simulates the ERC-223 handler path","states":["checking","compatible","incompatible","unknown"],"props":["address","tokenAddress","onResult"]},{"name":"TransferWithData","description":"Transfer input with optional data payload","states":["simple","with-data","encoding","ready"],"props":["amount","recipient","data","onTransfer"]},{"name":"SafetyIndicator","description":"Label the selected token and recipient support without promising universal protection","states":["supported","unsupported","unknown"],"props":["tokenStandard","recipient"]},{"name":"RevertExplainer","description":"Explain a callback or handler failure with an address and token fallback","states":["displaying","dismissed"],"props":["reason","tokenName","alternatives"]}],"antiPatterns":[{"pattern":"Presenting ERC-223 as universal transfer protection","why":"Users confused when transaction reverts","instead":"Name the selected token, receiver, handler requirement, and the limits of the check","severity":"high"},{"pattern":"Hiding the callback/data functionality","why":"Users miss the one-transaction benefit","instead":"Describe one-step deposits only for a compatible receiver and show its resulting state","severity":"high"},{"pattern":"Treating ERC-223 support as interchangeable with ERC-20","why":"Users don't understand the safety difference","instead":"Show which token transfer method and receiver path are supported","severity":"medium"},{"pattern":"Assuming a handler check proves the intended recipient","why":"Users waste gas on doomed transfers","instead":"Show the token and destination, simulate when possible, and retain a clear fallback","severity":"medium"},{"pattern":"Complex data encoding exposed to users","why":"Technical details confuse users","instead":"UI constructs data payload, shows human-readable summary","severity":"medium"}],"keyTakeaways":["ERC-223's handler check applies to the selected token's transfer path and contract recipient.","A failed handler call can revert that transaction; it is not a guarantee for arbitrary addresses or token standards.","Show the token, destination, data, callback result, and fallback state.","Call a deposit one-step only when the compatible receiver performs the intended action."],"technicalNotes":"ERC-223 requires a token transfer to call tokenReceived(address, uint256, bytes) on a contract recipient; a missing handler causes the compliant transfer path to revert. EOAs do not receive the callback. The callback receives arbitrary data and must validate the token contract and payload. The standard does not protect transfers made through another token interface, prove a destination is the intended business contract, or guarantee a callback's external service outcome.","lastUpdated":"2026-09-18","specConstraints":[{"id":"token-receiver-required","constraint":"A contract receiving ERC-223 tokens must implement `tokenReceived`; otherwise the transfer reverts.","appliesTo":"application","normativity":"normative","specQuote":"Token transfers to contracts not implementing `tokenReceived` as described below MUST revert.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#specification","designImplication":"Detect whether a destination supports the receiver flow before sending and explain why a contract destination may reject the transfer."},{"id":"optional-token-metadata","constraint":"Name, symbol, and decimals are optional usability metadata and must not be assumed to exist.","appliesTo":"application","normativity":"normative","specQuote":"OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.","specSection":"Specification — Token contract — Token Methods — name","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#name","designImplication":"Use the token address and raw-unit fallback when metadata is absent. Never block a valid transfer because optional display metadata is missing."},{"id":"contract-transfer-reverts-without-handler","constraint":"A transfer to a contract calls the receiver handler and reverts without it, so the token movement does not complete.","appliesTo":"application","normativity":"normative","specQuote":"This function must transfer tokens, and if `_to` is a contract, it must call the `tokenReceived(address, uint256, bytes calldata)` function of `_to`. If the `tokenReceived` function is not implemented in `_to` (recipient contract), then the transaction must fail and the transfer of tokens must be reverted.\nIf `_to` is an externally owned address, then the transaction must be sent without executing `tokenReceived` in `_to`.\n `_data` can be attached to this token transaction, but it requires more gas. `_data` can be empty.","specSection":"Specification — Token contract — Token Methods — transfer(address, uint)","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#transfer-address-uint","designImplication":"Make recipient compatibility part of the send review and distinguish an unsupported contract receiver from a balance failure."},{"id":"receiver-callback-order","constraint":"The receiver callback runs after the token state updates to avoid re-entrancy attacks.","appliesTo":"account-implementation","normativity":"normative","specQuote":"The `tokenReceived` function of `_to` MUST be called after all other operations to avoid re-entrancy attacks.","specSection":"Specification — Token contract — Token Methods — transfer(address, uint)","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#transfer-address-uint","designImplication":"No additional user control is required, but a product should not claim final settlement before the callback-enabled transfer completes."},{"id":"native-token-delivery-order","constraint":"When a payable transfer includes native value, the value is delivered before token balances update and the receiver callback runs.","appliesTo":"application","normativity":"normative","specQuote":"NOTE: If `transfer` function is `payable` and ether was deposited then the amount of deposited ether MUST be delivered to `_to` address alongside tokens. If ether was sent alongside tokens in this way then ether MUST be delivered first, then token balances must be updated, then `tokenReceived` function MUST be called in `_to` if it is a contract.","specSection":"Specification — Token contract — Token Methods — transfer(address, uint)","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#transfer-address-uint","designImplication":"Show token and native-value components together in review. A combined transfer must not be described as a token-only action."},{"id":"transfer-with-data-reverts","constraint":"The data-bearing transfer also requires a receiver handler for contract destinations and reverts without it.","appliesTo":"application","normativity":"normative","specQuote":"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` (recipient contract), then the transaction must fail and the transfer of tokens must not occur. \nIf `_to` is an externally owned address (determined by the code size being zero), then the transaction must be sent without executing `tokenReceived` in `_to`.\n `_data` can be attached to this token transaction, but it requires more gas. `_data` can be empty.","specSection":"Specification — Token contract — Token Methods — transfer(address, uint, bytes)","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#transfer-address-uint-bytes","designImplication":"Preserve transfer metadata in the confirmation and keep the failure state recoverable when the recipient cannot handle it."},{"id":"erc20-deposit-loss-risk","constraint":"Sending an ERC-20 token to a contract with plain `transfer` can reduce the sender balance without crediting the contract’s deposit record.","appliesTo":"application","normativity":"backwards-compatibility","specQuote":"If tokenA is deposited with `transfer` function then it will result in a loss of tokens for the depositor","specSection":"Backwards Compatibility","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#backwards-compatibility","designImplication":"Do not route ERC-20 deposits through an ERC-223 receiver assumption. Show the required deposit method and token contract before the user confirms."},{"id":"replay-awareness","constraint":"The plain-ether-like transfer model requires replay issues to be considered.","appliesTo":"application","normativity":"security-considerations","specQuote":"## Security Considerations\n\nThis token utilizes the model similar to plain ether behavior. Therefore replay issues must be taken into account.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#security-considerations","designImplication":"Keep transaction status and retry behavior tied to the exact transfer request so a retry cannot be mistaken for a second intended payment."},{"id":"receiver-magic-acceptance","constraint":"A tokenReceived callback must return 0x8943ec02 after handling an incoming token transfer; a fallback callback may not return that magic value.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification > ERC-223 Token Receiver > Receiver Methods","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#receiver-methods","designImplication":"Treat the magic return as the receiver acceptance signal, distinguish token filtering from successful handling, and keep a rejected callback visibly failed.","specQuote":"NOTE: `msg.sender` will be a token-contract inside the `tokenReceived` function. It may be important to filter which tokens were sent (by token-contract address). The token sender (the person who initiated the token transaction) will be `_from` inside the `tokenReceived` function. The `tokenReceived` function must return `0x8943ec02` after handling an incoming token transfer. The `tokenReceived` function call can be handled by the fallback function of the recipient contact (and in this case it may not return the magic value 0x8943ec02)."},{"id":"decimal-display-scaling","constraint":"The reference implementation illustrates display scaling using token decimals; raw balances stay in base units.","appliesTo":"application","normativity":"security-considerations","specSection":"Security Considerations > Reference Implementation","specUrl":"https://ercs.ethereum.org/ERCS/erc-223#reference-implementation","designImplication":"Show the declared precision beside formatted balances and preserve raw-unit or unknown states when decimals is absent.","specQuote":"* @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5,05` (`505 / 10 ** 2`)."}],"sources":[{"label":"ERC-223 official specification","url":"https://ercs.ethereum.org/ERCS/erc-223","type":"official-spec"}],"lastReviewed":"2026-09-18"},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-223","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-223","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-223","markdown":"https://www.eipsfordesigners.com/standards/ERC-223/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-223/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-223","official":"https://ercs.ethereum.org/ERCS/erc-223","discussion":"https://ethereum-magicians.org/search?q=ERC-223"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}