Skip to content

ERC 1594: Core Security Token Standard #1594

Closed
@adamdossa

Description

@adamdossa

eip: ERC-1594
title: Core Security Token Standard (part of the ERC-1400 Security Token Standards)
author: Adam Dossa (@adamdossa), Pablo Ruiz (@pabloruiz55), Fabian Vogelsteller (@frozeman), Stephane Gosselin (@thegostep)
discussions-to: #1411
status: Draft
type: Standards Track
category: ERC
created: 2018-09-09
require: ERC-20 (#20), ERC-1066 (#1066)


Simple Summary

This standard sits under the ERC-1400 (#1411) umbrella set of standards related to security tokens.

Provides a standard to support off-chain injection of data into transfers / issuance / redemption and the ability to check the validity of a transfer distinct from it's execution.

Abstract

Incorporates error signalling, off-chain data injection and issuance / redemption semantics.

This standard inherits from ERC-20 (#20) and could be easily extended to meet the ERC-777 (#777) standard if needed.

Motivation

Accelerate the issuance and management of securities on the Ethereum blockchain by specifying a standard interface through which security tokens can be operated on and interrogated by all relevant parties.

Security tokens differ materially from other token use-cases, with more complex interactions between off-chain and on-chain actors, and considerable regulatory scrutiny.

The ability to provide data (e.g. signed authorisation) alongside transfer, issuance and redemption functions allows security tokens to more flexibly implement transfer restrictions without depending on on-chain whitelists exclusively.

Using ERC-1066 (#1066) to provide reason codes as to why a transfer would fail, without requiring a user to actually try and execute a transfer, allows for improved UX and potentially saves gas on what would otherwise be failed transfers.

Formalising issuance and redemption semantics (similar to minting / burning) provides visibility into the total supply of the token and how it has changed over time.

Requirements

See ERC-1400 (#1411) for a full set of requirements across the library of standards.

The following requirements have been compiled following discussions with parties across the Security Token ecosystem.

  • MUST have a standard interface to query if a transfer would be successful and return a reason for failure.
  • MUST emit standard events for issuance and redemption.
  • MAY require signed data to be passed into a transfer transaction in order to validate it on-chain.
  • SHOULD NOT restrict the range of asset classes across jurisdictions which can be represented.
  • MUST be ERC-20 (ERC: Token standard #20) compatible.
  • COULD be ERC-777 (ERC777 Token Standard #777) compatible.

Rationale

Transfer Restrictions

Transfers of securities can fail for a variety of reasons in contrast to utility tokens which generally only require the sender to have a sufficient balance.

These conditions could be related to metadata of the securities being transferred (i.e. whether they are subject to a lock-up period), the identity of the sender and receiver of the securities (i.e. whether they have been through a KYC process, whether they are accredited or an affiliate of the issuer) or for reasons unrelated to the specific transfer but instead set at the token level (i.e. the token contract enforces a maximum number of investors or a cap on the percentage held by any single investor).

For ERC-20 / ERC-777 tokens, the balanceOf and allowance functions provide a way to check that a transfer is likely to succeed before executing the transfer, which can be executed both on and off-chain.

For tokens representing securities the standard introduces a function canTransfer which provides a more general purpose way to achieve this when the reasons for failure are more complex; and a function of the whole transfer (i.e. includes any data sent with the transfer and the receiver of the securities).

In order to support off-chain data inputs to transfer functions, transfer functions are extended to transferWithData / transferFromWithData which can optionally take an additional bytes _data parameter.

In order to provide a richer result than just true or false, a byte return code is returned. This allows us to give a reason for why the transfer failed, or at least which category of reason the failure was in. The ability to query documents and the expected success of a transfer is included in Security Token section.

Specification

Restricted Transfers

canTransfer / canTransferFrom

Transfers of securities may fail for a number of reasons, for example relating to:

  • the identity of the sender or receiver of the tokens
  • limits placed on the specific tokens being transferred (i.e. lockups on certain quantities of token)
  • limits related to the overall state of the token (i.e. total number of investors)

The standard provides an on-chain function to determine whether a transfer will succeed, and return details indicating the reason if the transfer is not valid.

These rules can either be defined using smart contracts and on-chain data, or rely on _data passed as part of the transferWithData function which could represent authorisation for the transfer (e.g. a signed message by a transfer agent attesting to the validity of this specific transfer).

The function will return both a ESC (Ethereum Status Code) following the EIP-1066 standard, and an additional bytes32 parameter that can be used to define application specific reason codes with additional details (for example the transfer restriction rule responsible for making the transfer operation invalid).

If bytes _data is empty, then this corresponds to a check on whether a transfer (or transferFrom) request will succeed, if bytes _data is populated, then this corresponds to a check on transferWithData (or transferFromWithData) will succeed.

canTransfer assumes the sender of tokens is msg.sender and will be executed via transfer or transferWithData whereas canTransferFrom allows the specification of the sender of tokens and that the transfer will be executed via transferFrom or transferFromWithData.

function canTransfer(address _to, uint256 _value, bytes _data) external view returns (byte, bytes32);
function canTransferFrom(address _from, address _to, uint256 _value, bytes _data) external view returns (byte, bytes32);

transferWithData

Transfer restrictions can take many forms and typically involve on-chain rules or whitelists. However for many types of approved transfers, maintaining an on-chain list of approved transfers can be cumbersome and expensive. An alternative is the co-signing approach, where in addition to the token holder approving a token transfer, and authorised entity provides signed data which further validates the transfer.

The bytes _data allows arbitrary data to be submitted alongside the transfer, for the token contract to interpret or record. This could be signed data authorising the transfer (e.g. a dynamic whitelist) but is flexible enough to accomadate other use-cases.

transferWithData MUST emit a Transfer event with details of the transfer.

function transferWithData(address _to, uint256 _value, bytes _data) external;

transferFromWithData

This is the analogy to the transferWithData function.

msg.sender MUST have a sufficient allowance set and this allowance must be debited by the _value.

function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) external;

Token Issuance

isIssuable

A security token issuer can specify that issuance has finished for the token (i.e. no new tokens can be minted or issued).

If a token returns FALSE for isIssuable() then it MUST always return FALSE in the future.

If a token returns FALSE for isIssuable() then it MUST never allow additional tokens to be issued.

function isIssuable() external view returns (bool);

issue

This function must be called to increase the total supply.

The bytes _data parameter can be used to inject off-chain data (e.g. signed data) to authorise or authenticate the issuance and receiver of issued tokens.

When called, this function MUST emit the Issued event.

function issue(address _tokenHolder, uint256 _value, bytes _data) external;

Token Redemption

redeem

Allows a token holder to redeem tokens.

The redeemed tokens must be subtracted from the total supply and the balance of the token holder. The token redemption should act like sending tokens and be subject to the same conditions.

The Redeemed event MUST be emitted every time this function is called.

As with transferWithData this function has a bytes _data parameter that can be used in the token contract to authenticate the redemption.

function redeem(uint256 _value, bytes _data) external;

redeemFrom

This is the analogy to the redeem function.

msg.sender MUST have a sufficient allowance set and this allowance must be debited by the _value.

The Redeemed event MUST be emitted every time this function is called.

function redeemFrom(address _tokenHolder, uint256 _value, bytes _data) external;

Interface

/// @title IERC1594 Security Token Standard
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec

interface IERC1594 is IERC20 {

    // Transfers
    function transferWithData(address _to, uint256 _value, bytes _data) external;
    function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) external;

    // Token Issuance
    function isIssuable() external view returns (bool);
    function issue(address _tokenHolder, uint256 _value, bytes _data) external;

    // Token Redemption
    function redeem(uint256 _value, bytes _data) external;
    function redeemFrom(address _tokenHolder, uint256 _value, bytes _data) external;

    // Transfer Validity
    function canTransfer(address _to, uint256 _value, bytes _data) external view returns (bool, byte, bytes32);
    function canTransferFrom(address _from, address _to, uint256 _value, bytes _data) external view returns (bool, byte, bytes32);

    // Issuance / Redemption Events
    event Issued(address indexed _operator, address indexed _to, uint256 _value, bytes _data);
    event Redeemed(address indexed _operator, address indexed _from, uint256 _value, bytes _data);

}

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions