ERC1410 (等同ERC1411)將 ERC20/ERC777 中不存在解釋屬性的餘額,附加額外的資訊,從而劃分成不同的部分,就可以做一些操作上的限制。而 ERC1400 (等同ERC1411)是對 ERC1410 標準的繼承和改進。
1.摘要
ERC1410為STO環境中使用的一個以太坊協議標準。輝哥著眼於深度理解和編碼實現,從以下幾個方面闡述對ERC1410的理解。
1) ERC1410和ERC1411(ERC1400),ERC1404的區別
2)同質化通證,非同質化通證,部分同質化通證的區別
3)ERC1410標準的資料結構分析
4) ERC1410的介面函式分析
5) ERC1410的場景嘗試
6) 程式碼部署和測試
2. ERC1410和ERC1411(ERC1400),ERC1404的區別
ERC1410標準由 Gosselin, Adam Dossa, Pablo Ruiz 和 Fabian Vogelsteller 撰寫,其中 Gosselin 和 Dossa 為 Polymath 工作,就是那個釋出ST20.幫助資產實現證券化通證的平臺公司。
ERC1410是一種以太坊上實現STO的技術方案,它相容ERC20和ERC777標準的證券型通證發行標準,引入了部分同質化通證(Partially Fungible Token Standard)的概念。
它在GITHUB的地址為ERC 1410: Partially Fungible Token Standard #1410.
ERC1400同ERC1411是同一個標準,現在在GITHUB上已經把他們合併了。ERC1411(ERC1400)則是繼承ERC1410標準,增加了證券相關業務會使用到的函式:證券增發,相關法律檔案儲存等。
它在GITHUB的地址為ERC 1400: Security Token Standard #1411.
ERC1404是指簡單的受限代幣標準,ERC-1404則在ERC-20標準的基礎上,新增了兩種函式。
detectTransferRestriction:此函式是發行者強制執行通證傳輸的限制邏輯。例子包括,(1)檢查通證接收者是否在白名單內,(2)檢查傳送者的通證是否在鎖定期內被凍結等等。該函式實現僅面向發行者,另外,第三方可以公開呼叫該函式來檢查轉移的預期結果。因為這個函式會返回一個uint8程式碼,所以它允許函式呼叫者知道傳輸失敗的原因,並將其報告給相關的對方。
messageForTransferRestriction:這個函式實際上是一個“訊息”訪問器,它負責以人類可閱讀的方式解釋一筆交易為什麼會被限制。透過規範訊息查詢,開發者授權使用者介面構建器,有效地向使用者報告錯誤。
它在GITHUB的地址為https://github.com/ethereum/EIPs/issues/1404.
3.同質化通證(FT),非同質化通證(NFT),部分同質化通證(PFT)的區別
ERC1410引入了部分同質化通證(Partially Fungible Token Standard)的概念,在此之前介紹下同質化通證(FT),非同質化通證(NFT)的概念。
所有的ERC20 Token都是“同質化通證”(Fungible Token,簡稱FT),同一通證內兩個相等單位的Token並無差別。這個類似於美元,作為一般等價物,假設都是真鈔,你手裡的美元和我手裡的美元在購物能力上並無區別。
ERC721 Token都是“非同質化通證”(Non-Fungible Token,簡稱NFT)。非同質代表獨一無二,謎戀貓為例,每隻貓都被賦予擁有基因,是獨一無二的(一隻貓就是一個NFTs),貓之間是不能直接替換的。
非同質性其實廣泛存在於我們的生活中,如圖書館的每一本,寵物商店的每一隻寵物,歌手所演唱的歌曲,花店裡不同的花等等,因此ERC721合約必定有廣泛的應用場景,例如房地產,畫作等上鍊具有廣泛的意義。
ERC1410 Token是部分可替代通證(Partially Fungible Token,簡稱PFT)就是兼具了可替代性和不可替代性2種屬性的通證形態。它可以把TOKEN跟證券對等起來,可以做security token的一些功能。ERC1410標準引入了“Tranche”的名字,在證券行業中通常翻譯為“分級”(例如AAA級、AA級、次級),目的是將不同的通證持有者的賬戶內Token分成不同的tranches級別。舉個例子來說,輝哥發起主導了一個名為“BIG STO PROGRAMME”的專案,由輝哥,歐陽和ELLA小姐姐一起參與,並且已經經過了天使輪(A資本投資),A輪(I資本)參與,專案發展不錯,正準備到美國做STO申請。那麼,創始團隊(創始股),A資本(天使輪),I資本(A輪)這些投資方對應的價格,基金到期時間都不相同,可以定義tranches級別分別為1級,2級,3級等。
這3級的TOKEN具有相同性和不同性,後面場景嘗試的時候會深入分析。
TOKEN持倉情況
4.ERC1410標準的資料結構分析
1.TOKEN總供應量
uint256 public totalSupply;
2.TOKEN名稱
string public name;
3.TOKEN標識
string public symbol;
4.覆蓋所有投資人所持通證的每個賬戶地址下的通證餘額總額
// Mapping from investor to aggregated balance across all investor token sets
mapping (address => uint256) balances;
5. 投資人到分級份額Tranche[]的對映
// Mapping from investor to their tranches
mapping (address => Tranche[]) tranches;
// Represents a fungible set of tokens.
struct Tranche {
uint256 amount;
bytes32 tranche;
}
6. trancheToIndex-分級份額索引
// Mapping from (investor, tranche) to index of corresponding tranche in tranches
//三維陣列,就是在分級份額Tranches對映(investor, tranche) 到對應分級份額的索引(index)
mapping (address => mapping (bytes32 => uint256)) trancheToIndex;
7.交易員針對tranche,的授權狀態
// Mapping from (investor, tranche, operator) to approved status
//四維陣列,就是三維陣列(investor, tranche, operator)的授權狀態
mapping (address => mapping (bytes32 => mapping (address => bool))) trancheApprovals;
8.交易員針對投資人的授權狀態
// Mapping from (investor, operator) to approved status (can be used against any tranches)
//三維陣列,就是 投資人對應的操作員(investor, operator)的授權狀態
mapping (address => mapping (address => bool)) approvals;
5.ERC1410的介面函式分析
5.1 與tranche有關的查詢介面
5.1.1 查詢某個賬戶下指定 tranche 的餘額:
function balanceOfByTranche(bytes32 _tranche,address _tokenHolder) external view returns (uint256);
查詢某個賬戶下所有的tranches:
function tranchesOf(address _tokenHolder) external view returns (bytes32[]);
5.2 與tranche有關的轉賬介面
5.2.1 從呼叫者的指定tranche轉指定金額到目的賬戶
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直接用此引數指定,或存放任何與該交易相關的資料,比如授權資訊。
5.2.2 從指定的多個tranches往多個目標地址進行轉賬。
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)對自有賬戶的操作。
5.2.3 交易員轉賬特定Tranche
ERC1410基於 ERC777繼承了交易員(operator)的相關概念,允許某個交易員代表某個賬戶持有者基於tranche進行轉賬。operatorSendByTranche和operatorSendByTranches就是該型別介面。
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。
5.2.4 交易員轉賬多個Tranches:
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。
5.3 預設tranche管理
5.3.1 設定預設Tranches(setDefaultTranches)
為了保證與ERC777、ERC20的相容性,當呼叫send時,需要決定從哪個或哪幾個tranches中轉出。為解決這個問題,可以指定某一個或某幾個tranches為預設。
function setDefaultTranche(bytes32[] _tranches) external;
設定預設tranches,這樣在呼叫send時,將從default tranches中轉賬。
5.3.2 獲取預設Tranches(getDefaultTranches)
function getDefaultTranches(address _tokenHolder) external view returns (bytes32[]);
獲得某個賬戶的預設tranches。如果返回值為空,則呼叫send會丟擲異常。如果返回多個,則可以按照某種策略進行轉出。
5.3.3 交易員(Operator)相關介面
交易員可以被授權操作:
所有賬戶的所有tranches
所有賬戶的某個特定的tranche
某個特定賬戶的所有tranches(包括現在與未來的)
某個特定賬戶的特定tranche
5.3.3.1 defaultOperatorsByTranche
function defaultOperatorsByTranche(bytes32 _tranche) external view returns (address[]);
返回具有所有賬戶的某個特定tranche的預設操作員列表。
5.3.3.2 authorizeOperatorByTranche
function authorizeOperatorByTranche(bytes32 _tranche,address _operator) external;
訊息傳送者授權給某個交易員某個特定tranche的操作權。每次被呼叫,必須emit AuthorizedOperatorByTranche event。
5.3.3.3 revokeOperatorByTranche
function revokeOperatorByTranche(bytes32 _tranche,address _operator) external;
訊息傳送者撤銷某個交易員對某個特定tranche的操作權。每次被呼叫,必須emit RevokedOperatorByTranche event。
(注: 此Operator仍有可能透過defaultOperatorsByTranche或defaultOperators擁有對此tranche的操作權。)
5.3.3.4 isOperatorForTranche:
function isOperatorForTranche(bytes32 _tranche,address _operator,address _tokenHolder) external view returns (bool);
查詢_operator是否是某個賬戶特定tranche的操作員。
6.ERC1410的場景嘗試
回到文章前面章節的故事,輝哥發起主導了一個名為“BIG STO PROGRAMME”的專案,由輝哥,歐陽和ELLA小姐姐一起出資參與,並且已經完成了天使輪(A資本投資),A輪(I資本),專案發展不錯,正準備到美國做STO申請。
我們假設以下為對應的合格投資人錢包地址:
輝哥地址:0xD1F7922e8b78cBEB182250753ade8379d1E09949
歐陽地址:0x17b1177E0491D25a13a8329a8D2C46621b6ca19F
ELLA地址:0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363
A資本地址:0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192
I資本地址:0xcE689dBB962DbF45534Af13e4414cCB2dFC78c30
交易員: 0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC
我們假設創始團隊(創始股),A資本(天使輪),I資本(A輪)這些投資方對應的價格,基金到期時間都不相同,可以定義tranches級別分別為1級,2級,3級等。
tranche:
00000000000000000000000000000001 創始團隊
00000000000000000000000000000002 天使輪
00000000000000000000000000000003 A輪
6.1 創始團隊發起原始TOKEN
“ BIG STO PROJECT”專案,由輝哥/歐陽/ELLA創始發起,分配總額度和TRANCHE屬性。
根據參與3人的投入的資金量,總髮行量為1000萬,由輝哥賬號建立,分配份額如下:輝哥 400萬,歐陽 300萬,ELLA 300萬。
1)首先建立PartialFungibleToken合約,設定初始屬性如下:
totalSupply = 0;
name = “Big STO Project”;
symbol = “BST”;
2)呼叫介面增發1000萬TOKEN。
mint("00000000000000000000000000000001", "0xD1F7922e8b78cBEB182250753ade8379d1E09949", "10000000.000000000000000000", "the founding team share tranche")
3) 輝哥轉發給歐陽,ELLA對應份額
sendTranche("00000000000000000000000000000001", "0x17b1177E0491D25a13a8329a8D2C46621b6ca19F","3000000.000000000000000000", "創始團隊歐陽份額,1級tranche") ;
sendTranche("00000000000000000000000000000001", "0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363","3000000.000000000000000000", "創始團ELLA份額,1級tranche") ;
6.2 A資本參與天使輪
A資本參與天使輪,再發行200萬份額,其中輝哥佔有100萬,A佔有100萬,鎖倉1年;跟輝哥簽訂對賭條約,公司營收在2018年達到1000萬元,設定操作員賬號質押輝哥TOKEN份額。
1) 增發200萬
mint("00000000000000000000000000000002", "0xD1F7922e8b78cBEB182250753ade8379d1E09949", "200000.000000000000000000", "the spinel captital joined in.")
2)A佔有100萬; 輝哥佔有100萬,有業績對賭;
sendTranche("00000000000000000000000000000002", 0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192","1000000.000000000000000000", "A資本參與天使輪,鎖倉1年") ;
3)輝哥業績對賭,TOKEN指定交易員授權;
authorizeOperator("0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
4)該專案進展順利,對賭失效
該專案進展順利,對賭失效,輝哥要撤回授權。
revokeOperator("0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
6.3 I資本參與A輪
該專案到2018年11月份就完成了全年目標,專案發展不錯。I資本決定參與A輪。再發行200萬TOKEN,其中輝哥佔有50萬,A佔有50萬,I佔有100萬。
1) 增發200萬TOKEN
mint("00000000000000000000000000000001", "0xD1F7922e8b78cBEB182250753ade8379d1E09949", "2000000.000000000000000000", "I CAPITAL joined in!")
sendTranche("00000000000000000000000000000003", 0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192","500000.000000000000000000", "A資本參與A輪,鎖倉1年") ;
sendTranche("00000000000000000000000000000003", "0xcE689dBB962DbF45534Af13e4414cCB2dFC78c30","1000000.000000000000000000", "I資本參與A輪,鎖倉1年") ;
2)跟輝哥對賭2級股權100萬
I資本跟輝哥有對賭條款,約定到2018年年底總營收能到1500萬人民幣,把輝哥天使輪對應的TOKEN質押,授權交易員。
authorizeOperatorTranche("00000000000000000000000000000002", " 0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
結果專案進展又特別順利,對賭失效,輝哥撤回授權。
revokeOperatorTranche("00000000000000000000000000000002", " 0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
6.4 A質押50萬2級TOKEN,找I資本融資5000萬,後續轉讓100萬給I資本,退出該專案
authorizeOperatorTranche("00000000000000000000000000000002", " 0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
後來A資本和I資本代表人到交易所辦理了轉讓交割手續。
operatorSendTranche("00000000000000000000000000000002", "0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192", "0xcE689dBB962DbF45534Af13e4414cCB2dFC78c30", "500000.000000000000000000", "A資本轉讓2級TOKEN給IDG資本", "由Polyman交易所交易員Mask辦理交割")
6.5 ELLA退出運營,銷燬對應代幣
ELLA因為要跟男票一起去蘇南農村做ELLA山莊,享受田園生活。所以申請退出“BIG STO PROJECT”運營。公司回購300萬,銷燬對應代幣。
burn("00000000000000000000000000000001", "0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363", "3000000.000000000000000000", "ELLA退出運營,縮股銷燬TOKEN。")
7.程式碼部署和測試
故事講完了,接下來就是部署測試。
這部分的程式碼量有點大,輝哥就不再全部列出了。有需要的小夥伴可以去知識星球下載來執行。
該工程程式碼還未經過商用測試,只作為練習使用。