Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ERC-1363 Payable Token #2496

Merged
merged 2 commits into from
Apr 26, 2020
Merged

ERC-1363 Payable Token #2496

merged 2 commits into from
Apr 26, 2020

Conversation

vittominacori
Copy link
Contributor

@vittominacori vittominacori commented Jan 30, 2020

eip: 1363
title: ERC-1363 Payable Token
author: Vittorio Minacori (@vittominacori)
discussions-to: https://github.com/ethereum/eips/issues/1363
status: Final
type: Standards Track
category: ERC
created: 2020-01-30
requires: 20, 165

Simple Summary

Description of a Payable Token compatible with the ERC-20 definition.
Also description of a Token Receiver and/or Spender that accepts Payable Token payments.

Closes #1363

Abstract

The following describes standard functions a token contract and contracts working with specified token can implement to make a Token Payable.

Motivation

This proposal allows to implement an ERC-20 compatible token that can be used for payments.

It defines transferAndCall and transferFromAndCall functions that will call an onTransferReceived on a ERC1363Receiver contract.
It defines approveAndCall functions that will call an onApprovalReceived on a ERC1363Spender contract.

This proposal is inspired by the ERC-721 onERC721Received and ERC721TokenReceiver behaviours.

Specification

Every Payable Token compliant contract MUST implement the ERC-1363 interface other than ERC-20 and ERC-165 interfaces.

pragma solidity ^0.6.0;


interface ERC1363 /* is ERC20, ERC165 */ {
  /*
   * Note: the ERC-165 identifier for this interface is 0x4bbee2df.
   * 0x4bbee2df ===
   *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
   *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
   *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
   *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)'))
   */

  /*
   * Note: the ERC-165 identifier for this interface is 0xfb9ec8ce.
   * 0xfb9ec8ce ===
   *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
   *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
   */

  /**
   * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
   * @param to address The address which you want to transfer to
   * @param value uint256 The amount of tokens to be transferred
   * @return true unless throwing
   */
  function transferAndCall(address to, uint256 value) external returns (bool);

  /**
   * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
   * @param to address The address which you want to transfer to
   * @param value uint256 The amount of tokens to be transferred
   * @param data bytes Additional data with no specified format, sent in call to `to`
   * @return true unless throwing
   */
  function transferAndCall(address to, uint256 value, bytes memory data) external returns (bool);

  /**
   * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver
   * @param from address The address which you want to send tokens from
   * @param to address The address which you want to transfer to
   * @param value uint256 The amount of tokens to be transferred
   * @return true unless throwing
   */
  function transferFromAndCall(address from, address to, uint256 value) external returns (bool);


  /**
   * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver
   * @param from address The address which you want to send tokens from
   * @param to address The address which you want to transfer to
   * @param value uint256 The amount of tokens to be transferred
   * @param data bytes Additional data with no specified format, sent in call to `to`
   * @return true unless throwing
   */
  function transferFromAndCall(address from, address to, uint256 value, bytes memory data) external returns (bool);

  /**
   * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
   * and then call `onApprovalReceived` on spender.
   * Beware that changing an allowance with this method 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:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param spender address The address which will spend the funds
   * @param value uint256 The amount of tokens to be spent
   */
  function approveAndCall(address spender, uint256 value) external returns (bool);

  /**
   * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
   * and then call `onApprovalReceived` on spender.
   * Beware that changing an allowance with this method 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:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param spender address The address which will spend the funds
   * @param value uint256 The amount of tokens to be spent
   * @param data bytes Additional data with no specified format, sent in call to `spender`
   */
  function approveAndCall(address spender, uint256 value, bytes memory data) external returns (bool);
}

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

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

A contract that wants to accept token payments via transferAndCall or transferFromAndCall MUST implement the following interface:

/**
 * @title ERC1363Receiver interface
 * @dev Interface for any contract that wants to support transferAndCall or transferFromAndCall
 *  from ERC1363 token contracts.
 */
interface ERC1363Receiver {
  /*
   * Note: the ERC-165 identifier for this interface is 0x88a7ca5c.
   * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
   */

  /**
   * @notice Handle the receipt of ERC1363 tokens
   * @dev Any ERC1363 smart contract calls this function on the recipient
   * after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the
   * transfer. Return of other than the magic value MUST result in the
   * transaction being reverted.
   * Note: the token contract address is always the message sender.
   * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function
   * @param from address The address which are token transferred from
   * @param value uint256 The amount of tokens transferred
   * @param data bytes Additional data with no specified format
   * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))`
   *  unless throwing
   */
  function onTransferReceived(address operator, address from, uint256 value, bytes memory data) external returns (bytes4);
}

A contract that wants to accept token payments via approveAndCall MUST implement the following interface:

/**
 * @title ERC1363Spender interface
 * @dev Interface for any contract that wants to support approveAndCall
 *  from ERC1363 token contracts.
 */
interface ERC1363Spender {
  /*
   * Note: the ERC-165 identifier for this interface is 0x7b04a2d0.
   * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))
   */

  /**
   * @notice Handle the approval of ERC1363 tokens
   * @dev Any ERC1363 smart contract calls this function on the recipient
   * after an `approve`. This function MAY throw to revert and reject the
   * approval. Return of other than the magic value MUST result in the
   * transaction being reverted.
   * Note: the token contract address is always the message sender.
   * @param owner address The address which called `approveAndCall` function
   * @param value uint256 The amount of tokens to be spent
   * @param data bytes Additional data with no specified format
   * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`
   *  unless throwing
   */
  function onApprovalReceived(address owner, uint256 value, bytes memory data) external returns (bytes4);
}

Rationale

There are many proposed uses of Ethereum smart contracts that can accept ERC-20 payments (i.e. to create a token payable crowdsale, selling services for tokens, paying invoices, making subscriptions, use them for a specific utility and many other purposes).

This proposal wants to make token payments easier and working without the use of any other listener. It also allows to make a callback after a transfer in a single transaction.

Backwards Compatibility

This proposal is inspired also by ERC-223 and ERC-677 but it uses the ERC-721 approach so it doesn't override the ERC-20 transfer and transferFrom methods and defines the interfaces IDs to be implemented maintaining the ERC-20 backwards compatibility.

Test Cases

https://github.com/vittominacori/erc1363-payable-token/tree/master/test

Implementation

https://github.com/vittominacori/erc1363-payable-token/tree/master/contracts/token/ERC1363

Copyright

Copyright and related rights waived via CC0.

@gcolvin gcolvin merged commit 4c1bf4d into ethereum:master Apr 26, 2020
pizzarob pushed a commit to pizzarob/EIPs that referenced this pull request Jun 12, 2020
* Add EIP 1363

* Update to solidity 0.6 and use external instead of public
tkstanczak pushed a commit to tkstanczak/EIPs that referenced this pull request Nov 7, 2020
* Add EIP 1363

* Update to solidity 0.6 and use external instead of public
Arachnid pushed a commit to Arachnid/EIPs that referenced this pull request Mar 6, 2021
* Add EIP 1363

* Update to solidity 0.6 and use external instead of public
@lukehutch
Copy link

@gcolvin When calling transferFromAndCall, the allowance amount is decreased by the transfer amount. It seems like the spender should be notified of the new allowance amount in this situation, by calling ERC1363Spender.onApprovalReceived (although if the spender does not implement this interface, this call should not cause the transaction to fail).

@vittominacori
Copy link
Contributor Author

@lukehutch transferFromAndCall must have the standard transferFrom behavior. Then the receiver must be notified by onTransferReceived.

onApprovalReceived must be used to notify the spender after an approveAndCall.

@lukehutch
Copy link

@vittominacori Yes, I understand what the spec says. But what I'm saying is that transferFrom[AndCall] modifies the approval amount, just as does approve[AndCall]. So the spender should be notified of that approval amount too, when transferFrom[AndCall] is called, just as the spender is notified when approve[AndCall] is called. Otherwise the spender will not be notified when the approval decreases due to a successful transferFrom[AndCall].

@vittominacori
Copy link
Contributor Author

vittominacori commented Jun 1, 2022

@lukehutch no because after a transferFromAndCall the receiver/spender really receive the amount so must happen the onTransferReceived. The approveAndCall instead just edit the allowance so the spender must take action to transfer the approved tokens.

Check that implementation for samples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ERC-1363 Payable Token
3 participants