Digital sunset on an analogue world.

Tokenising Shares: Introducing ERC-884

Dave Sag
Apr 17, 2018 · 11 min read

Core Principles

ERC-884 tokens enable conformance with the following principles, over and above the baseERC-20 standard:

  1. It must record the information specified in Sections 156, 159, 217(a) and 218 of The Act.
  2. It must record transfers of shares as governed by Article 8 of Subtitle I of Title 6.

No Partial ERC-884 Tokens

Each ERC-884 token must correspond to a single unnumbered share, each of which must be paid for in full. There is no need to record information concerning partly paid shares. Similarly there is no requirement for an ERC-884 token to correspond to a specifically numbered share. It is enough to know that a shareholder owns a certain volume of shares. The notion of individually numbered shares is, in this context, needlessly burdensome.

Securities Exchange Commission Requirements

The Securities Exchange Commission (SEC) has additional requirements as to how a crowdsale ought to be run and what information must be made available to the general public. This information is, however, out of scope of the ERC-884 standard, though the standard does support such requirements.

Use of an Identity Hash

Implementers of a crowdsale, in order to comply with The Act, must be able to produce an up-to-date list of the names and addresses of all shareholders. It is not desirable to include those details in a public blockchain, both for reasons of privacy, and also for reasons of economy. Storing arbitrary string data on the blockchain is strongly discouraged.

GET https://<host>/<pathPrefix>/:ethereumAddress -> [true|false]

Users Who Have Lost Access to Their Shares

A traditional share registry is typically managed by a Transfer Agent who is authorised to maintain the register accurately, and to handle shareholder enquiries. A common request is for share certificates to be reissued in the case where the shareholder has lost or destroyed their original.

The Technicalities: Extending ERC-20

The ERC20 token provides the following basic features, presented here in Solidity code:

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
  );
}
/**
 *  An `ERC20` compatible Token that conforms to
 *  Delaware State Senate, 149th General Assembly,
 *  Senate Bill No. 69: An act to Amend Title 8
 *  of the Delaware Code Relating to the General Corporation Law.
 *
 *  Implementation Details.
 *
 *  An implementation of this Token standard SHOULD
 *  provide the following:
 *
 *  `name` - for use by wallets and exchanges.
 *  `symbol` - for use by wallets and exchanges.
 *
 *  The implementation MUST take care not to allow
 *  unauthorised access to stock transfer functions.
 *
 *  In addition to the above, the following optional
 *  `ERC20` function MUST be defined.
 *
 *  `decimals` — MUST return `0` as each Token represents
 *  a single Share and Shares are non-divisible.
 *
 */
contract ERC884 is ERC20 {    /**
     *  This event is emitted when a verified address
     *  and associated identity hash are added to the contract.
     *
     *  @param addr The address that was added.
     *  @param hash The identity hash associated with the address.
     *  @param sender The address that caused the address
     *                to be added.
     */
    event VerifiedAddressAdded(
        address indexed addr,
        bytes32 hash,
        address indexed sender
    );    /**
     *  This event is emitted when a verified address and its
     *  associated identity hash are removed from the contract.
     *
     *  @param addr The address that was removed.
     *  @param sender The address that caused the address
     *                to be removed.
     */
    event VerifiedAddressRemoved(
        address indexed addr,
        address indexed sender
    );    /**
     *  This event is emitted when the identity hash associated
     *  with a verified address is updated.
     *
     *  @param addr The address whose hash was updated.
     *  @param oldHash The identity hash that was associated
     *                 with the address.
     *  @param hash The hash now associated with the address.
     *  @param sender The address that caused the hash to
     *                be updated.
     */
    event VerifiedAddressUpdated(
        address indexed addr,
        bytes32 oldHash,
        bytes32 hash,
        address indexed sender
    );    /**
     *  This event is emitted when an address is cancelled and
     *  replaced with a new address.  This happens in the case
     *  where a shareholder has lost access to their original
     *  address and needs to have their share reissued to a new
     *  address.  This is the equivalent of issuing replacement
     *  share certificates.
     *
     *  @param original The address being superseded.
     *  @param replacement The new address.
     *  @param sender The address that caused the address
     *                to be superseded.
     */
    event VerifiedAddressSuperseded(
        address indexed original,
        address indexed replacement,
        address indexed sender
    );    /**
     *  Add a verified address, along with an associated
     *  verification hash to the contract. Upon successful
     *  addition of a verified address, the contract must emit
     *  `VerifiedAddressAdded(addr, hash, msg.sender)`.
     *  It MUST throw if the supplied address or hash are zero,
     *  or if the address has already been supplied.
     *
     *  @param addr The address of the person represented
     *              by the supplied hash.
     *  @param hash A cryptographic hash of the address-holder's
     *              verified information.
     */
    function addVerified(address addr, bytes32 hash) public;    /**
     *  Remove a verified address and the associated
     *  verification hash.
     *  If the address is unknown to the contract then this 
     *  does nothing.
     *  If the address is successfully removed, this function
     *  must emit `VerifiedAddressRemoved(addr, msg.sender)`.
     *  It MUST throw if an attempt is made to remove a 
     *  verifiedAddress that owns Tokens.
     *
     *  @param addr The verified address to be removed.
     */
    function removeVerified(address addr) public;    /**
     *  Update the hash for a verified address
     *  known to the contract.
     *  Upon successful update of a verified address
     *  the contract must emit
     *  `VerifiedAddressUpdated(addr, oldHash, hash, msg.sender)`.
     *  If the hash is the same as the value already stored then
     *  no `VerifiedAddressUpdated` event is to be emitted.
     *  It MUST throw if the hash is zero, or if the
     *  address is unverified.
     *
     *  @param addr The verified address of the person
     *              represented by the supplied hash.
     *  @param hash A new cryptographic hash of the address-holder's 
     *              updated verified information.
     */
    function updateVerified(address addr, bytes32 hash) public;    /**
     *  Cancel the original address and reissue the
     *  Tokens to the replacement address.
     *  Access to this function MUST be strictly controlled.
     *  The `original` address MUST be removed from the
     *  set of verified addresses.
     *  Throw if the `original` address supplied
     *  is not a shareholder.
     *  Throw if the `replacement` address is not
     *  a verified address.
     *  Throw if the `replacement` address already holds Tokens.
     *  This function MUST emit `VerifiedAddressSuperseded`.
     *
     *  @param original The address to be superseded.
     *                  This address MUST NOT be reused.
     */
    function cancelAndReissue(
        address original,
        address replacement
    ) public;    /**
     *  The `transfer` function MUST NOT allow transfers
     *  to addresses that have not been verified and
     *  added to the contract.
     *  If the `to` address is not currently a shareholder
     *  then it MUST become one.
     *  If the transfer will reduce `msg.sender`'s balance
     *  to 0 then that address MUST be removed from the
     *  list of shareholders.
     */
    function transfer(
        address to,
        uint256 value
    ) public returns (bool);    /**
     *  The `transferFrom` function MUST NOT allow transfers
     *  to addresses that have not been verified and added
     *  to the contract.
     *  If the `to` address is not currently a shareholder
     *  then it MUST become one.
     *  If the transfer will reduce `from`'s balance to 0 then
     *  that address MUST be removed from the list of shareholders.
     */
    function transferFrom(
        address from,
        address to,
        uint256 value
     ) public returns (bool);    /**
     *  Tests that the supplied address is known to the contract.
     *
     *  @param addr The address to test.
     *  @return true if the address is known to the contract.
     */
    function isVerified(address addr) public view returns (bool);    /**
     *  Checks to see if the supplied address is a shareholder.
     *
     *  @param addr The address to check.
     *  @return true if the supplied address owns a token.
     */
    function isHolder(address addr) public view returns (bool);    /**
     *  Checks that the supplied hash is associated with
     *  the given address.
     *
     *  @param addr The address to test.
     *  @param hash The hash to test.
     *  @return true if the hash matches the one supplied
     *               with the address in `addVerified`, or
     *               `updateVerified`.
     */
    function hasHash(
         address addr,
         bytes32 hash
    ) public view returns (bool);    /**
     *  The number of addresses that hold tokens.
     *
     *  @return the number of unique addresses that hold tokens.
     */
    function holderCount() public view returns (uint);    /**
     *  By counting the number of Token holders using `holderCount`
     *  you can retrieve the complete list of Token holders,
     *  one at a time.
     *  It MUST throw if `index >= holderCount()`.
     *
     *  @param index The zero-based index of the holder.
     *  @return the address of the Token holder with
     *          the given index.
     */
    function holderAt(uint256 index) public view returns (address);    /**
     *  Checks to see if the supplied address was superseded.
     *
     *  @param addr The address to check.
     *  @return true if the supplied address was
     *               superseded by another address.
     */
    function isSuperseded(address addr) public view returns (bool);    /**
     *  Gets the most recent address, given a superseded one.
     *  Addresses may be superseded multiple times, so this
     *  function needs to follow the chain of addresses until it 
     *  reaches the final, verified address.
     *
     *  @param addr The superseded address.
     *  @return the verified address that ultimately
     *          holds the stock.
     */
    function getCurrentFor(
        address addr
    ) public view returns (address);
}

Test Cases and Reference Implementation

Test cases and a reference implementation are available at github.com/davesag/ERC884-reference-implementation.

Permissions Management

It is not desirable that anyone can add, remove, update, or supersede verified addresses. How access to these functions is controlled is outside of the scope of this standard, but the reference implementation offers a simple form of access control which could easily be extended.

Backwards Compatibility

The ERC-884 standard offers as minimal an extension as possible over the existing ERC-20 standard in order to conform to the requirements of The Act, while maintaining compatibility with ERC-20 itself.

  1. The transfer and transferFrom functions must not allow transfers to non-verified addresses, and must maintain the list of stockholders.
  2. Shareholders who transfer away their remaining tokens must be pruned from the list of shareholders.

Conclusions

ERC-884 tokens offer a mechanism by which a Delaware corporation can maintain an official share register using an Ethereum smart-contract, hosted on the Ethereum blockchain, rather than using a traditional mechanism.

Thanks

I would like to thank Harrison Perl and Tigran Avakyan of C4Coin for their valuable input into the creation of ERC-884, as well as Dr Ross Williams, and also Adrian Andreacchio from Industrie&Co for their detailed proofreading of this article.

Links

Coinmonks

Coinmonks is a technology-focused publication embracing decentralize technologies. We are Non-profit and education is our core value. Learn, Build and thrive. Our other project— https://coinmonks.com, https://cryptofi.co, https://coincodecap.com

1.4K

1.4K claps
Dave Sag

Written by

Dave Sag

Blockchain Tsar & Senior Javascript Practitioner at Industrie&Co. (https://industrie.co)

Coinmonks

Coinmonks

Coinmonks is a technology-focused publication embracing decentralize technologies. We are Non-profit and education is our core value. Learn, Build and thrive. Our other project— https://coinmonks.com, https://cryptofi.co, https://coincodecap.com