解讀stETH的巧妙設計,它是如何按天自動發放收益的?

24-03-11 21:00
閱讀本文需 11 分鐘
总结 AI 總結
看總結 收起
原文標題:《巧妙的合約設計,看看 stETH 如何按天自動發放收益?讓你的ETH 參與質押獲取穩定利息》
原文來源:ZAN Team


筆者把自己為數不多的ETH 兌換為stETH後,發現 stETH 每天都在自然成長,不斷獲取收益。但是卻沒有看到帳戶有交易產生,這是為什麼呢?本文帶大家一起來看看背後的巧妙設計,揭開收益發放的秘密。


1 個stETH 過去幾天后已經有了一些收益


在這之前先介紹一下stETH 賺取收益背後的邏輯,也就是以太坊的質押(Staking),已經了解這部分概念的讀者可以直接跳到後面。



最初的以太坊和比特幣一樣是透過工作量證明(Proof of Work,PoW)來作為它的共識機制,但是PoW 因為耗電以及其它安全性和性能上發展的考慮,以太坊從2022 年9 月開始升級為權益證明(Proof of Stake,PoS)。


原本依靠算力挖礦來吸引礦工實現共識的以太坊,搖身一變,變成了依靠大家透過質押ETH 獲取投票權,透過投票來獲取收益,從而激勵大家透過PoS 的方式來實現共識。


透過質押 32 個 ETH 可以加入以太坊網絡,可以成為驗證者,負責儲存資料、處理交易以及向區塊鏈添加新區塊。只要運行將交易正確打包為新區塊並檢查其他驗證者工作,就會獲得 ETH 獎勵,這樣就相當於你可以透過質押的方式讓 ETH 可以擁有相對穩定的收益。


但是這樣的質押對於普通用戶來說還是太麻煩,畢竟32 個以太坊和一台要能夠全年無休接入以太坊網絡的專用計算機還是有一定的門檻的。而質押 ETH 會使得喪失了這部分 ETH 的流動性。於是就有了流動性質押衍生品(Liquid Staking Derivatives,LSD),它旨在解決傳統質押中的門檻和流動性問題,允許用戶質押 32 個以下的ETH,以及不需要自己擁有節點,而是把ETH 委託給第三方質押,並獲取相應的質押代幣(如Lido 的stETH 或Rocket Pool 的rETH),這些流動性代幣可以在其他平台上交易、借貸或用於其他金融活動,這樣,用戶既能更方便參與質押中獲得獎勵,又能保持資金的彈性。



所以stETH 本質的邏輯就是把ETH 給到Lido,Lido 會用這些ETH 去參與以太坊的PoS 以獲取收益,使用者會得到對應的stETH 作為憑證。接下來就是 Lido 要把收益發給這些擁有 stETH 的地址。


我們可以看到 stETH 的收益每天都會自動更新,下圖是我們測試的收益情況,對應每天都可以檢查加密錢包驗證相關內容。



但是到這裡我想熟悉智能合約開發的同學就會疑惑了:每天發放這麼少的收益,可能收益都不夠付GAS 的。


確實,如果 Lido 按照最簡單的做法來發放收益的話,那確實難以覆蓋 GAS 的成本。從我們的直覺來看,要往如此眾多的地址發送代幣,GAS 是難以想像的。


但是確實 Lido 就實現了錢包中的 stETH 收益自動增長,而且我們並沒有發現該地址有任何交易,這是怎麼實現的呢?


我們找到了Lido 的合約 https://etherscan.io/token/0xae7ab96520de3a18e5e111b5eaab095312d7fe84 追溯到合約的balanceOf 方法:



balanceOf 是符合ERC20 規範的方法,錢包就是透過此方法取得使用者有多少token 的。


我們可以看到 stETH 的合約中這裡呼叫了 getPooledEthByShares 方法。此方法入參是 mapping (address => uint256) private shares;。這代表使用者有多少 stETH?顯然不是,不然每天都需要更新每個地址的數據,雖然這樣也可以做到只要調用合約中的方法來更新 shares 來實現一次交易就能更新所有地址的token,但是顯然這樣做GAS 的消耗同樣也是巨大的。


想必到這裡大家已經要猜到合約是怎麼實現的了,我們繼續來看 getPooledEthByShares 方法。



可以看到最終回傳的結果是用位址中 sharesAmount 乘以  _getTotalPooledEther() 再除 _getTotalShares。


_getTotalPooledEther 代表總共有多少 stETH(依照 stETH 兌 ETH 為 1:1 的話也代表有多少 ETH),_getTotalShares 代表有多少份額。這樣一算每個位址有多少 stETH 就是動態計算出來的了。


舉例說如果現在一共有1000 個份額(Shares,也就是 _getTotalShares 方法回傳的數量),其中A 位址有100 份(對應上面的 sharesAmount)。這 1000 個份額對應 1000 個 stETH(也就是 _getTotalPooledEther)回傳的數量。那麼按照這個計算,A 位址就對應有 100 個 stETH。那Lido 拿這總得1000 個ETH 去質押獲取到1 個ETH 的收益後對應更新 _getTotalPooledEther 為1001,也就是最初總共1000 個的stETH 變多變成1001 個了,那麼新的計算出來A 地址就擁有了100 * 1001 / 1000 = 100.1 個stETH。


簡單點說就是每個地址擁有的股份不變,股份對應的stETH 變多了,那麼一計算自然stETH 就變多了。


我們繼續看程式碼, _getTotalPooledEther 中的邏輯是會受到 handleOracleReport 方法影響,而這個方法則會更新合約中的相關數據。具體的呼叫是會透過 https://etherscan.io/address/0x852deD011285fe67063a08005c71a85690503Cee 合約定期呼叫 submitReportData Rep.alclid;



我們可以看到每天都會有呼叫更新相關內容,這就是為什麼雖然我們無法看到我們的地址中有發放收益的交易,但是金額依然每天在變化的原因。


這背後其實體現了以太坊ERC20 智能合約的一個特點,就是這些ERC20 的合約擁有多少代幣並不是寫死在地址上的,而是合約方法回傳的,所以可能會出現帳號雖然沒有任何交易,但是代幣的數量也可能會改變。這一方面讓 ERC20 合約更靈活,但是另一方面也給很多對合約不熟悉的朋友帶來了很多困惑,希望本文可以幫助大家更多的理解智能合約,更安全的和智能合約交互。


另外,雖然透過把ETH 質押為stETH 能夠獲得看似穩定的質押收益,但是依然有可能的風險存在,本文只作為對質押合約的技術研究參考,不構成任何投資建議。


原文連結


欢迎加入律动 BlockBeats 官方社群:

Telegram 订阅群:https://t.me/theblockbeats

Telegram 交流群:https://t.me/BlockBeats_App

Twitter 官方账号:https://twitter.com/BlockBeatsAsia

選擇文庫
新增文庫
取消
完成
新增文庫
僅自己可見
公開
保存
糾錯/舉報
提交