# 合約開發的實用技巧:從Uniswap代碼中學到的經驗最近在開發一個去中心化交易所的教程時,參考了Uniswap V3的代碼實現,學到了很多有趣的知識點。作爲首次嘗試開發Defi合約的開發者,這些技巧對想要學習合約開發的新手會很有幫助。接下來讓我們看看這些實用的小技巧,有些甚至可以稱得上是奇技淫巧。## 預測合約部署地址通常情況下,部署合約得到的地址看起來是隨機的,因爲與nonce有關,不易預測。但在某些場景下,我們需要通過交易對和相關信息推斷出合約地址。這在判斷交易權限或獲取池子地址等情況下很有用。Uniswap採用了CREATE2的方式創建合約,通過添加salt參數使得生成的合約地址可預測。新地址的生成邏輯爲:hash("0xFF",創建者地址, salt, initcode)。這種方法讓合約地址變得可預測,便於後續操作。## 巧用回調函數Solidity中合約之間可以互相調用。一種常見場景是A方法調用B,B在被調用的方法中回調A。這在某些情況下非常實用。以Uniswap爲例,當調用UniswapV3Pool合約的swap方法進行交易時,它會回調swapCallback,傳入計算出的本次交易實際需要的Token。調用方需在回調中將交易所需Token轉入UniswapV3Pool。這種設計確保了swap方法的完整執行和安全性,無需繁瑣的變量記錄。## 利用異常傳遞信息,用try catch實現交易預估Uniswap的Quoter合約中,使用try catch包裹執行UniswapV3Pool的swap方法。這是爲了模擬swap方法來預估交易所需Token。由於預估時不會實際產生Token交換,所以會報錯。Uniswap通過在交易回調函數中拋出特殊錯誤,然後捕獲該錯誤並從中解析所需信息。這種方法看似取巧,但很實用。它避免了爲預估交易需求而改造swap方法,使邏輯更加簡潔。## 用大數解決精度問題Uniswap代碼中涉及大量計算邏輯,如根據當前價格和流動性計算交換的Token。爲避免除法操作導致精度丟失,Uniswap經常使用"<< FixedPoint96.RESOLUTION"操作,即左移96位,相當於乘以2^96。先左移再進行除法運算,可在正常交易不溢出的情況下(通常用uint256計算)保證精度。雖然理論上仍會有微小的精度丟失,但這種程度的誤差通常可以接受。## 用Share方式計算收益Uniswap需要記錄LP(流動性提供者)的手續費收益。爲避免每次交易都給每個LP記錄手續費而消耗大量Gas,Uniswap採用了一種巧妙的方法。在Position結構體中定義了feeGrowthInside0LastX128和feeGrowthInside1LastX128,記錄每個頭寸上次提取手續費時每個流動性應得的手續費。這樣只需記錄總手續費和每個流動性應分配的手續費,LP提取時根據持有的流動性即可計算可提取的手續費。這類似於股票分紅機制,提取時只需知道公司歷史每股收益和上次提取時的收益即可。## 合理選擇信息獲取途徑鏈上存儲相對昂貴,因此並非所有信息都需要上鏈或從鏈上獲取。例如,Uniswap前端網站調用的許多接口是傳統Web2接口。交易池列表、交易池信息等可存儲在普通數據庫中,部分數據可能需要定期從鏈上同步,但無需實時調用鏈或節點服務的RPC接口獲取相關數據。一些區塊鏈RPC供應商提供了高級接口,可以更快速、更經濟地獲取某些數據。這些接口通常利用緩存來提高性能和效率。當然,關鍵交易仍需在鏈上進行。## 學習合約拆分和利用現有標準合約一個項目可能包含多個實際部署的合約。即使實際部署只有一個合約,我們也可以通過繼承的方式將合約拆分爲多個部分來維護。例如,Uniswap的NonfungiblePositionManager合約就繼承了多個合約。在實現ERC721Permit合約時,直接使用了@openzeppelin/contracts/token/ERC721/ERC721.sol合約。這不僅方便通過NFT方式管理頭寸,還能利用現有標準合約提高開發效率。## 總結親身實踐開發一個簡易版去中心化交易所,能讓你更深入理解Uniswap的代碼實現,也能學習到更多實際項目中的知識點。相信這些技巧對想要學習合約開發的新手會很有幫助。
Uniswap代碼解析:7個實用合約開發技巧
合約開發的實用技巧:從Uniswap代碼中學到的經驗
最近在開發一個去中心化交易所的教程時,參考了Uniswap V3的代碼實現,學到了很多有趣的知識點。作爲首次嘗試開發Defi合約的開發者,這些技巧對想要學習合約開發的新手會很有幫助。
接下來讓我們看看這些實用的小技巧,有些甚至可以稱得上是奇技淫巧。
預測合約部署地址
通常情況下,部署合約得到的地址看起來是隨機的,因爲與nonce有關,不易預測。但在某些場景下,我們需要通過交易對和相關信息推斷出合約地址。這在判斷交易權限或獲取池子地址等情況下很有用。
Uniswap採用了CREATE2的方式創建合約,通過添加salt參數使得生成的合約地址可預測。新地址的生成邏輯爲:hash("0xFF",創建者地址, salt, initcode)。這種方法讓合約地址變得可預測,便於後續操作。
巧用回調函數
Solidity中合約之間可以互相調用。一種常見場景是A方法調用B,B在被調用的方法中回調A。這在某些情況下非常實用。
以Uniswap爲例,當調用UniswapV3Pool合約的swap方法進行交易時,它會回調swapCallback,傳入計算出的本次交易實際需要的Token。調用方需在回調中將交易所需Token轉入UniswapV3Pool。這種設計確保了swap方法的完整執行和安全性,無需繁瑣的變量記錄。
利用異常傳遞信息,用try catch實現交易預估
Uniswap的Quoter合約中,使用try catch包裹執行UniswapV3Pool的swap方法。這是爲了模擬swap方法來預估交易所需Token。由於預估時不會實際產生Token交換,所以會報錯。Uniswap通過在交易回調函數中拋出特殊錯誤,然後捕獲該錯誤並從中解析所需信息。
這種方法看似取巧,但很實用。它避免了爲預估交易需求而改造swap方法,使邏輯更加簡潔。
用大數解決精度問題
Uniswap代碼中涉及大量計算邏輯,如根據當前價格和流動性計算交換的Token。爲避免除法操作導致精度丟失,Uniswap經常使用"<< FixedPoint96.RESOLUTION"操作,即左移96位,相當於乘以2^96。先左移再進行除法運算,可在正常交易不溢出的情況下(通常用uint256計算)保證精度。
雖然理論上仍會有微小的精度丟失,但這種程度的誤差通常可以接受。
用Share方式計算收益
Uniswap需要記錄LP(流動性提供者)的手續費收益。爲避免每次交易都給每個LP記錄手續費而消耗大量Gas,Uniswap採用了一種巧妙的方法。
在Position結構體中定義了feeGrowthInside0LastX128和feeGrowthInside1LastX128,記錄每個頭寸上次提取手續費時每個流動性應得的手續費。這樣只需記錄總手續費和每個流動性應分配的手續費,LP提取時根據持有的流動性即可計算可提取的手續費。這類似於股票分紅機制,提取時只需知道公司歷史每股收益和上次提取時的收益即可。
合理選擇信息獲取途徑
鏈上存儲相對昂貴,因此並非所有信息都需要上鏈或從鏈上獲取。例如,Uniswap前端網站調用的許多接口是傳統Web2接口。
交易池列表、交易池信息等可存儲在普通數據庫中,部分數據可能需要定期從鏈上同步,但無需實時調用鏈或節點服務的RPC接口獲取相關數據。
一些區塊鏈RPC供應商提供了高級接口,可以更快速、更經濟地獲取某些數據。這些接口通常利用緩存來提高性能和效率。
當然,關鍵交易仍需在鏈上進行。
學習合約拆分和利用現有標準合約
一個項目可能包含多個實際部署的合約。即使實際部署只有一個合約,我們也可以通過繼承的方式將合約拆分爲多個部分來維護。
例如,Uniswap的NonfungiblePositionManager合約就繼承了多個合約。在實現ERC721Permit合約時,直接使用了@openzeppelin/contracts/token/ERC721/ERC721.sol合約。這不僅方便通過NFT方式管理頭寸,還能利用現有標準合約提高開發效率。
總結
親身實踐開發一個簡易版去中心化交易所,能讓你更深入理解Uniswap的代碼實現,也能學習到更多實際項目中的知識點。相信這些技巧對想要學習合約開發的新手會很有幫助。