幣圈亂象終結者! 以太坊新STO方案詳解

“現在兩根金條放這兒,你告訴我哪一根是高尚的,哪一根是齷齪的”

——《潛伏》謝若林

STO:幣圈亂象終結者

隨着數字貨幣和ICO市場的逐漸冷卻,以監管合規爲特點的證券型通證發行(Security Token Offering,證券型通證發行,簡稱STO)開始獲得廣泛關注。

STO是將證券的發行、交易以及其他生命週期中的事件放到區塊鏈賬本上管理的方式。既然是證券,就必須符合證券法的監管。證券型通證在現實中必須有某種金融資產和權益作爲內在價值支撐,例如公司股權、債權、黃金、房地產投資信託、可轉債的轉換權等。

技術方面,9月14日,開發者Stephane Gosselin在以太坊改進方案(EIP)中提出了一種以太坊上實現STO的技術方案ERC1410,稱爲“部分可替代通證標準(Partially Fungible Token Standard)”,是一種兼容ERC20和ERC777標準的證券型通證發行標準。

筆者將從代碼層面,爲您剖析ERC1410提案的實現細節,幫助開發者深入理解以太坊ERC1410實現可監管證券型通證的細節。

從Fungible定義說起

要搞清什麼是“部分可替代通證標準(Partially Fungible Token Standard)”,就需要從Fungile的定義講起。

Fungible一般被翻譯爲“可替代的”或“可互換的”,根據Wikipedia上的定義:

在經濟學中,Fungibility指的是物品可以根據特定單位進行互換的屬性。 比方說,黃金可以根據重量單位進行互換,純度100%的1公斤黃金可以和任意同純度的1公斤黃金互換,而不管它是硬幣、鑄錠還是其他的狀態。其他可互換的物品包括輕質原油、公司股票、債券、貴金屬以及貨幣等等。 可替代性僅指每單位物品與另一同類物品每單位之間的等價性,而不是指一種物品與另一種物品的交換,比如以物換物。

在以太坊上,所有的ERC20 Token都是“可替代通證”(Fungible Token,簡稱FT),同一通證內兩個相等單位的Token並無差別。Bitcoin與Ether也是可替代的,我的一個Ether跟你的一個Ether並無差別。

與可替代通證相反的叫做“不可替代通證”(Non-Fungible Token,簡稱NFT)。不可替代通證中的每一個Token都是獨一無二的。比如一部2002年的諾基亞無法與一部2018年的智能手機相互替換,因爲它們在各自的功能、型號、外觀等屬性上千差萬別,無法通過一種可互換的基礎單位進行替代。在以太坊上,ERC721標準即爲不可替代通證的標準,ERC721標準中主要刻畫了不同屬物品的所有權轉移。在2017年底流行的CryptoKitties(加密貓遊戲)就是基於ERC721實現的。

Partially Fungible Token是什麼

如果把可替代通證與不可替代通證看做兩端,部分可替代通證(Partially Fungible Token,簡稱PFT)就是兼具了可替代性和不可替代性2種屬性的通證形態。

部分可替代通證是如何做到兼具可替代性和不可替代性的呢?

ERC1410引入一個新的名詞叫作“Tranche”,在證券行業中通常翻譯爲“分級”(例如AAA級、AA級、次級),目的是將通證持有者的賬戶內Token分成不同的tranches份額,比如用戶Alice擁有100個Token,其中tranche1是20個,tranche2是30個,tranche3是50個。不同的tranche具有不同的屬性(元數據,metadata),基於這些元數據,對tranche進行不同的控制。比如tranche1只能用作投票,tranche2只能用於股權確認,tranche3只能用於流通交易。從而在更小的業務粒度上管理賬戶的數字資產。

不支持tranche的通證賬戶:

支持tranche的通證賬戶:

ERC1410技術細節剖析

ERC1410提案的核心部分是tranche,所有提供的操作接口都圍繞tranche展開。每個tranche用一個key(類型爲bytes32)表示,每個賬戶地址下可以有多個tranches。

與tranche有關的查詢接口

查詢某個賬戶下指定 tranche 的餘額:

function balanceOfByTranche(bytes32 _tranche,address _tokenHolder) external view returns (uint256);

查詢某個賬戶下所有的tranches:

function tranchesOf(address _tokenHolder) external view returns (bytes32[]);

與tranche有關的轉賬接口

舉個例子,Alice想要用自己的賬戶給Bob的賬戶轉賬,那麼她需要考慮如下問題:

  1. 要選取的是賬戶中哪個tranche?
  2. 要轉到Bob賬戶的哪個tranche?
  3. 轉賬的數量是多少?

針對問題1,可以指定一個tranche,或默認的1個或多個tranches(默認tranches將在後文講述);針對問題2,由業務邏輯決定;針對問題3,需要由接口指定。

sendByTranche

function sendByTranche(bytes32 _tranche,address _to,uint256_amount,bytes _data) external returns (bytes32);

上述接口表示從調用者的指定tranche轉指定金額到目的賬戶。

  • 如果轉賬交易不能完成,函數必須revert;
  • 如果轉賬成功,必須emit SentByTranche event;
  • 如果轉賬成功但接收者的tranche與發送者的tranche不同,則必須emit ChangedTranche event;
  • 返回值爲目的賬戶接受此次轉賬的tranche;
  • 最後一個參數_data,可以將目標tranche直接用此參數指定,或存放任何與該交易相關的數據,比如授權信息。

sendByTranches

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

上述接口是sendByTranche接口的升級版本,從指定的多個tranches中,往多個目標地址進行轉賬。

  • 如果轉賬交易不能完成,函數必須revert;
  • 如果轉賬成功,必須emit SentByTranche event;
  • 如果轉賬成功但接收者的tranche與發送者的tranche不同,則必須emit ChangedTranche event

sendByTranche、sendByTranches接口均爲交易發起者(msg.sender)對自有賬戶的操作。

ERC1410基於 ERC777繼承了交易員(operator)的相關概念,允許某個交易員代表某個賬戶持有者基於tranche進行轉賬。operatorSendByTranche和operatorSendByTranches就是該類型接口。

operatorSendByTranche:

function operatorSendByTranche(bytes32 _tranche,address _from,address _to,uint256 _amount,bytes _data,bytes _operatorData) external returns (bytes32);

  • 如果一個未被授權(isOperatorForTranche)的地址調用,函數必須revert;
  • 如果轉賬成功,則必須emit SentByTranche event;
  • 如果轉賬成功且接收者的tranche與發送者的tranche不同,則必須emit ChangedTranche event。

operatorSendByTranches:

function operatorSendByTranches(bytes32[] _tranches,address _from,address _to,uint256[] _amounts,bytes _data,bytes _operatorData) external returns (bytes32[]);

  • 如果一個未被授權(isOperatorForTranche)的地址調用,函數必須revert;
  • 如果轉賬成功,則必須emit SentByTranche event;
  • 如果轉賬成功且接收者的tranche與發送者的tranche不同,則必須emit ChangedTranche event。

默認tranche管理

setDefaultTranches:

爲了保證與ERC777、ERC20的兼容性,當調用send時,需要決定從哪個或哪幾個tranches中轉出。爲解決這個問題,可以指定某一個或某幾個tranches爲默認。

function setDefaultTranche(bytes32[] _tranches) external;

設置默認tranches,這樣在調用send時,將從default tranches中轉賬。

getDefaultTranches:

function getDefaultTranches(address _tokenHolder) external view returns (bytes32[]);

獲得某個賬戶的默認tranches。如果返回值爲空,則調用send會拋出異常。如果返回多個,則可以按照某種策略進行轉出。

交易員(Operator)相關接口

交易員可以被授權操作:

  • 所有賬戶的所有tranches
  • 所有賬戶的某個特定的tranche
  • 某個特定賬戶的所有tranches(包括現在與未來的)
  • 某個特定賬戶的特定tranche

defaultOperatorsByTranche:

function defaultOperatorsByTranche(bytes32 _tranche) external view returns (address[]);

返回具有所有賬戶的某個特定tranche的默認操作員列表。

authorizeOperatorByTranche:

function authorizeOperatorByTranche(bytes32 _tranche,address _operator) external;

消息發送者授權給某個交易員某個特定tranche的操作權。每次被調用,必須emit AuthorizedOperatorByTranche event。

revokeOperatorByTranche:

function revokeOperatorByTranche(bytes32 _tranche,address _operator) external;

消息發送者撤銷某個交易員對某個特定tranche的操作權。每次被調用,必須emit RevokedOperatorByTranche event。

(注: 此Operator仍有可能通過defaultOperatorsByTranche或defaultOperators擁有對此tranche的操作權。)

isOperatorForTranche:

function isOperatorForTranche(bytes32 _tranche,address _operator,address _tokenHolder) external view returns (bool);

查詢_operator是否是某個賬戶特定tranche的操作員。

兼容性

爲了使新標準與ERC20/ERC777(另一個NFT標準)兼容,需要定義在transfer/send操作時,哪些tranches會被用到。ERC1410中擴展的getDefaultTranches/setDefaultTranche規則將會被用到。

function getDefaultTranches(address _tokenHolder) external view returns (bytes32[]);

function setDefaultTranche(bytes32[] _tranches) external;

token的創建者必須爲所有的通證持有人定義默認的tranche(s)(使用tranchesOf),以供ERC20/ERC777相關函數使用。而每個單獨的賬戶所有者也可以針對性的修改其默認tranche(s)。

以下行爲是在實施ERC777函數中的注意點:

  • send()必須調用getDefaultTranche獲得默認tranche
  • operatorSend()必須調用getDefaultTranche獲得默認tranche
  • burn()必須調用getDefaultTranche獲得默認tranche
  • operatorBurn()必須調用getDefaultTranche獲得默認tranche
  • balanceOf()必須遍歷token持有者的所有tranche得到總值
  • totalSupply()必須計算合約中的所有token
  • defaultOperators()必須返回能操作所有賬戶與tranche的操作員列表
  • authorizeOperator()必須針對msg.sender的所有tranche進行授權
  • revokenOperator()必須撤銷操作員對msg.sender所有tranche的授權
  • isOperatorFor()必須查詢_operator是否對_tokenHolder的所有tranches具有授權
  • 無論何種原因,如果token總量增加,必須emit兩個event : Minted(),MintedByTranche()
  • 無論何種原因,如果token總量減少,必須emit兩個event : Burned(),BurnedByTranche()
  • authorizeOperator()必須emit AuthorizedOperator()
  • revokeOperator()必須emit RevokedOperator()

應用場景

ERC1410帶來的部分可替換通證能力,爲證券通證化帶來了可能。通過關聯元數據,還能帶來豐富的功能邏輯和更靈活的權限控制。

證券型通證發行

  • 對證券型通證增加元數據來實現細粒度功能控制。比如股權和投票權分離機制下,一些tranche對應股權信息,一些tranche對應投票權。
  • 通證鎖定機制。部分通證具有鎖定期,比如25%鎖定一年,75%鎖定兩年。則可以劃分成三個tranches:不受限部分,受限1年部分,受限2年部分。沒到達鎖定期前,不能將受限Token轉出到不受限制的tranche中;但是可以對相同tranche的Token進行互轉。
  • 關聯公共數據到證券(比如發行者信息,KYC/AML,相關法律文件),加強通證的信息披露和合規能力。

遊戲點卡控制

遊戲內的道具需要用點卡購買,爲吸引玩家,玩家存入10個點數(Token)贈送2個點數(此刻玩家一共獲得12個點數)。但從細粒度來看,這12個點數分爲兩個 tranches:10個是玩家自有的,可提現但需要優先消費;2個是系統贈送的,不可提現可後消費。在智能合約內就可以實現對不同tranches的不同控制邏輯。

總結

STO的興起,意味着區塊鏈通證主動進入證券法的監管範圍。ERC1410結合可替代通證和證券業務的特點,在技術層面將通證分割成不同的tranches(份額),不同tranches擁有不同的業務邏輯,從而賦予智能合約對通證的細粒度控制能力。

雖然ERC1410目前還處於草案階段,需要在社區進行廣泛聽證、討論後才能正式實施,但從可監管的角度來看,ERC1410依舊是證券通證化的一個良好開端,筆者團隊也將持續跟進,爲讀者解讀標準的後續進展。

參考鏈接

ERC 1400: Security Token Standard #1411

https://github.com/ethereum/EIPs/issues/1411

ERC 1410: Partially Fungible Token Standard #1410

https://github.com/ethereum/EIPs/issues/1410

Security Token Standard

https://github.com/SecurityTokenStandard/EIP-Spec

作者簡介:Codefine好碼安全團隊專注於智能合約安全審計和全生命週期管理, 已爲全球多家交易所、錢包、公鏈做過智能合約安全審計和開發管理。團隊通過獨有的多維審計引擎,持續爲合作伙伴提供正確、安全、可用的智能合約基礎設施。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章