Yos Riady software craftsman 🌱

Automated Regulatory Compliance of Security Tokens

Automated Regulatory Compliance with Security Token Standards

A crypto token that passes the Howey Test is deemed a Security token. Security tokens are designed to represent complete or fractional ownership interests in assets. While utility tokens have no limitations on who can send or receive the token, security tokens are subject to many restrictions based on identity and jurisdiction.

In the same way that the ERC-20 token standard helped to create a boom in utility tokens, a security token standard will help drive the adoption of security tokens.

In this article, let’s look at the current state of security token standards in the Ethereum ecosystem and each of their approaches to build a self-regulatory governance mechanism for these tokens

The Case for Tokenization

Security tokens offer the benefit of bringing transparency over traditional paper shares through the use of the blockchain and its associated public ledger. Security token structure, distribution, or changes that could affect investors are publicly accessible to all on the blockchain.

Tokenizing private securities also creates opportunities for greater efficiency. Blockchains enable trades to occur securely between two parties without a middleman. The process and timeline for settlement and clearing of transactions can be condensed significantly, and any reconciliation processes can be greatly simplified. Tokenizing private securities has the potential to significantly reduce costs, increase speed of settlement, and improve security.

Securities Regulations

Private securities must follow any applicable laws to avoid onerous public filing requirements. These laws can require limiting the number of total investors, only allowing specific types of investors (e.g. accredited investors), implementing a holding period, and applying many other rules.

Restrictions differ by jurisdiction, and compliance with both the issuer’s jurisdiction as well as each investor’s jurisdiction is required. Furthermore, these restrictions apply not only to the initial offering (where much of the responsibility lies on the issuer), but to all secondary trades where responsibility is also placed on the seller.

Security Tokens

Enforcing regulatory requirements has been a significant roadblock in the adoption of blockchain-powered securities. Moving the issuance, trading and lifecycle events of a security onto a public ledger requires having a standard way of modeling securities, their ownership and their properties on-chain.

In order for tokens to comply with securities regulations, tokens need a mechanism to impose both initial and secondary transfer restrictions on Token Holders. We want to be able to:

  • Require only Token Holders who have successfully gone through KYC/AML to be able to buy tokens from the issuer during the initial offering, and prevent them from being able to subsequently buy or sell to other parties unless they both have been cleared to do so.
  • Restrict transfers based on legal status, jurisdiction, sanctioned countries, global watchlists, and other rules.
  • Limit the number of investors.
  • Limit the maximum cap of each investor’s holdings.
  • Other requirements.

Current blockchain-based protocols lack an effective governance mechanism that ensures token transfers comply with these requirements.

A minimum viable compliance layer is to have whitelists of addresses who are validated Token Holders, and only allow transfers between whitelisted addresses. However, for the security token ecosystem to be interoperable with each other a predefined set of standards is necessary.

To solve this problem, we want to devise a self-regulatory system for tokens that is administered by the network and enforced on-chain, allowing parties that trust each other to transact securely. We also want this governance mechanism to become more and more decentralized over time.

Security Token Standards

Token Standards such as ERC20 has gained widespread adoption and allowed the interoperability of all kinds of tokens on different exchanges. Security tokens standards builds on top of standards like ERC-20 to add the ability to enforce transfer restrictions at the token level. For your reference, here is the ERC-20 Standard Token interface that all of the security token standards builds on:

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);
}

At a high-level, adding transfer restrictions to tokens is straightforward. Consider the ERC20 transfer() function that moves tokens between two addresses:

function transfer(address _to, uint256 _value) public returns (bool) {
  require(_to != address(0));
  require(_value <=   balances[msg.sender]);
  balances[msg.sender] = balances[msg.sender].sub(_value);
  balances[_to] = balances[_to].add(_value);
  emit Transfer(msg.sender, _to, _value);
  return true;  
}

We can add a single line to add transfer restrictions:

function transfer(address _to, uint256 _value) public returns (bool) {
  require(checkTransferAllowed(msg.sender, _to, _value), "Transfer is not allowed.");

  require(_to != address(0));
  require(_value <=   balances[msg.sender]);   
  balances[msg.sender] = balances[msg.sender].sub(_value);
  balances[_to] = balances[_to].add(_value);
  emit Transfer(msg.sender, _to, _value);
  return true;  
}

We defined a checkTransferAllowed() function that performs the authorization check of a transfer and require it to return successfully for the transfer to succeed. At minimum, the check would allow all transfers:

function checkTransferAllowed(address _from, address _to, uint256 _amount) public view returns(bool) {
  return true;
}

We could choose to disallow transfers beyond a certain volume:

function checkTransferAllowed(address _from, address _to, uint256 _amount) public view returns(bool) {
  if(_amount > 100)
    return false;
  else
    return true;
}

The logic within the checkTransferAllowed() function will depend on your specific use cases and securities regulations.

As of October 2018, there are a handful of distinct security token standards proposed and implemented by different parties. In this article we’ll look at each of these standards and see how each compares.

We will examine the following Ethereum security token standards:

ST-20

An ST-20 token is an Ethereum-based token implemented on top of the ERC-20 protocol that adds the ability for tokens to control transfers based on specific rules. ST-20 tokens rely on Transfer Managers to determine the ruleset the token should apply in order to allow or deny a transfer, be it between the issuer and investors, in a peer to peer exchange, or a transaction with an exchange.

Below is the IST20 token contract interface:

pragma solidity ^0.4.24;

import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
import "openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol";

/**
 * @title Interface for the ST20 token standard
 */
contract IST20 is StandardToken, DetailedERC20 {

    // off-chain hash
    string public tokenDetails;

    //transfer, transferFrom must respect use respect the result of verifyTransfer
    function verifyTransfer(address _from, address _to, uint256 _amount) public returns (bool success);

    /**
     * @notice mints new tokens and assigns them to the target _investor.
     * Can only be called by the STO attached to the token (Or by the ST owner if there's no STO attached yet)
     */
    function mint(address _investor, uint256 _amount) public returns (bool success);

    /**
     * @notice Burn function used to burn the securityToken
     * @param _value No. of token that get burned
     */
    function burn(uint256 _value) public;

    event Minted(address indexed to, uint256 amount);
    event Burnt(address indexed _burner, uint256 _value);

}

Source: IST20.sol

In ST-20, the verifyTransfer() function calls external TransferManager contracts that contains business rules related to transfer restrictions. This approach makes it possible for developers to upgrade these rules over time.

function verifyTransfer(address _from, address _to, uint256 _amount) public view returns (bool success) {
  if (modules[TRANSFERMANAGER_KEY].length == 0) {
    return true;
  }
  for (uint8 i = 0; i < modules[TRANSFERMANAGER_KEY].length; i++) {
    if (ITransferManager(modules[TRANSFERMANAGER_KEY][i].moduleAddress).verifyTransfer(_from, _to, _amount)) {
      return true;
    }
  }
  return false;
}

TransferManager addresses are stored in a modules mapping within the token contract.

In the above function, the token’s verifyTransfer() calls ITransferManager().verifyTransfer() to find out if a transfer is allowed or disallowed. This method will loop through all the transfer managers it contains and the transfer will be allowed if a transfer manager approves the transaction. Transfer managers implement the following interface:

/**
 * @title Interface to be implemented by all Transfer Manager modules
 */
contract ITransferManager {
    //If verifyTransfer returns:
    //  FORCE_VALID, the transaction will always be valid, regardless of other TM results
    //  INVALID, then the transfer should not be allowed regardless of other TM results
    //  VALID, then the transfer is valid for this TM
    //  NA, then the result from this TM is ignored
    enum Result {INVALID, NA, VALID, FORCE_VALID}

    function verifyTransfer(address _from, address _to, uint256 _amount, bool _isTransfer) public returns(Result);
}

TransferManager contracts such as Polymath’s GeneralTransferManager can maintain a whitelist of allowed addresses and any timelocks assigned to that address. This effectively allows transfer restrictions and token holding periods to be implemented at the token level:

struct TimeRestriction {
    uint256 fromTime;
    uint256 toTime;
    uint256 expiryTime;
    bool canBuyFromSTO;
}

mapping (address => TimeRestriction) public whitelist;

KYC/AML processes to verify these addresses are performed off-chain. Once the KYC passes, the results are set on the TransferManager’s on-chain whitelist to be enforced in both initial and secondary transfers. Only authorized staff addresses are allowed to modify this whitelist of addresses.

R-Token

R-Token is a permissioned token on the Ethereum blockchain, enabling token transfers to occur if and only if they are approved by an on-chain Regulator Service. The Regulator Service can be configured to meet relevant securities regulations, Know Your Customer (KYC) policies, Anti-Money Laundering (AML) requirements, tax laws, and more. Implemented with the correct configurations, the R-Token standard makes compliant transfers possible, both on exchanges and person to person, in ICOs and secondary trades, and across jurisdictions. R-Token enables ERC-20 tokens to be used for regulated securities.

Here is a reference RegulatedToken implementation:

contract RegulatedToken {
  /**
   * @notice Triggered when regulator checks pass or fail
   */
  event CheckStatus(uint8 reason, address indexed spender, address indexed from, address indexed to, uint256 value);

  /**
   * @notice Address of the `ServiceRegistry` that has the location of the
   *         `RegulatorService` contract responsible for checking trade
   *         permissions.
   */
  ServiceRegistry public registry;

  /**
   * @notice Constructor
   *
   * @param _registry Address of `ServiceRegistry` contract
   * @param _name Name of the token: See DetailedERC20
   * @param _symbol Symbol of the token: See DetailedERC20
   */
  function RegulatedToken(ServiceRegistry _registry, string _name, string _symbol) public
    DetailedERC20(_name, _symbol, RTOKEN_DECIMALS)
  {
    require(_registry != address(0));

    registry = _registry;
  }

  /**
   * @notice ERC-20 overridden function that include logic to check for trade validity.
   *
   * @param _to The address of the receiver
   * @param _value The number of tokens to transfer
   *
   * @return `true` if successful and `false` if unsuccessful
   */
  function transfer(address _to, uint256 _value) public returns (bool) {
    if (_check(msg.sender, _to, _value)) {
      return super.transfer(_to, _value);
    } else {
      return false;
    }
  }

  /**
   * @notice ERC-20 overridden function that include logic to check for trade validity.
   *
   * @param _from The address of the sender
   * @param _to The address of the receiver
   * @param _value The number of tokens to transfer
   *
   * @return `true` if successful and `false` if unsuccessful
   */
  function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
    if (_check(_from, _to, _value)) {
      return super.transferFrom(_from, _to, _value);
    } else {
      return false;
    }
  }

  /**
   * @notice Performs the regulator check
   *
   * @dev This method raises a CheckStatus event indicating success or failure of the check
   *
   * @param _from The address of the sender
   * @param _to The address of the receiver
   * @param _value The number of tokens to transfer
   *
   * @return `true` if the check was successful and `false` if unsuccessful
   */
  function _check(address _from, address _to, uint256 _value) private returns (bool) {
    var reason = _service().check(this, msg.sender, _from, _to, _value);

    CheckStatus(reason, msg.sender, _from, _to, _value);

    return reason == 0;
  }

  /**
   * @notice Retreives the address of the `RegulatorService` that manages this token.
   *
   * @dev This function *MUST NOT* memoize the `RegulatorService` address.  This would
   *      break the ability to upgrade the `RegulatorService`.
   *
   * @return The `RegulatorService` that manages this token.
   */
  function _service() constant public returns (RegulatorService) {
    return RegulatorService(registry.service());
  }
}

Similar to the previous ST-20 standard, the R-Token standard has a _check(address _from, address _to, uint256 _value) function that is finds out if there are any transfer restrictions for the parties involved:

function transfer(address _to, uint256 _value) public returns (bool) {
  if (_check(msg.sender, _to, _value)) {
    return super.transfer(_to, _value);
  } else {
    return false;
  }
}

RegulatorService

The _check function calls an external RegulatorService.check() function in a separate regulator contract. This is similar to the ST-20 TransferManager contract:

function _check(address _from, address _to, uint256 _value) private returns (bool) {
  var reason = _service().check(this, msg.sender, _from, _to, _value);
  CheckStatus(reason, msg.sender, _from, _to, _value);
  return reason == 0;
}

function _service() constant public returns (RegulatorService) {
  return RegulatorService(registry.service());
}

Here is the RegulatorService contract interface:

contract RegulatorService {
  function check(address _token, address _spender, address _from, address _to, uint256 _amount) public returns (uint8);
}

Note that the RegulatorService.check() returns a uint8 status code instead of a bool.

The token stores a ServiceRegistry contract address in a public variable registry, and uses this registry contract to retrieve the regulator contract address.

ServiceRegistry

ServiceRegistry public registry;

function _service() constant public returns (RegulatorService) {
  return RegulatorService(registry.service());
}

To upgrade RegulatorService, you can deploy a new version and change the address stored in the ServiceRegistry.

The initial implementation of the Regulator Service relies on an off-chain Trade Controller to update its permissions as well as add newly approved participants.

The Trade Controller determines the status of the permissions. It maps participant data against relevant securities regulations, KYC/AML policies, and tax laws to determine whether each participant can send and/or receive the token. It also determines whether the token is locked/unlocked and whether partial trading is allowed/disallowed. These token-level permissions are then updated in the Regulator Service.

Only an approved Trade Controller with the correct private/public key pair can make changes to the Regulator Service.

ERC-1400

ERC-1400 is the most complex and generalizable standard examined in this article. The standard is split into two sub-standards:

  • ERC 1400 Security Token Standard: A standard interface for issuing security tokens, managing their ownership and transfer restrictions.
  • ERC 1410 Partially Fungible Token Standard: A standard interface for organising an owners tokens into a set of tranches (or buckets.)

Here are some of the unique characteristics of the ERC-1400 standard:

  • Unlike previous standards where the validator functions are only available internally, ERC-1400 exposes a standard interface to query if a transfer would be successful and return a reason for failure.
  • Able to perform forced transfer for legal action or fund recovery.
  • Able to attach metadata to a subset of a token holder’s balance such as special shareholder rights or data for transfer restrictions.
  • Able to modify metadata at time of transfer based on off-chain data, on-chain data and the parameters of the transfer.
  • Is both ERC-20 and ERC-777 compatible.

ERC-1410 Partially Fungible Token Standard

A Partially-Fungible Token allows for attaching metadata to a partial balance of a token holder. These partial balances are called tranches and are indexed by a bytes32 _tranche key which can be associated with metadata on-chain or off-chain.

Tokens that represent securities often require metadata to be attached to individual tokens, such as the category of share or restrictions associated with the share. Being able to associate metadata with individual fungible tokens is useful when building functionality associated with those tokens.

For example, knowing when an individual token was minted allows vesting or lockup logic to be implemented for a portion of a token holders balance.

Being able to associate arbitrary metadata with groups of tokens held by users is useful in a variety of use-cases. It can be used for token provenance (i.e. recording the previous owner(s) of tokens) or to attach data to a token which is then used to determine any transfer restrictions of that token.

/// @title ERC-1410 Partially Fungible Token Standard
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec

interface IERC1410 is IERC777 {

    // Token Information
    function balanceOfByTranche(bytes32 _tranche, address _tokenHolder) external view returns (uint256);
    function tranchesOf(address _tokenHolder) external view returns (bytes32[]);

    // Token Transfers
    function sendByTranche(bytes32 _tranche, address _to, uint256 _amount, bytes _data) external returns (bytes32);
    function sendByTranches(bytes32[] _tranches, address _to, uint256[] _amounts, bytes _data) external returns (bytes32[]);
    function operatorSendByTranche(bytes32 _tranche, address _from, address _to, uint256 _amount, bytes _data, bytes _operatorData) external returns (bytes32);
    function operatorSendByTranches(bytes32[] _tranches, address _from, address _to, uint256[] _amounts, bytes _data, bytes _operatorData) external returns (bytes32[]);

    // Default Tranche Management
    function getDefaultTranches(address _tokenHolder) external view returns (bytes32[]);
    function setDefaultTranche(bytes32[] _tranches) external;

    // Operators
    function defaultOperatorsByTranche(bytes32 _tranche) external view returns (address[]);
    function authorizeOperatorByTranche(bytes32 _tranche, address _operator) external;
    function revokeOperatorByTranche(bytes32 _tranche, address _operator) external;
    function isOperatorForTranche(bytes32 _tranche, address _operator, address _tokenHolder) external view returns (bool);

    // Transfer Events
    event SentByTranche(
        bytes32 indexed fromTranche,
        address operator,
        address indexed from,
        address indexed to,
        uint256 amount,
        bytes data,
        bytes operatorData
    );

    event ChangedTranche(
        bytes32 indexed fromTranche,
        bytes32 indexed toTranche,
        uint256 amount,
    );

    // Operator Events
    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
    event RevokedOperator(address indexed operator, address indexed tokenHolder);

    event AuthorizedOperatorByTranche(bytes32 indexed tranche, address indexed operator, address indexed tokenHolder);
    event RevokedOperatorByTranche(bytes32 indexed tranche, address indexed operator, address indexed tokenHolder);
}

A key characteristic of ERC-1411 is the idea of ‘tranches’ and how each partial balance can have metadata attached to it such as token holding periods. You can view balances by tranche with function balanceOfByTranche(bytes32 _tranche, address _tokenHolder) external view returns (uint256); and send tokens by specific tranches:

function sendByTranche(bytes32 _tranche, address _to, uint256 _amount, bytes _data) external returns (bytes32);
function sendByTranches(bytes32[] _tranches, address[] _tos, uint256[] _amounts, bytes _data) external returns (bytes32);

ERC-1400 Security Token Standard

Builds on the partially fungible token standard (ERC 1410 (#1410)) to provide additional functionality to manage different types of ownership of fungible tokens representing asset ownership.

/// @title IERCST Security Token Standard (EIP 1400)
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec

interface IERCST is IERCPFT {
    // Document Management
    function getDocument(bytes32 _name) external view returns (string, bytes32);
    function setDocument(bytes32 _name, string _uri, bytes32 _documentHash) external;

    // Controller Operation
    function isControllable() external view returns (bool);

    // Token Issuance
    function isIssuable() external view returns (bool);
    function issueByTranche(bytes32 _tranche, address _tokenHolder, uint256 _amount, bytes _data) external;
    event IssuedByTranche(bytes32 indexed tranche, address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);

    // Token Redemption
    function redeemByTranche(bytes32 _tranche, uint256 _amount, bytes _data) external;
    function operatorRedeemByTranche(bytes32 _tranche, address _tokenHolder, uint256 _amount, bytes _operatorData) external;
    event RedeemedByTranche(bytes32 indexed tranche, address indexed operator, address indexed from, uint256 amount, bytes operatorData);

    // Transfer Validity
    function canSend(address _from, address _to, bytes32 _tranche, uint256 _amount, bytes _data) external view returns (byte, bytes32, bytes32);
}

The ERC-1400 standard defines the following notable features:

  • Stores legal documents on-chain for reference.
  • Builds on the ERC-777 standard Operators by setting an irrevocable default operator to perform forced transfers when necessary.
  • Adds a public canSend() function that provides a more general purpose way to query if sending tokens would be successful. It accepts a set of parameters which may include signed bytes _data and returns a reason byte code with information about the success or failure of the transaction.
  • The canSend() function returns an EIP-1066 compliant application-specific status codes specified below. An implementation can also return arbitrary data as a bytes3

The identity aspects of transfer restrictions are outside the scope of the standard. This is because there are various identity standards (e.g. ERC725, 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 canSend 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.

ERC-1404

Token issuers need a way to restrict transfers of ERC-20 tokens to be compliant with securities laws and other contractual obligations.

ERC-1404 Simple Restricted Token Standard is a simple and interoperable standard for issuing tokens with transfer restrictions. The following draws on input from top issuers, law firms, relevant US regulatory bodies, and exchanges.

contract ERC1404 is ERC20 {
  function detectTransferRestriction (address from, address to, uint256 value) public view returns (uint8);
  function messageForTransferRestriction (uint8 restrictionCode) public view returns (string);
}

Similar to other standards, ERC-1404 has a detectTransferRestriction() function that returns a uint status code such as ALLOWED and DISALLOWED. This function is called at the start of token movement functions such as transfer().

It also has a messageForTransferRestriction() function which returns details about the uint status code.

ERC-1462

ERC-1462 is an extension to ERC-20 standard token that provides compliance with securities regulations and legal enforceability.

interface IERC1462 /* is ERC-20 */ {
    // Checking functions
    function checkTransferAllowed (address from, address to, uint256 value) public view returns (byte);
    function checkTransferFromAllowed (address from, address to, uint256 value) public view returns (byte);
    function checkMintAllowed (address to, uint256 value) public view returns (byte);
    function checkBurnAllowed (address from, uint256 value) public view returns (byte);

    // Documentation functions
    function attachDocument(bytes32 _name, string _uri, bytes32 _contentHash) external;
    function lookupDocument(bytes32 _name) external view returns (string, bytes32);
}

Similar to other standards, ERC-1462 defines:

  • Transfer restriction functions such as checkTransferAllowed()
  • Legal documentation functions such as attachDocument()

The four new check*Allowed() functions are used to check that the actions are allowed for the provided inputs. The implementation details of each function are left for the token issuer, it is the issuer’s responsibility to add all necessary checks that will validate an operation in accordance with KYC/AML policies and legal requirements set for a specific token asset.

Each function returns a status code from the common set of Ethereum status codes (ESC), according to ERC-1066.

Bonus: Transaction Permission Layer (TPL)

As we’ve seen in previous standards, aside from the ability to moderate transfers of securities using either an on-chain codified rule set or off-chain approvals, we also want to associate some kind of public data to the addresses involved in the transaction.

The Transaction Permission Layer protocol (TPL) is a method for assigning metadata (herein referred to as “attributes”) to Ethereum addresses. These attributes then form the basis for designing systems that enforce permissions when performing certain transactions. Security token transfers are one such transaction, where compliance with various laws and regulations will compel tokens to only permit a transfer once a set of conditions (e.g. identity verification, KYC/AML, or other attributes) have been met.

TPL allows for the creation of digital “jurisdictions” with requirements that are administered and regulated on-chain by one or more projects. Within these jurisdictions, third-party organizations operate as “validators” which identify whether a proposed token transfer is compliant with the requirements of the jurisdiction. Projects can tailor the TPL to limit transfers of their tokens on an ongoing basis, using any criteria that can be recorded in a registry by a validator.

At the core of TPL is the jurisdiction — a single smart contract that links attributes to addresses. It implements an AttributeRegistry interface, where attributes are registered to addresses as a uint256 => uint256 key-value pair, defined as follows:

Permissioned tokens and other contracts can then use this interface to identify and confirm attributes recognized by a jurisdiction without needing to take on the additional technical overhead of managing attribute assignment and revocation themselves.

To sum up, TPL allows contracts such as security tokens to verify claims from independent Cerificate Validators. While not a security token standard, TPL offers a key building block to implement decentralized address verifications for transfer restrictions.

Common Security Token Features

To summarize, here are some features that are common across the different security tokens we’ve examined.

KYC / Whitelisting

In the regulated world, almost every investor needs to be qualified with KYC/AML before you send them a security. All of the security token standards that described here can do whitelisting, which means that they keep a list of blockchain addresses that represent investors that are qualified to receive a security. Then, they prevent the transfer of the token to anyone that is not on the list.

Transfer Rules

If you are selling a private security, you will need to enforce transfer rules so that you send only to qualified investors, according to the different rules of various jurisdictions. You probably also want to enforce lockups, such as the one year lockup for private securities sold in the US. You may have other rules that are typically part of a “subscription agreement” signed by a shareholder.

Upgradability

During the multi-year life of a security, the rules change. The sets of qualified investors change. You may want to provide new features for compatibility with emerging exchanges. You may want to fix your implementation. It’s important to be able to update the source code of an existing token.

Decentralized Identity Registries

Some standards use identity contracts that hold investor information and permissions for purposes of enforcing transfer restrictions. This aspect is usually handled by a separate standard such as ERC725 or ERC1484.

In Summary

In this article, we looked at the current state of security token standards in the Ethereum ecosystem. Security tokens has requirements that are fundamentally different from the requirements of the typical cryptocurrency.

We’ve also looked at a handful of different security token standards. The standards space is full of activity, with new standards being published by a diverse set of organizations. So many groups of people are working towards the goal of regulated security tokens and tokenized assets.

Standards adoption amongst token issuers has the potential to evolve into a dynamic and interoperable landscape of automated compliance. Be sure to keep an eye on this space for new developments.

Author

Yos is a software craftsman based in Singapore.

📬 Subscribe to my newsletter

Get notified of my latest articles by providing your email below.


Going Serverless book

Interested to find out more about serverless? Going Serverless teaches you how to build scalable applications with the Serverless framework and AWS Lambda. You'll learn how to design, develop, test, deploy, and secure Serverless applications from planning to production.

Learn More →