forked from DecentralizedClimateFoundation/DCIPs
209 lines
9.8 KiB
Markdown
209 lines
9.8 KiB
Markdown
---
|
|
eip: 1363
|
|
title: 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-31
|
|
requires: 20, 165
|
|
---
|
|
|
|
## Simple Summary
|
|
Defines a token interface for [ERC-20](./eip-20.md) tokens that supports executing recipient code after `transfer` or `transferFrom`, or spender code after `approve`.
|
|
|
|
## Abstract
|
|
Standard functions a token contract and contracts working with tokens can implement to make a token Payable.
|
|
|
|
`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 after a [ERC-20](./eip-20.md) transfer or approval (i.e. making a payment), so to make an action it is required to send another transaction and pay GAS twice.
|
|
|
|
This proposal wants to make token payments easier and working without the use of any other listener. It allows to make a callback after a transfer or approval in a single transaction.
|
|
|
|
There are many proposed uses of Ethereum smart contracts that can accept [ERC-20](./eip-20.md) payments.
|
|
|
|
Examples could be
|
|
* to create a token payable crowdsale
|
|
* selling services for tokens
|
|
* paying invoices
|
|
* making subscriptions
|
|
|
|
For these reasons it was named as **"Payable Token"**.
|
|
|
|
Anyway you can use it for specific utilities or for any other purposes who require the execution of a callback after a transfer or approval received.
|
|
|
|
This proposal has been inspired by the [ERC-721](./eip-721.md) `onERC721Received` and `ERC721TokenReceiver` behaviours.
|
|
|
|
## Specification
|
|
Implementing contracts **MUST** implement the [ERC-1363](./eip-1363.md) interface as well as the [ERC-20](./eip-20.md) and [ERC-165](./eip-165.md) interfaces.
|
|
|
|
```solidity
|
|
pragma solidity ^0.8.0;
|
|
|
|
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)'))
|
|
*/
|
|
|
|
/**
|
|
* @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.
|
|
* @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.
|
|
* @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:
|
|
|
|
```solidity
|
|
/**
|
|
* @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:
|
|
|
|
```solidity
|
|
/**
|
|
* @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
|
|
The choice to use `transferAndCall`, `transferFromAndCall` and `approveAndCall` derives from the [ERC-20](./eip-20.md) naming. They want to highlight that they have the same behaviours of `transfer`, `transferFrom` and `approve` with the addition of a callback on receiver or spender.
|
|
|
|
## Backwards Compatibility
|
|
This proposal has been inspired also by [ERC-223](https://github.com/ethereum/EIPs/issues/223) and [ERC-677](https://github.com/ethereum/EIPs/issues/677) but it uses the [ERC-721](./eip-721.md) approach, so it doesn't override the [ERC-20](./eip-20.md) `transfer` and `transferFrom` methods and defines the interfaces IDs to be implemented maintaining the [ERC-20](./eip-20.md) backwards compatibility.
|
|
|
|
## Security Considerations
|
|
The `approveAndCall` and `transferFromAndCall` methods can be affected by the same issue of the standard [ERC-20](./eip-20.md) `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 ([EIP-20#issuecomment-263524729](https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729)).
|
|
|
|
## Copyright
|
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|