Skip to content

Latest commit

 

History

History
285 lines (198 loc) · 17.8 KB

eip-1400.md

File metadata and controls

285 lines (198 loc) · 17.8 KB
eip title author discussions-to status type category created require
ERC-1400
Security Token Standards
Adam Dossa (@adamdossa), Pablo Ruiz (@pabloruiz55), Fabian Vogelsteller (@frozeman), Stephane Gosselin (@thegostep)
Draft
Standards Track
ERC
2018-09-09
ERC-1410 (#1410), ERC-1594 (#1594), ERC-1644 (#1644), ERC-1643 (#1643), ERC-20 (#20), ERC-1066 (#1066)

Simple Summary

Represents a library of standards for security tokens on Ethereum.

In aggregate provides a suite of standard interfaces for issuing / redeeming security tokens, managing their ownership and transfer restrictions and providing transparency to token holders on how different subsets of their token balance behave with respect to transfer restrictions, rights and obligations.

Abstract

Standards should be backwards compatible with ERC-20 (#20) and easily extended to be compatible with ERC-777 (#777).

ERC-1410 (#1410): differentiated ownership / transparent restrictions ERC-1594 (#1594): on-chain restriction checking with error signalling, off-chain data injection for transfer restrictions and issuance / redemption semantics ERC-1643 (#1643): document / legend management ERC-1644 (#1644): controller operations (force transfer)

Motivation

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

Taken together, these security token standards provide document management, error signalling, gate keeper (operator) access control, off-chain data injection, issuance / redemption semantics and expose partially fungible subsets of a token holders balance.

Requirements

Moving the issuance, trading and lifecycle events of a security onto a public ledger requires having a standard way of modelling securities, their ownership and their properties on-chain.

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 be able to perform forced transfer for legal action or fund recovery.
  • MUST emit standard events for issuance and redemption.
  • MUST be able to attach metadata to a subset of a token holder's balance such as special shareholder rights or data for transfer restrictions.
  • MUST be able to modify metadata at time of transfer based on off-chain data, on-chain data and the parameters of the transfer.
  • MUST support querying and subscribing to updates on any relevant documentation for the security.
  • 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 compatible.
  • COULD be ERC-777 compatible.

Rationale

ERC-1594: Core Security Token Standard

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 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 / canTransferByPartition 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 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.

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.

ERC-1410: Partially Fungible Tokens

There are many types of securities which, although they represent the same underlying asset, need to have differentiating data tied to them.

This additional metadata implicitly renders these securities non-fungible, but in practice this data is usually applied to a subset of the security rather than an individual security. The ability to partition a token holder's balance into partitions, each with separate metadata is addressed in the Partially Fungible Token section.

For example a token holder's balance may be split in two: those tokens issued during the primary issuance, and those received through secondary trading.

Security token contracts can reference this metadata in order to apply additional logic to determine whether or not a transfer is valid, and determine the metadata that should be associated with the tokens once transferred into the receiver's balance.

Alternatively a security token can use this mechanism simply to be able to transparently display to investors how different subsets of their tokens will behave with respect to transfer restrictions. In this case, the balances could be determined programatically.

ERC-1643: Document Management Standard

Security tokens usually have documentation associated with them. This could be an offering document, legend details and so on.

Being able to set / remove and retrieve these documents, and having events associated with these actions allows investors to stay up to date with documentation on their investments.

This standard does not provide any way for an investor to signal on-chain that they have read, or agree, with any of these documents.

ERC-1644: Controller Token Operation Standard

Since security tokens are subject to regulatory and legal oversight (the details of which will vary depending on jurisdiction, regulatory framework and underlying asset) in many instances the issuer (or a party delegated to by the issuer acting as a controller, e.g. a regulator or transfer agent) will need to retain the ability to force transfer tokens between addresses.

These controller transfers should be transparent (emit events that flag this as a forced transfer) and the token contract itself should be explicit as to whether or not this is possible.

Examples of where this may be needed is to reverse fraudulent transactions, resolve lost private keys and responding to a court order.

Specification

This standard does not specify any additional functions, but references ERC-1410 (#1410), ERC-1594 (#1594), ERC-1643 (#1643) and ERC-1655 (#1644) as an underlying library of security token standards, each covering a different aspect of security token functionality.

In order to combine these two standards, the additional constraints are specified.

operatorTransferByPartition

If the token is controllable (isControllable returns TRUE) then the controller may use operatorTransferByPartition without being explicitly authorised by the token holder.

In this instance, the operatorTransferByPartition MUST also emit a ControllerTransfer event.

Correspondingly, if isControllable returns FALSE then the controller cannot call operatorTransferByPartition unless explicitly authorised by the token holder.

operatorRedeemByPartition

If the token is controllable (isControllable returns TRUE) then the controller may use operatorRedeemByPartition without being explicitly authorised by the token holder.

In this instance, the operatorRedeemByPartition MUST also emit a ControllerRedemption event.

Correspondingly, if isControllable returns FALSE then the controller cannot call operatorRedeemByPartition unless explicitly authorised by the token holder.

Default Partitions

In order for transfer and transferWithData to operate on partially fungible tokens, there needs to be some notion of default partitions that these functions apply to. The details for how these are determined (e.g. either a fixed list, dynamically, or using partitionsOf) is left as an implementation detail rather than defined as part of the standard.

When transferring tokens as part of a transfer or transferWithData operation, these transfers should respect the invariant of partially fungible tokens, namely that the sum of the balances across all partitions should equal to the total balance of a token holder.

Interface

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

interface IERC1400 is IERC20 {

  // Document Management
  function getDocument(bytes32 _name) external view returns (string, bytes32);
  function setDocument(bytes32 _name, string _uri, bytes32 _documentHash) external;

  // Token Information
  function balanceOfByPartition(bytes32 _partition, address _tokenHolder) external view returns (uint256);
  function partitionsOf(address _tokenHolder) external view returns (bytes32[]);

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

  // Partition Token Transfers
  function transferByPartition(bytes32 _partition, address _to, uint256 _value, bytes _data) external returns (bytes32);
  function operatorTransferByPartition(bytes32 _partition, address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external returns (bytes32);

  // Controller Operation
  function isControllable() external view returns (bool);
  function controllerTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external;
  function controllerRedeem(address _tokenHolder, uint256 _value, bytes _data, bytes _operatorData) external;

  // Operator Management
  function authorizeOperator(address _operator) external;
  function revokeOperator(address _operator) external;
  function authorizeOperatorByPartition(bytes32 _partition, address _operator) external;
  function revokeOperatorByPartition(bytes32 _partition, address _operator) external;

  // Operator Information
  function isOperator(address _operator, address _tokenHolder) external view returns (bool);
  function isOperatorForPartition(bytes32 _partition, address _operator, address _tokenHolder) external view returns (bool);

  // Token Issuance
  function isIssuable() external view returns (bool);
  function issue(address _tokenHolder, uint256 _value, bytes _data) external;
  function issueByPartition(bytes32 _partition, 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;
  function redeemByPartition(bytes32 _partition, uint256 _value, bytes _data) external;
  function operatorRedeemByPartition(bytes32 _partition, address _tokenHolder, uint256 _value, bytes _operatorData) external;

  // Transfer Validity
  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);
  function canTransferByPartition(address _from, address _to, bytes32 _partition, uint256 _value, bytes _data) external view returns (byte, bytes32, bytes32);    

  // Controller Events
  event ControllerTransfer(
      address _controller,
      address indexed _from,
      address indexed _to,
      uint256 _value,
      bytes _data,
      bytes _operatorData
  );

  event ControllerRedemption(
      address _controller,
      address indexed _tokenHolder,
      uint256 _value,
      bytes _data,
      bytes _operatorData
  );

  // Document Events
  event Document(bytes32 indexed _name, string _uri, bytes32 _documentHash);

  // Transfer Events
  event TransferByPartition(
      bytes32 indexed _fromPartition,
      address _operator,
      address indexed _from,
      address indexed _to,
      uint256 _value,
      bytes _data,
      bytes _operatorData
  );

  event ChangedPartition(
      bytes32 indexed _fromPartition,
      bytes32 indexed _toPartition,
      uint256 _value
  );

  // Operator Events
  event AuthorizedOperator(address indexed _operator, address indexed _tokenHolder);
  event RevokedOperator(address indexed _operator, address indexed _tokenHolder);
  event AuthorizedOperatorByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _tokenHolder);
  event RevokedOperatorByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _tokenHolder);

  // 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);
  event IssuedByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _to, uint256 _value, bytes _data, bytes _operatorData);
  event RedeemedByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _from, uint256 _value, bytes _operatorData);


}

Notes

On-chain vs. Off-chain Transfer Restrictions

The rules determining if a security token can be sent may be self-executing (e.g. a rule which limits the maximum number of investors in the security) or require off-chain inputs (e.g. an explicit broker approval for the trade). To facilitate the latter, the transferByPartition, transferWithData, transferFromWithData, canTransferByPartition and canTransfer functions accept an additional bytes _data parameter which can be signed by an approved party and used to validate a transfer.

The specification for this data is outside the scope of this standard and would be implementation specific.

Identity

Under many jurisdictions, whether a party is able to receive and send security tokens depends on the characteristics of the party's identity. For example, most jurisdictions require some level of KYC / AML process before a party is eligible to purchase or sell a particular security. Additionally, a party may be categorized into an investor qualification category (e.g. accredited investor, qualified purchaser), and their citizenship may also inform restrictions associated with their securities.

There are various identity standards (e.g. ERC-725 (#725), Civic, uPort) which can be used to capture the party's identity data, as well as other approaches which are centrally managed (e.g. maintaining a whitelist of addresses that have been approved from a KYC perspective). These identity standards have in common to key off an Ethereum address (which could be a party's wallet, or an identity contract), and as such the canTransfer function can use the address of both the sender and receiver of the security token as a proxy for identity in deciding if eligibility requirements are met.

Beyond this, the standard does not mandate any particular approach to identity.

Reason Codes

To improve the token holder experience, canTransfer MUST return a reason byte code on success or failure based on the EIP-1066 application-specific status codes specified below. An implementation can also return arbitrary data as a bytes32 to provide additional information not captured by the reason code.

Code Reason
0x50 transfer failure
0x51 transfer success
0x52 insufficient balance
0x53 insufficient allowance
0x54 transfers halted (contract paused)
0x55 funds locked (lockup period)
0x56 invalid sender
0x57 invalid receiver
0x58 invalid operator (transfer agent)
0x59
0x5a
0x5b
0x5a
0x5b
0x5c
0x5d
0x5e
0x5f token meta or info

These codes are being discussed at:
https://ethereum-magicians.org/t/erc-1066-ethereum-status-codes-esc/283/24

References

Copied from original issue: ethereum/EIPs#1400