Blockchain
Dapp
ERC1404

Safe Ether: A DApp with ERC1404 implementation

こんにちは、JP です。In this article、we will be discussing a simple implementation of ERC1404. Let's get started.


ERC1404

Basically, ERC1404 extends ERC20 interface but with two additional functions.


Specification

ERC20 token features:

contract ERC20 {

function totalSupply() public view returns (uint256);
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
}

ERC1404 extends ERC20 interface, adding two functions:

contract ERC1404 is ERC20 {

function detectTransferRestriction (address from, address to, uint256 value) public view returns (uint8);
function messageForTransferRestriction (uint8 restrictionCode) public view returns (string);
}

These are detectTransferRestriction and messageForTransferRestriction. It is up to the issuer how the restrictions in detectTransferRestriction are evaluated inside transfer and transferFrom functions. If, inside these transfer methods, detectTransferRestriction returns a value other than 0, the transaction should be reverted. Also, messageForTransferRestriction function is a function for a human-readable message as to why a transaction is restricted to effectively report errors to users.


自分のERC1404のImplementation

image.png

これはSafe Etherです。このDAppはERC1404の簡単な実装です。 Basically, when a user is included in the blacklist, transfer of tokens will not be successful.


コード

This is the code for the ERC1404 Implementation. First, a token should be created and the tokens will be used for the security transfers.

pragma solidity ^0.4.24;

import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";

contract ERC1404 is ERC20 {
/// @notice Detects if a transfer will be reverted and if so returns an appropriate reference code
/// @param from Sending address
/// @param to Receiving address
/// @param value Amount of tokens being transferred
/// @return Code by which to reference message for rejection reasoning
/// @dev Overwrite with your custom transfer restriction logic
function detectTransferRestriction (address from, address to, uint256 value) public view returns (uint8 restrictionCode);

/// @notice Returns a human-readable message for a given restriction code
/// @param restrictionCode Identifier for looking up a message
/// @return Text showing the restriction's reasoning
/// @dev Overwrite with your custom message and restrictionCode handling
function messageForTransferRestriction (uint8 restrictionCode) public view returns (string message);
}

contract JPToken is ERC1404 {

string public name = "JPToken";
string public symbol = "JPS";
uint public decimal = 18;
uint8 public constant SUCCESS_CODE = 0;
uint8 public constant ZERO_ADDRESS_RESTRICTION_CODE = 1;
string public constant UNKNOWN_MESSAGE = "UNKNOWN";
string public constant SUCCESS_MESSAGE = "SUCCESS";
string public constant ZERO_ADDRESS_RESTRICTION_MESSAGE = "ILLEGAL_TRANSFER_TO_ZERO_ADDRESS";

mapping (address => uint256) private blacklist;

constructor () public{
_mint(msg.sender,10000000000);
}

function addToBlacklist (address _user) public {
blacklist[_user] = 1;
}

function removeToBlacklist (address _user) public {
blacklist[_user] = 0;
}

function isUserBlacklisted(address _user) public view returns (uint256) {
return blacklist[_user];
}

/// @notice Checks if a transfer is restricted, reverts if it is
/// @param from Sending address
/// @param to Receiving address
/// @param value Amount of tokens being transferred
/// @dev Defining this modifier is not required by the standard, using detectTransferRestriction and appropriately emitting TransferRestricted is however
modifier notRestricted (address from, address to, uint256 value) {
uint8 restrictionCode = detectTransferRestriction(from, to, value);
require(blacklist[from] == 0 && blacklist[to] == 0);
require(restrictionCode == SUCCESS_CODE, messageForTransferRestriction(restrictionCode));
_;
}

/// @notice Detects if a transfer will be reverted and if so returns an appropriate reference code
/// @param from Sending address
/// @param to Receiving address
/// @param value Amount of tokens being transferred
/// @return Code by which to reference message for rejection reasoning
/// @dev Overwrite with your custom transfer restriction logic
function detectTransferRestriction (address from, address to, uint256 value)
public view returns (uint8 restrictionCode)
{
restrictionCode = SUCCESS_CODE; // success
if (to == address(0x0)) {
restrictionCode = ZERO_ADDRESS_RESTRICTION_CODE; // illegal transfer to zero address
}
}

/// @notice Returns a human-readable message for a given restriction code
/// @param restrictionCode Identifier for looking up a message
/// @return Text showing the restriction's reasoning
/// @dev Overwrite with your custom message and restrictionCode handling
function messageForTransferRestriction (uint8 restrictionCode)
public view returns (string message)
{
message = UNKNOWN_MESSAGE;
if (restrictionCode == SUCCESS_CODE) {
message = SUCCESS_MESSAGE;
} else if (restrictionCode == ZERO_ADDRESS_RESTRICTION_CODE) {
message = ZERO_ADDRESS_RESTRICTION_MESSAGE;
}
}

/// @notice Subclass implementation of StandardToken's ERC20 transfer method
/// @param to Receiving address
/// @param value Amount of tokens being transferred
/// @return Transfer success status
/// @dev Must compare the return value of detectTransferRestriction to 0
function transfer (address to, uint256 value)
public notRestricted(msg.sender, to, value) returns (bool)
{
return super.transfer(to, value);
}

/// @notice Subclass implementation of StandardToken's ERC20 transferFrom method
/// @param from Sending address
/// @param to Receiving address
/// @param value Amount of tokens being transferred
/// @return Transfer success status
/// @dev Must compare the return value of detectTransferRestriction to 0
function transferFrom (address from, address to, uint256 value)
public notRestricted(from, to, value) returns (bool)
{
return super.transferFrom(from, to, value);
}
}

This code shows how a user becomes a restricted user。A value 1 is assigned for the user who is blacklisted or a restricted user. A value 0 will be assigned for the user who is removed from blacklist.

    function addToBlacklist (address _user) public {

blacklist[_user] = 1;
}

function removeToBlacklist (address _user) public {
blacklist[_user] = 0;
}

In this code, the restriction for blacklist is included. If the value for blacklist from and to is 0, the address is whitelisted and is not restricted. It can successfully transfer and receive tokens.

    modifier notRestricted (address from, address to, uint256 value) {

uint8 restrictionCode = detectTransferRestriction(from, to, value);
require(blacklist[from] == 0 && blacklist[to] == 0);
require(restrictionCode == SUCCESS_CODE, messageForTransferRestriction(restrictionCode));
_;
}


Conclusion

In this article, we learned a simple implementation of ERC1404 where a token is created first and a restriction is defined for transferring tokens. ERC1404 is built in the ERC20 interface with an additional two functions which are detectTransferRestriction to detect if user is restricted and messageForTransferRestriction to show a message as to why a transaction is restricted.

ありがとうございました。


Reference

https://github.com/ethereum/EIPs/issues/1404