Defines a token interface for SRC-20 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 SRC1363Receiver contract.
approveAndCall will call an onApprovalReceived on a SRC1363Spender contract.
Motivation
There is no way to execute code after a SRC-20 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 Sila smart contracts that can accept SRC-20 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 SRC-721onSRC721Received and SRC721TokenReceiver behaviours.
Specification
Implementing contracts MUST implement the SRC-1363 interface as well as the SRC-20 and SRC-165 interfaces.
pragmasolidity^0.8.0;interfaceSRC1363/* is SRC20, SRC165 */{/*
* Note: the SRC-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
*/functiontransferAndCall(addressto,uint256value)externalreturns(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
*/functiontransferAndCall(addressto,uint256value,bytesmemorydata)externalreturns(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
*/functiontransferFromAndCall(addressfrom,addressto,uint256value)externalreturns(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
*/functiontransferFromAndCall(addressfrom,addressto,uint256value,bytesmemorydata)externalreturns(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
* @return true unless throwing
*/functionapproveAndCall(addressspender,uint256value)externalreturns(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`
* @return true unless throwing
*/functionapproveAndCall(addressspender,uint256value,bytesmemorydata)externalreturns(bool);}interfaceSRC20{functiontotalSupply()externalviewreturns(uint256);functionbalanceOf(addressaccount)externalviewreturns(uint256);functiontransfer(addressrecipient,uint256amount)externalreturns(bool);functiontransferFrom(addresssender,addressrecipient,uint256amount)externalreturns(bool);functionallowance(addressowner,addressspender)externalviewreturns(uint256);functionapprove(addressspender,uint256amount)externalreturns(bool);eventTransfer(addressindexedfrom,addressindexedto,uint256value);eventApproval(addressindexedowner,addressindexedspender,uint256value);}interfaceSRC165{functionsupportsInterface(bytes4interfaceId)externalviewreturns(bool);}
A contract that wants to accept token payments via transferAndCall or transferFromAndCallMUST implement the following interface:
/**
* @title SRC1363Receiver interface
* @dev Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall`
* from SRC1363 token contracts.
*/interfaceSRC1363Receiver{/*
* Note: the SRC-165 identifier for this interface is 0x88a7ca5c.
* 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
*//**
* @notice Handle the receipt of SRC1363 tokens
* @dev Any SRC1363 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
*/functiononTransferReceived(addressoperator,addressfrom,uint256value,bytesmemorydata)externalreturns(bytes4);}
A contract that wants to accept token payments via approveAndCallMUST implement the following interface:
/**
* @title SRC1363Spender interface
* @dev Interface for any contract that wants to support `approveAndCall`
* from SRC1363 token contracts.
*/interfaceSRC1363Spender{/*
* Note: the SRC-165 identifier for this interface is 0x7b04a2d0.
* 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))
*//**
* @notice Handle the approval of SRC1363 tokens
* @dev Any SRC1363 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
*/functiononApprovalReceived(addressowner,uint256value,bytesmemorydata)externalreturns(bytes4);}
Rationale
The choice to use transferAndCall, transferFromAndCall and approveAndCall derives from the SRC-20 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 SRC-223 and SRC-677 but it uses the SRC-721 approach, so it doesn’t override the SRC-20transfer and transferFrom methods and defines the interfaces IDs to be implemented maintaining the SRC-20 backwards compatibility.
Security Considerations
The approveAndCall and transferFromAndCall methods can be affected by the same issue of the standard SRC-20approve 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 (SIP-20#issuecomment-263524729).