在DEFI資料分析應用中通常需要獲取大量區塊鏈資料進行顯示或分析,而這往往會多次呼叫合約, 從而導致總查詢時間過長。並且,如果我們使用的是Infura這樣的第三方提供程式,短時間內的多次 請求還有可能觸發服務商的限流管控。MakeDAO的Multicall就是解決這一問題的最佳方案,它包含 鏈上合約與鏈下NPM包兩個部分,可以將多個針對以太坊區塊鏈查詢請求合併為一個,從而有效縮短響應時間並降低eth_call呼叫的消耗。
為了幫助你瞭解這種機制的工作原理以及其相對於傳統方法是否確實有所改進(每個函式呼叫n次), 我們將舉一個示例,分別在不使用/使用Multicall的情況下進行操作,然後分析結果。為此,我們 將透過呼叫Compound協議的getAccountLiquidity()函式來查詢1,000個不同的地址的流動性資料。
用自己熟悉的語言學習 以太坊DApp開發 : Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、multicall對比測試:安裝NPM依賴包為了進行此測試,我們將建立一個Node專案,並將安裝如下依賴:
ethers.js:和以太坊區塊鏈進行互動money-legos:以更簡單的方式訪問DeFi協議ethers-multicall:multicall呼叫的封裝包首先使用以下命令建立專案:
npm init -y
然後安裝上述依賴包:
npm install -S @studydefi/money-legos ethers ethers-multicall
2、multicall對比測試:匯入NPM依賴包
無論是否使用multicall,我們都要使用一些公共的依賴包並例項化提供程式以便連線區塊鏈。 我們透過以下程式碼實現這一點:
const { ethers } = require("ethers");const { ALCHEMY_URL } = require('./config')const compound = require("@ studydefi/money-legos/compound");const { accounts } = require("./accounts");const { Contract, Provider } = require('ethers-multicall');const provider = new ethers.providers.JsonRpcProvider(ALCHEMY_URL);
接下來建立一個函式,以便顯示測試結果和執行時間,如下所示:
const calculateTime = async () => { const startDate = new Date(); const result = await getLiquidity() const endDate = new Date(); const milliseconds = (endDate.getTime() - startDate.getTime()); console.log(`Time to process in milliseconds: $ {milliseconds}`) console.log(`Time to process in seconds: $ {milliseconds / 1000}`) const callsCount = Object.keys(result).length; console.log(`Number of entries in the result: $ {callsCount}`);}
3、multicall對比測試:傳統方式迴圈呼叫合約
為了使用傳統方法進行測試,我們將遍歷包含1000個地址的陣列,然後在一個map迴圈中,逐個返回每個地址的查詢結果。為此,可以實現如下函式程式碼:
const getLiquidity = () => { const compoundContract = new ethers.Contract( compound.comptroller.address, compound.comptroller.abi, provider ) return Promise.all(accounts.map(account => { let data try { data = compoundContract.getAccountLiquidity(account.id) } catch (error) { console.log(`Error getting the data $ {error}`) } return data }))}
在上面的程式碼中,我們例項化compound協議的comptroller合約,然後為每個地址呼叫 賬戶流動性查詢函式。
4、multicall對比測試:使用multicall呼叫的最佳化實現使用Multicall呼叫時,上述程式碼需要稍作更改:
const getLiquidity = async () => { const ethcallProvider = new Provider(provider); await ethcallProvider.init(); const compoundContract = new Contract( compound.comptroller.address, compound.comptroller.abi, ) const contractCalls = accounts.map(account => compoundContract.getAccountLiquidity(account.id)) const results = await ethcallProvider.all(contractCalls); return results}
在上面的程式碼中,我們利用了Multicall包中的Provider和Contract類。
首先,利用compound協議的comptroller合約的地址和abi初始化提供程式。 然後將地址陣列對映為合約呼叫陣列,並在最後將得到的合約呼叫陣列傳入 提供器的all函式,這時請求才真正傳送到網路中。
5、結果分析讓我們看一下執行時間的差異。
普通方法(不使用multicall):
處理時間:124.653 秒結果條目:1000改進方法(使用mutlicall):
處理時間:9.591 秒結果條目:10006、結論正如我們所看到的,使用multicall時執行時間的減少是相當可觀的,從124秒減少到9.5秒, 花費的時間減少到原來的大約十分之一。
此外,如果我們比較eth_call請求數量,也會看到非常明顯的減少,從上千個減少到 只有一個。因此,如果我們依賴以太坊的訪問提供商,而在該提供商中對API的呼叫 受到限制,則需要考慮這一點。
原文連結:http://blog.hubwiz.com/2020/12/26/multicall-intro/