ERC-1363

Extension for ERC-20 tokens that supports executing callbacks on receiver contracts.

What is ERC-1363?

ERC-1363 is an extension interface for ERC-20 tokens that supports executing code on a recipient contract after transfers, or code on a spender contract after approvals, in a single transaction.


Abstract

The following standard allows for the implementation of a standard API for tokens interaction with smart contracts after transfer, transferFrom or approve. This standard provides basic functionality to transfer tokens, as well as allow tokens to be approved so they can be spent by another on-chain third party, and then make a callback on the receiver or spender contract.

The following are functions and callbacks introduced by this EIP:

  • transferAndCall and transferFromAndCall will call an onTransferReceived on a ERC1363Receiver contract.
  • approveAndCall will call an onApprovalReceived on a ERC1363Spender contract.

Motivation

There is no way to execute code on a receiver/spender contract after an ERC-20 transfer, transferFrom or approve so, to perform an action, it is required to send another transaction. This introduces complexity in UI development and friction on adoption as users must wait for the first transaction to be executed and then submit the second one. They must also pay GAS twice.

This proposal aims to make tokens capable of performing actions more easily and working without the use of any off-chain listener. It allows to make a callback on a receiver/spender contract, after a transfer or an approval, in a single transaction.

ERC-1363 tokens can be used for specific utilities in all cases that require a callback to be executed after a transfer or an approval received. ERC-1363 is also useful for avoiding token loss or token locking in contracts by verifying the recipient contract's ability to handle tokens.

Specification

Smart contracts implementing the ERC-1363 standard MUST implement all of the functions in the ERC1363 interface, as well as the ERC20 and ERC165 interfaces.

pragma solidity ^0.8.0;

/**
 * @title ERC1363
 * @dev An extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface ERC1363 is ERC20, ERC165 {
  /*
   * NOTE: the ERC-165 identifier for this interface is 0xb0202a11.
   * 0xb0202a11 ===
   *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
   *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
   *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
   *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
   *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
   *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
   */

  /**
   * @dev Moves a `value` amount of tokens from the caller's account to `to`
   * and then calls `ERC1363Receiver::onTransferReceived` on `to`.
   * @param to The address to which tokens are being transferred.
   * @param value The amount of tokens to be transferred.
   * @return A boolean value indicating the operation succeeded unless throwing.
   */
  function transferAndCall(address to, uint256 value) external returns (bool);

  /**
   * @dev Moves a `value` amount of tokens from the caller's account to `to`
   * and then calls `ERC1363Receiver::onTransferReceived` on `to`.
   * @param to The address to which tokens are being transferred.
   * @param value The amount of tokens to be transferred.
   * @param data Additional data with no specified format, sent in call to `to`.
   * @return A boolean value indicating the operation succeeded unless throwing.
   */
  function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

  /**
   * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
   * and then calls `ERC1363Receiver::onTransferReceived` on `to`.
   * @param from The address from which to send tokens.
   * @param to The address to which tokens are being transferred.
   * @param value The amount of tokens to be transferred.
   * @return A boolean value indicating the operation succeeded unless throwing.
   */
  function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

  /**
   * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
   * and then calls `ERC1363Receiver::onTransferReceived` on `to`.
   * @param from The address from which to send tokens.
   * @param to The address to which tokens are being transferred.
   * @param value The amount of tokens to be transferred.
   * @param data Additional data with no specified format, sent in call to `to`.
   * @return A boolean value indicating the operation succeeded unless throwing.
   */
  function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

  /**
   * @dev Sets a `value` amount of tokens as the allowance of `spender` over the caller's tokens
   * and then calls `ERC1363Spender::onApprovalReceived` on `spender`.
   * @param spender The address which will spend the funds.
   * @param value The amount of tokens to be spent.
   * @return A boolean value indicating the operation succeeded unless throwing.
   */
  function approveAndCall(address spender, uint256 value) external returns (bool);

  /**
   * @dev Sets a `value` amount of tokens as the allowance of `spender` over the caller's tokens
   * and then calls `ERC1363Spender::onApprovalReceived` on `spender`.
   * @param spender The address which will spend the funds.
   * @param value The amount of tokens to be spent.
   * @param data Additional data with no specified format, sent in call to `spender`.
   * @return A boolean value indicating the operation succeeded unless throwing.
   */
  function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

interface ERC20 {
  event Transfer(address indexed from, address indexed to, uint256 value);
  event Approval(address indexed owner, address indexed spender, uint256 value);
  function transfer(address to, uint256 value) external returns (bool);
  function transferFrom(address from, address to, uint256 value) external returns (bool);
  function approve(address spender, uint256 value) external returns (bool);
  function totalSupply() external view returns (uint256);
  function balanceOf(address account) external view returns (uint256);
  function allowance(address owner, address spender) external view returns (uint256);
}

interface ERC165 {
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

A contract that wants to accept ERC-1363 tokens via transferAndCall or transferFromAndCall MUST implement the ERC1363Receiver interface:

/**
 * @title ERC1363Receiver
 * @dev Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall` from ERC-1363 token contracts.
 */
interface ERC1363Receiver {
  /**
   * @dev Whenever ERC-1363 tokens are transferred to this contract via `ERC1363::transferAndCall` or `ERC1363::transferFromAndCall`
   * by `operator` from `from`, this function is called.
   *
   * NOTE: To accept the transfer, this must return
   * `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))`
   * (i.e. 0x88a7ca5c, or its own function selector).
   *
   * @param operator The address which called `transferAndCall` or `transferFromAndCall` function.
   * @param from The address which are tokens transferred from.
   * @param value The amount of tokens transferred.
   * @param data Additional data with no specified format.
   * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` if transfer is allowed unless throwing.
   */
  function onTransferReceived(address operator, address from, uint256 value, bytes calldata data) external returns (bytes4);
}

A contract that wants to accept ERC-1363 tokens via approveAndCall MUST implement the ERC1363Spender interface:

/**
 * @title ERC1363Spender
 * @dev Interface for any contract that wants to support `approveAndCall` from ERC-1363 token contracts.
 */
interface ERC1363Spender {
  /**
   * @dev Whenever an ERC-1363 tokens `owner` approves this contract via `ERC1363::approveAndCall`
   * to spend their tokens, this function is called.
   *
   * NOTE: To accept the approval, this must return
   * `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`
   * (i.e. 0x7b04a2d0, or its own function selector).
   *
   * @param owner The address which called `approveAndCall` function and previously owned the tokens.
   * @param value The amount of tokens to be spent.
   * @param data Additional data with no specified format.
   * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))` if approval is allowed unless throwing.
   */
  function onApprovalReceived(address owner, uint256 value, bytes calldata data) external returns (bytes4);
}

Rationale

There are many proposed uses of Ethereum smart contracts that can accept ERC-20 callbacks.

Examples could be:

  • creating a token payable crowdsale
  • selling services for tokens
  • paying invoices
  • making subscriptions

For these reasons it was originally named "Payable Token".

The choice to use transferAndCall, transferFromAndCall and approveAndCall derives from the ERC-20 naming. They want to highlight that they have the same behaviors of transfer, transferFrom and approve with the addition of a callback on receiver or spender contracts.

ERC1363Receiver and ERC1363Spender were inspired by the ERC-721 ERC721TokenReceiver behavior.

Backwards Compatibility

Unlike other ERC-20 extension proposals, ERC-1363 doesn't override the ERC-20 transfer and transferFrom methods and defines the interfaces IDs to be implemented maintaining backward compatibility with ERC-20.

Security Considerations

The approveAndCall and transferFromAndCall methods can be affected by the same issue of the standard ERC-20 approve and transferFrom method. Changing an allowance with the approveAndCall methods brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering.

One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards.

Discover more about ERC-1363

Create powerful tokens. Build modern DAPPs.