LoginSignup
20
9

More than 3 years have passed since last update.

技術者向け セキュリティトークンの規格(ERC1400)を理解する

Posted at

はじめに

なかなコアな内容になりますが、セキュリティトークンの規格を理解します。

セキュリティトークンに関するethereumの規格のうち、気になった部分をまとめています。

STO(Security Token Offering)

STOはブロックチェーン上で発行されるトークン化された証券です。

証券関連の法律は国によって異なります。一例としてアメリカでは有価証券としての性質を持つものはセキュリティトークンに該当するとされ、証券関連の規制が適用されます。
SEC(米国証券取引委員会)では、下記のHowey Testの要件を満たしたトークンを指します。

Howey Test

  • 金銭または資産による投資であること
  • 共同事業に対する投資であること
  • 利益に対する期待があること
  • その利益は第三者の努力によって生じるものであること

STOのエコシステムのマップ

Screen-Shot-2018-11-30-at-3.04.23-PM-1159x675.png
(参考:The Block

セキュリティトークンエコシステムには、6つのカテゴリがあります。

  • 発行(Issuance)
  • ブローカーディーラー(Broker-Dealer)
  • 保管と信頼(Custody & Trust)
  • 法的(Legal)
  • コンプライアンス(Compliance)
  • 取引(Trading)

Polymath(ST20)

Polymathは、セキュリティトークンをcrypto市場へ提供した最初の会社で有名です。ERC20を継承したST20という規格を開発し、発行プラットフォームを提供しています。

最近メジャーバージョンを更新し、v3.0.0へ更新したようです。(https://github.com/PolymathNetwork/polymath-core)
v2.0.0から比較すると、ERC1400の規格が組み込まれていました。

STOの規格

EthereumでSTOの規格が提案されています。ERC1400(Security Token Standard)はPolymathを中心に開発が進められています。

また、ERC1400の機能が増えてきたため、以下の機能の統合版として定められています。

  • ERC1410: Partially Fungible Tokens
    トークンをパーティション(トランシェ)で分割できるようになります。
    ※証券化商品を、リスクレベルや利回りなどの条件で区分したもの。特定の条件により区分することをトランチングといい、区分された各部分をトランシェといいます。
    ERC1410ではトランシェのことをパーティションと呼ぶようです。

  • ERC1594: Core Security Token Standard
    転送/発行/償還へのデータのオフチェーン注入をサポートする標準と、転送の有効性をチェックする機能を提供します。

  • ERC1643: Document Management Standard
    ERC1410へドキュメントを添付できるようにします。

  • ERC1644: Controller Token Operation Standard
    強制的に転送できるようにします。

ERC1400: Security Token Standard

STOの標準インターフェイスを提供します。

  • セキュリティトークンの発行/償還
  • 所有権と転送制限の管理
  • トークンバランスの異なるサブセットが転送制限、権利、義務に関する透明性

ERC1400の条件

  • [MUST]転送が成功するかどうかを照会し、失敗の理由を返す標準インターフェイスが。
  • [MUST]法的措置または資金回収のために強制送金を実行できなければなりません。
  • [MUST]発行および償還のための標準イベントを発行する必要があります。
  • [MUST]特別な株主の権利や譲渡制限のデータなど、トークン所有者の残高のサブセットにメタデータを添付できる必要があります。
  • [MUST]オフチェーンデータ、オンチェーンデータ、および転送のパラメーターに基づいて、転送時にメタデータを変更できる必要があります。
  • [MUST]セキュリティの関連ドキュメントの更新のクエリとサブスクライブをサポートする必要があります。
  • [MAY]チェーン上で検証するために、署名されたデータを転送トランザクションに渡す必要がある場合があります。
  • [SHOULD NOT]表現できる管轄区域全体の資産クラスの範囲を制限すべきではありません。
  • [MUST]ERC-20と互換性がある必要があります。
  • [COULD]ERC-777と互換性があります。

他の規格(ERC1410/1594/1643/1644)と組み合わせて使うために必要な関数

ERC1400は、ERC-1410、ERC-1594、ERC-1643、ERC-1644をセキュリティトークン標準の基礎ライブラリとして参照しています。

組み合わせて使うために、operatorTransferByPartitionoperatorRedeemByPartitionの2つの関数が追加されています。

インターフェース

/// @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);


}

ERC1410: Partially Fungible Tokens

所有者トークンをパーティションに分割管理できるようにした規格です。

メタデータを個々の代替トークンに関連付けることができると、それらのトークンに関連付けられた機能を構築するときに役立ちます。

たとえば、個々のトークンがいつmintされたかを知ることで、トークン保有者の残高の一部に対して権利確定またはロックアップロジックを実装できます。

証券を表すトークンは、多くの場合、共有に関連付けられた制限など、メタデータを個々のトークンに添付する必要があります。

ユーザが保持するトークンのグループに任意のメタデータを関連付けることができることは、さまざまなユースケースで役立ちます。トークンの出所(つまり、トークンの前の所有者を記録する)に使用したり、トークンにデータを添付して、そのトークンの転送制限を決定するために使用したりできます。

一般的に、トークンは特定の状況下では代替可能ですが、他の状況下ではありません(たとえば、ゲーム内のクレジットや預け入れ残高)。このようなグループ化を定義し、これに関係なくトークンの全体的な分布に関するデータを維持しながら操作できることは、これらのタイプの資産のモデリングに役立ちます。

全体の残高内でトークンのグループ化を識別する標準的な方法があると、トークン保有者の残高に関する透明性が高まります。

インターフェース

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

interface IERC1410 {

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

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

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

    // 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;

    // Issuance / Redemption
    function issueByPartition(bytes32 _partition, 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 Events
    event TransferByPartition(
        bytes32 indexed _fromPartition,
        address _operator,
        address indexed _from,
        address indexed _to,
        uint256 _value,
        bytes _data,
        bytes _operatorData
    );

    // 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 IssuedByPartition(bytes32 indexed partition, address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
    event RedeemedByPartition(bytes32 indexed partition, address indexed operator, address indexed from, uint256 amount, bytes operatorData);

}

ERC1594: Core Security Token Standard

転送/発行/償還へのデータのオフチェーン注入をサポートする標準と、転送の有効性をチェックする機能を提供します。

セキュリティトークンは、他のトークンのユースケースとは大きく異なり、オフチェーンアクターとオンチェーンアクター間の相互作用が複雑で、規制の精査がかなり進んでいます。

転送、発行、償還と共にデータ(署名付き承認など)を提供する機能により、セキュリティトークンは、チェーン上のホワイトリストのみに依存することなく、より柔軟に転送制限を実装できます。

ERC-1066を使用して、ユーザーが実際に転送を実行する必要なく、転送が失敗する理由に関する理由コードを提供することで、UXを改善し転送が失敗した場合のガスを節約できます。

発行および償還のセマンティクスの形式化で、トークンの総供給量とトークンの経時的な変化を可視化できます。

インターフェース

/// @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);

}

ERC1643: Document Management Standard

ERC1410へドキュメントを添付できるようにします。

セキュリティトークンとその所有権は通常、投資家または発行者からの権利と義務を伴うため、法的文書を関連する契約に関連付ける能力は重要です。これを標準化された方法で行うことにより、ウォレット、取引所、および他のエコシステムメンバーがこれらのドキュメントの標準ビューを提供し、投資家が標準化された方法で更新できます。

インターフェース

/// @title IERC1643 Document Management (part of the ERC1400 Security Token Standards)
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec

interface IERC1643 {

    // Document Management
    function getDocument(bytes32 _name) external view returns (string, bytes32, uint256);
    function setDocument(bytes32 _name, string _uri, bytes32 _documentHash) external;
    function removeDocument(bytes32 _name) external;
    function getAllDocuments() external view returns (bytes32[]);

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

}

ERC1644: Controller Token Operation Standard

強制的に転送できるようにします。

セキュリティトークンは規制および法的監視の対象となるため(詳細は管轄権、規制の枠組み、および原資産に応じて異なります)、多くの場合、発行者(またはコントローラーとして機能する発行者によって委任された当事者、例えば、規制当局または転送エージェント)は、アドレス間でトークンを強制的に転送する機能を保持する必要があります。

これらのコントローラー転送は透過的である必要があり(これを強制転送としてフラグするイベントを発行します)、トークンコントラクト自体は、これが可能かどうかを明示する必要があります。

これが必要な場合の例は、不正な取引を取り消す、紛失した秘密鍵を解決する、裁判所命令に対応することです。

インターフェース

/// @title IERC1644 Controller Token Operation (part of the ERC1400 Security Token Standards)
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec

interface IERC1644 is IERC20 {

    // 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;

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

}

参考

20
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
9