99久久国产亚洲高清观看2020_国产免费成人_四虎国产精品永久地址入口_国产精品免费一区二区久久夜色_美国超碰在线_男人天堂tv

您的位置:首頁 > 互聯網 >

ERC777可定義代幣的生命周期 兼容ERC20標準

2019-10-04 11:00:10 來源: 區塊網

想必很多同學都已經使用過ERC20 創建過代幣[1],或許已經被老板要求在ERC20代幣上實現一些附加功能搞的焦頭爛額,如果還有選擇,一定要選擇

想必很多同學都已經使用過ERC20 創建過代幣[1],或許已經被老板要求在ERC20代幣上實現一些附加功能搞的焦頭爛額,如果還有選擇,一定要選擇 ERC777 。

ERC20 的問題

以下是一個遇到很多次的場景:有一天老板過來找你(開發者),最近存幣生息很火,我們也做一個合約吧, 用戶打幣過來給他計算利息, 看起來是一個很簡單的需求,你滿口答應說好,結果自己一研究發現,使用 ERC20 標準沒辦法在合約里記錄是誰發過來多少幣,從而沒法計算利息(因為接收者合約并不知道自己接收到ERC20代幣)。

ERC20 標準下,可以通過一個變通的辦法,采用兩個交易組合完成,方法是:第1步:先讓用戶把要轉移的金額用 ERC20 的approve 授權的存幣生息合約(這步通常稱為解鎖),第2步:再次讓用戶調用存幣生息合約的計息函數,計息函數中通過 transferFrom 把代幣從用戶手里轉移的合約內,并開始計息。

同樣由于ERC20 標準沒有一個轉賬通知機制,很多ERC20代幣誤轉到合約之后,再也沒有辦法把幣轉移出來,已經有大量的ERC20 因為這個原因被鎖死,如鎖死的QTUM[2],鎖死的EOS[3] 。

另外一個問題是ERC20 轉賬時,無法攜帶額外的信息,例如:我們有一些客戶希望讓用戶使用 ERC20 代幣購買商品,因為轉賬沒法攜帶額外的信息, 用戶的代幣轉移過來,不知道用戶具體要購買哪件商品,從而展加了線下額外的溝通成本。

ERC777很好的解決了這些問題,同時ERC777 也兼容 ERC20 標準。因此強烈建議新開發的代幣使用ERC777標準。

ERC777 在 ERC20的基礎上定義了 send(dest, value, data) 來轉移代幣, send函數額外的參數用來攜帶其他的信息,send函數會檢查持有者和接收者是否實現了相應的鉤子函數,如果有實現(不管是普通用戶地址還是合約地址都可以實現鉤子函數),則調用相應的鉤子函數。

ERC1820 接口注冊表合約

即便是一個普通用戶地址,同樣可以實現對 ERC777 轉賬的監聽, 聽起來有點神奇,其實這是通過 ERC1820 接口注冊表合約來是實現的。

ERC1820 如此的重要,以至于ERC777單獨把它拆出來作為一個EIP。

ERC1820 是一個全局的合約,有一個唯一在以太坊鏈上都相同的合約地址,它總是 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 ,這個合約是通過非常巧妙的方式進行部署的,有興趣的同學可以閱讀EIP1820文檔[4]。

ERC 1820 合約的官方實現代碼在ERC1820文檔[5]可以查閱,這里說明合約實現的主要內容。

ERC1820合約提過了兩個主要接口:

•setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) 用來設置地址(_addr)的接口(_interfaceHash 接口名稱的 keccak256 )由哪個合約實現(_implementer)。

•getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) 這個函數用來查詢地址(_addr)的接口由哪個合約實現。

setInterfaceImplementer函數會參數信息記錄到下面這個interfaces映射里:

// 記錄 地址(第一個鍵) 的接口(第二個鍵)的實現地址(第二個值)

mapping(address => mapping(bytes32 => address)) interfaces;

相對應的 getInterfaceImplementer() 通過 interfaces 這個mapping 來獲得接口的實現。

ERC777 使用 send轉賬時會分別在持有者和接收者地址上使用ERC1820 的getInterfaceImplementer函數進行查詢,查看是否有對應的實現合約,ERC777 標準規范里預定了接口及函數名稱,如果有實現則進行相應的調用。

ERC777 標準規范

ERC777 接口

ERC777 為了在實現上可以兼容ERC20,除了查詢函數和ERC20一致外,操作接口均采用的獨立的命名(避免相同的命令無法分辨是哪個標準),ERC777的接口定義如下,要求所有的ERC777代幣合約都必須實現這些接口:

interface ERC777Token {

function name() external view returns (string memory);

function symbol() external view returns (string memory);

function totalSupply() external view returns (uint256);

function balanceOf(address holder) external view returns (uint256);

// 定義代幣最小的劃分粒度

function granularity() external view returns (uint256);

// 操作員 相關的操作(操作員是可以代表持有者發送和銷毀代幣的賬號地址)

function defaultOperators() external view returns (address[] memory);

function isOperatorFor(

address operator,

address holder

) external view returns (bool);

function authorizeOperator(address operator) external;

function revokeOperator(address operator) external;

// 發送代幣

function send(address to, uint256 amount, bytes calldata data) external;

function operatorSend(

address from,

address to,

uint256 amount,

bytes calldata data,

bytes calldata operatorData

) external;

// 銷毀代幣

function burn(uint256 amount, bytes calldata data) external;

function operatorBurn(

address from,

uint256 amount,

bytes calldata data,

bytes calldata operatorData

) external;

// 發送代幣事件

event Sent(

address indexed operator,

address indexed from,

address indexed to,

uint256 amount,

bytes data,

bytes operatorData

);

// 鑄幣事件

event Minted(

address indexed operator,

address indexed to,

uint256 amount,

bytes data,

bytes operatorData

);

// 銷毀代幣事件

event Burned(

address indexed operator,

address indexed from,

uint256 amount,

bytes data,

bytes operatorData

);

// 授權操作員事件

event AuthorizedOperator(

address indexed operator,

address indexed holder

);

// 撤銷操作員事件

event RevokedOperator(address indexed operator, address indexed holder);

}

接口定義在 openzeppelin代碼庫[6] 里找到,路徑為:contracts/token/ERC777/IERC777.sol 。

接口說明與實現約定

所有的ERC777 合約除了必須實現上述接口,還有一些其他的必須遵守的約定(直接導致了ERC777官方文檔又長又臭...哭~)。

ERC777 合約必須要通過 ERC1820 注冊 ERC777Token 接口,這樣任何人都可以查詢合約是否是ERC777標準的合約,注冊方法是: 調用ERC1820 注冊合約的 setInterfaceImplementer 方法,參數 _addr 及 _implementer 均是合約的地址,_interfaceHash 是 ERC777Token 的 keccak256 哈希值(0xac7fbab5...177054)

如果 ERC777 要實現ERC20標準,還必須通過ERC1820 注冊ERC20Token接口。

ERC777 信息說明函數

name(),symbol(),totalSupply(),balanceOf(address) 和含義和在ERC20 中完全一樣。

granularity() 用來定義代幣最小的劃分粒度(>=1), 要求必須在創建時設定,之后不可以更改,不管是在鑄幣、發送還是銷毀操作的代幣數量,必需是粒度的整數倍。

granularity 和 ERC20 的 decimals 不一樣,decimals用來定義小數位數,decimals 是ERC20 可選函數,為了兼容 ERC20 代幣, decimals 函數要求必須返回18。而 granularity 表示的是基于最小位數(內部存儲)的劃分粒度。例如:0.5個代幣存儲為 500,000,000,000,000,000 (0.5 X 10^18),如果粒度為2,則最小轉賬單位是2(相對于500,000,000,000,000,000)。

操作員

ERC777 定義了一個新的操作員角色,操作員被作為移動代幣的地址。每個地址直觀地移動自己的代幣,將持有人和操作員的概念分開可以提供更大的靈活性。

與ERC20中的 approve 、 transferFrom 不同,其未明確定義批準地址的角色。

此外,ERC777還可以定義默認操作員(默認操作員列表只能在代幣創建時定義的,并且不能更改),默認操作員是被所有持有人授權的操作員,這可以為項目方管理代幣帶來方便,當然認何持有人仍然有權撤銷默認操作員。

操作員相關的函數:

•defaultOperators(): 獲取代幣合約默認的操作員列表.

•authorizeOperator(address operator): 設置一個地址作為msg.sender 的操作員,需要觸發AuthorizedOperator事件。

•revokeOperator(address operator): 移除 msg.sender 上 operator 操作員的權限, 需要觸發RevokedOperator事件。

•isOperatorFor(address operator, address holder):是否是某個持有者的操作員。

發送代幣

ERC777 發送代幣 使用以下兩個方法:

send(address to, uint256 amount, bytes calldata data) external

function operatorSend(

address from,

address to,

uint256 amount,

bytes calldata data,

bytes calldata operatorData

) external

operatorSend 可以通過參數operatorData攜帶操作者的信息,發送代幣除了執行對應賬戶的余額加減和觸發事件之外,還有額外的規定:

1.如果持有者有通過 ERC1820 注冊 ERC777TokensSender 實現接口, 代幣合約必須調用其 tokensToSend 鉤子函數。

2.如果接收者有通過 ERC1820 注冊 ERC777TokensRecipient 實現接口, 代幣合約必須調用其 tokensReceived 鉤子函數。

3.如果有 tokensToSend 鉤子函數,必須在修改余額狀態之前調用。

4.如果有 tokensReceived 鉤子函數,必須在修改余額狀態之后調用。

5.調用鉤子函數及觸發事件時, data 和 operatorData必須原樣傳遞,因為 tokensToSend 和 tokensReceived 函數可能根據這個數據取消轉賬(觸發 revert)。

ERC777TokensSender 接口定義如下:

interface ERC777TokensSender {

function tokensToSend(

address operator,

address from,

address to,

uint256 amount,

bytes calldata userData,

bytes calldata operatorData

) external;

}

如果持有者希望在轉賬時收到代幣轉移通知,就需要在ERC1820合約上注冊及實現 ERC777TokensSender 接口(稍后有案例介紹)。

有一個地方需要注意: 對于所有的 ERC777 合約, 一個持有者地址只能注冊一個ERC777TokensSender接口實現。因此 ERC777TokensSender 實現會被多個ERC777合約調用,在ERC777TokensSender接口的實現合約里, msg.sender 是ERC777合約地址,而不是操作者。

ERC777TokensRecipient 接口定義如下:

interface ERC777TokensRecipient {

function tokensReceived(

address operator,

address from,

address to,

uint256 amount,

bytes calldata data,

bytes calldata operatorData

) external;

}

如果接收者希望在轉賬時收到代幣轉移通知,就需要在ERC1820合約上注冊及實現 ERC777TokensRecipient 接口。

如果接收者是一個合約地址, 則必須要注冊及實現 ERC777TokensRecipient 接口(這樣可以防止代幣被鎖死),如果沒有實現,ERC777代幣合約必須revert 回退交易狀態。

鑄幣與銷毀

鑄幣(挖礦)是產生新幣的過程,銷毀代幣則相反,在ERC20 中,沒有明確定義這兩個行為,通常會transfer方法和Transfer事件來表達。ERC777 則定義了代幣從鑄幣、轉移到銷毀的整個生命周期。

ERC777 沒有定義鑄幣的方法名,只定義了 Minted事件,因為很多代幣,是在創建的時候就確定好代幣的數量。如果有需要合約可以自己定義鑄幣函數,鑄幣函數在實現時要求:

1.必須觸發Minted事件

2.發行量需要加上鑄幣量, 接收者是不為 0 ,且接收者余額加上鑄幣量。

3.如果接收者有通過 ERC1820 注冊 ERC777TokensRecipient 實現接口, 代幣合約必須調用其 tokensReceived 鉤子函數。

ERC777 定義了兩個函數用于銷毀代幣 (burn 和 operatorBurn),可以方便錢包和dapps有統一的接口交互。burn 和 operatorBurn 的實現要求:

1.必須觸發Burned事件。

2.總供應量必須減少代幣銷毀量, 持有者的余額必須減少代幣銷毀的數量。

3.如果持有者通過ERC1820注冊ERC777TokensSender 實現,必須調用持有者的tokensToSend鉤子函數。

注意,零個代幣數量的交易(不管是轉移、鑄幣與銷毀)也是合法的,同樣滿足粒度(granularity) 的整數倍,因此需要正確處理。

ERC777 代幣實現

OpenZeppelin 實現了一個 ERC777 基礎合約,要實現自己的ERC777代幣只需要繼承 OpenZeppelin ERC777。想了解 OpenZeppelin 的 ERC777 的實現可閱讀ERC777 源碼解析[7]。

如果大家是Truffle開發(或者是Node工程),可以使用以下方式安裝 OpenZeppelin 合約庫:

npm install @openzeppelin/contracts

發行一個 2100 個的 LBC7 代幣的代碼就很簡單了:

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC777/ERC777.sol";

contract MyERC777 is ERC777 {

constructor(

address[] memory defaultOperators

)

ERC777("MyERC777", "LBC7", defaultOperators)

public

{

uint initialSupply = 2100 * 10 ** 18;

_mint(msg.sender, msg.sender, initialSupply, "", "");

}

}

實現主要是兩步:通過基類ERC777的構造函數確認代幣名稱、代號以及默認操作員(可為空),然后調用 _mint 初始化發行量,注意發行量的小數位是固定的18位(和ether保持一致),在合約內部是按小數位保存的,因此發行的幣數需要乘上1018。

監聽代幣收款

我們假設有這樣一個需求:寺廟要實現了一個功德箱合約接收捐贈,功德箱合約需要記錄每位施主的善款金額。這時候就可以通過實現 ERC777TokensRecipient接口來完成。代碼也很簡單:

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";

import "@openzeppelin/contracts/token/ERC777/IERC777.sol";

import "@openzeppelin/contracts/introspection/IERC1820Registry.sol";

contract Merit is IERC777Recipient {

mapping(address => uint) public givers;

address _owner;

IERC777 _token;

IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);

// keccak256("ERC777TokensRecipient")

bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH =

0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;

constructor(IERC777 token) public {

_erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this));

_owner = msg.sender;

_token = token;

}

// 收款時被回調

function tokensReceived(

address operator,

address from,

address to,

uint amount,

bytes calldata userData,

bytes calldata operatorData

) external {

givers[from] += amount;

}

// 方丈取回功德箱token

function withdraw () external {

require(msg.sender == _owner, "no permision");

uint balance = _token.balanceOf(address(this));

_token.send(_owner, balance, "");

}

}

功德箱合約在構造時,調用 ERC1820 注冊表合約的 setInterfaceImplementer函數 注冊ERC777TokensRecipient接口實現(接口的實現是自身),這樣在收到代幣時,會回調 tokensReceived函數,tokensReceived函數通過givers映射來保存每個施主的善款金額。

注意:如果是在本地的開發者網絡環境,可能會沒有ERC1820 注冊表合約,如果沒有需要先部署ERC1820注冊表合約,參考eip-1820 中文文檔[8]。

功德箱這個實例僅僅是拋磚引玉,告訴大家如何實現收款時的回調,之后有時間,我寫一個完整的存幣生息應用。

普通賬戶地址監聽代幣轉出

功德箱合約的例子,收款地址和收款監聽是同一個合約, 現在來看看一個普通的用戶地址,如何委托一個合約來監聽代幣的轉出。監聽代幣的轉出可以讓持有者對發出去的代幣有更多的控制,例如持有者可以設置一些黑名單,禁止操作員對黑名單內賬號轉賬。(Tiny 熊)

關鍵詞: ERC777 代幣 ERC20

精選 導讀

募資55億港元萬物云啟動招股 預計9月29日登陸港交所主板

萬科9月19日早間公告,萬物云當日啟動招股,預計發行價介乎每股47 1港元至52 7港元,預計9月29日登陸港交所主板。按發行1 167億股計算,萬

發布時間: 2022-09-20 10:39
管理   2022-09-20

公募基金二季度持股情況曝光 隱形重倉股多為高端制造業

隨著半年報披露收官,公募基金二季度持股情況曝光。截至今年二季度末,公募基金全市場基金總數為9794只,資產凈值為269454 75億元,同比上

發布時間: 2022-09-02 10:45
資訊   2022-09-02

又有上市公司宣布變賣房產 上市公司粉飾財報動作不斷

再有上市公司宣布變賣房產。四川長虹25日稱,擬以1 66億元的轉讓底價掛牌出售31套房產。今年以來,A股公司出售房產不斷。根據記者不完全統

發布時間: 2022-08-26 09:44
資訊   2022-08-26

16天12連板大港股份回復深交所關注函 股份繼續沖高

回復交易所關注函后,大港股份繼續沖高。8月11日大港股份高開,隨后震蕩走高,接近收盤時觸及漲停,報20 2元 股。值得一提的是,在7月21日

發布時間: 2022-08-12 09:56
資訊   2022-08-12

萬家基金再添第二大股東 中泰證券擬受讓11%基金股權

7月13日,中泰證券發布公告,擬受讓齊河眾鑫投資有限公司(以下簡稱齊河眾鑫)所持有的萬家基金11%的股權,交易雙方共同確定本次交易的標的資

發布時間: 2022-07-14 09:39
管理   2022-07-14

央行連續7日每天30億元逆回購 對債市影響如何?

央行12日再次開展了30億元逆回購操作,中標利率2 10%。這已是央行連續7日每天僅進行30億元的逆回購縮量投放,創下去年1月以來的最低操作規

發布時間: 2022-07-13 09:38
資訊   2022-07-13

美元指數創近20年新高 黃金期貨創出逾9個月新低

由于對美聯儲激進加息的擔憂,美元指數11日大漲近1%創出近20年新高。受此影響,歐美股市、大宗商品均走弱,而黃金期貨創出逾9個月新低。美

發布時間: 2022-07-13 09:36
資訊   2022-07-13

美股三大股指全線下跌 納斯達克跌幅創下記錄以來最大跌幅

今年上半年,美股持續回落。數據顯示,道瓊斯指數上半年下跌15 3%,納斯達克綜合指數下跌29 5%,標普500指數下跌20 6%。其中,納斯達克連續

發布時間: 2022-07-04 09:51
推薦   2022-07-04

融資客熱情回升 兩市融資余額月內增加超344億元

近期A股走強,滬指6月以來上漲4%,融資客熱情明顯回升。數據顯示,截至6月16日,兩市融資余額1 479萬億元,月內增加344 67億元,最近一個半

發布時間: 2022-06-20 09:41
資訊   2022-06-20

4個交易日凈買入超百億元 北向資金持續流入A股市場

北向資金凈流入態勢延續。繼6月15日凈買入133 59億元后,北向資金6月16日凈買入44 52億元。自5月27日至今,除6月13日以外,北向資金累計凈

發布時間: 2022-06-17 09:37
推薦   2022-06-17