作者 |Yos Riady
譯者|火火醬,責編 | 李雪敬
頭圖 |CSDN 下載自視覺中國
“智慧合約”這個名字其實並不確切。儘管名字中有“智慧”二字,但 Ethereum 上的智慧合約並不能全自動執行。智慧合約程式碼的執行需要藉助外力的觸發。換句話說,我們需要透過一些外部流程來觸發智慧合約。
在本文中,我們將透過構建可行的解決方案來解決該問題,瞭解一下:
為什麼需要鏈下智慧合約自動化
智慧合約自動化的用例
如何藉助無伺服器架構來部署無伺服器功能
最後,我們還將介紹 serverless-ethers——全功能的智慧合約自動化服務,部署後可直接使用。我們可以以此服務為基礎,構建符合自己需求的自定義智慧合約自動化專案。
問題是:名叫智慧合約,卻無法自動執行
假設我們想要實現一個能夠每小時自動執行一次的智慧合約。要怎麼做呢?
現實就是:根本做不到。僅靠 Solidity 智慧合約是做不到這一點的。儘管名叫“智慧合約”,但 Ethereum 中的智慧合約並不能自動執行,我們需要藉助外部源(人或機器)來呼叫智慧合約並執行其程式碼。
合約最多能做到的是:在不同任務間插入一小時間隔,例如:
functionrunMe()public{require(block.timestamp>=lastTriggeredAt+1hour);...}
以上“require()”語句可確保兩次執行至少間隔一小時。但是,仍需要在開始時人為觸發智慧合約,然後程式碼才會正常執行。
來談一談“自動e執行”
從技術層面上來看,有一些操作是可以使用函式修改器來自動執行的。比如說Compound Governance的COMP分配。一旦地址獲得了0.001COMP,之後所有的Compound交易(例如提供資產,或轉移cToken)都會自動將COMP轉到其錢包中。
我們可以在函式修改器中實現上述邏輯,將修改器放在函式前,並在呼叫函式時自動執行邏輯。由呼叫方來支付相關的附加費用。
然而,並不是所有的智慧合約系統都可以採用這種方法。由於這些修改器只能在特定條件下執行,因此可能會導致意料之外的gas費用。同時,還可能會向使用者隨機收取額外的gas費用,以實現合約“平衡性”。
並且,程式碼的執行仍然需要透過人為呼叫智慧合約才能實現。
智慧合約自動化的常見用例
DeFi協議依賴於某種鏈下智慧合約自動化。MakerDAO依賴第三方來監控債務頭寸的抵押擔保比率,並清算擔保不足的頭寸。其他的DeFi協議也都有類似的需求。
在鏈下智慧合約自動化方面,有兩個常見用例:
自動觸發器(Automated Triggers):在特定情況下執行合約。
狀態和事件監控(State and Event Monitoring):瞭解合約在何時出現特定狀態。
1. 自動觸發器
我們經常需要定期、或在特定條件下執行合約,例如:
週期性地恢復平衡池
結束DAO/治理過程中的投票
按比例支付安全代幣股息
2. 狀態和事件監控
有時我們需要了解合約是否滿足了某些條件,例如:
瞭解智慧合約的價值是否發生了變化
獲取所有準入限制更改的通知
瞭解何時發出特定的智慧合約事件
解決方案:無伺服器函式?
實際上,無伺服器功能剛好適用於上面提到的這幾個用例。有了無伺服器化,我們便無需在部署程式碼之前預配任何東西,並且之後也不需要費心管理,極大地簡化了問題的解決方案。
快速入門:藉助Serverless Framework來實現無伺服器化
無伺服器架構(Serverless Framework)為我們提供了開發、部署、監控和保護無伺服器應用程式所需的一切內容。讓我們一起來看看如何能夠以最簡單的方式完成開發吧。
>npminstall-gserverless>serverless-vx.x.x
首先,我們來快速瞭解一下Serverless Framework的運作方式。
0. serverless.yml
所有Serverless服務中的Lambda函式和事件都可以在名為serverless.yml的配置檔案中找到。該檔案對服務(包含Functions和Events)進行了定義。
service:serverless-ethersprovider:name:awsruntime:nodejs12.xenvironment:CHAIN_ID:3DEFAULT_GAS_PRICE:60000000000functions:myFunc:handler:functions/myFunc.handlerevents:-schedule:rate(2hours)
我們可以在function屬性下,對無伺服器函式進行定義。在上面的例子中:
我們有名為myFunc的Function
handler屬性指向包含你想在函式中執行的程式碼的檔案和模組
events屬性為要執行的函式指定Event觸發器
一個服務中可以包含多個函式。
1. Functions
Function是AWS Lambda函式,是一個類似於微服務的獨立部署單元。作為一段部署在雲中的程式碼,通常被用於執行單個作業。
//functions/myFunc.jsexports.handler=asyncfunction(event,context){//Doanything};
Functions只是普通的JS函式,可以將事件物件作為有效負載。
2. Events
Events是觸發函式執行的事件,隸屬於每個Function,可以在serverless.yml中的事件屬性中找到。
我們可以使用Scheduled Events觸發器來定期自動執行函式。例如,我們指定每2小時執行一次myFunc函式:
#serverless.ymlfunctions:myFunc:handler:functions/myFunc.handlerevents:-schedule:rate(2hours)
我們還可以藉助cron schedule expression來指定安排計劃
#serverless.ymlevents:-schedule:cron(012**?*)#12PMUTC
如果你使用的是AWS的話,事件即為AWS中可以出發AWS Lambda函式的任意事件,比如:
AWS API Gateway HTTP端點請求(例如,REST API)
AWS S3儲存桶上傳(例如,影象)
CloudWatch計時器(例如,每5分鐘執行一次)
AWS SNS主題(例如,資訊)
等等……
就目前而言,知道這些就夠了。如果還想了解更多關於Serverless framework的內容的話,可以看一下這個檔案(https://www.serverless.com/framework/docs/?ref=hackernoon.com)
在瞭解了Serverless Framework的基礎知識後,我們來看一看serverless-ethers服務吧。
serverless-ethers是什麼
serverless-ethers是一個全功能Serverless服務,部署後即可直接使用。
[email protected]:yosriady/serverless-ethers.gitcdserverless-ethersnvmusenpminstall
我們可以將此專案作為構建自定義智慧合約自動化的基礎。其預先配置的是AWS,但修改後也適用於其他雲提供商(如GCP、Azure等)。
serverless-ethers專案的結構如下:
├──contracts/│├──abis/│├──abis.js│└──addresses.js├──functions/│└──exec.js└──serverless.yml
contracts/包含智慧合約ABI和地址。
functions/包含實現業務邏輯的JS函式。
serverless.yml描述服務配置。
接下來,我們將深入瞭解一下各個部分。
合約樣本示例
為了進行測試,我編寫並部署了一個智慧合約示例:
//SPDX-License-Identifier:GPL-3.0pragmasolidity^0.6.10;contractDummyStorage{eventWrite(addressindexedsource,uint256value);uintinternal_currentValue;functionget()publicviewreturns(uint){return_currentValue;}functionput(uintvalue)public{emitWrite(msg.sender,value);_currentValue=value;
該DummyStorage智慧合約具有以下功能:
get是一個能反饋合約當前值的只讀函式。
put是一個用於更新合約當前值的寫入函式。
該示例合約已經過驗證並在Ropsten上執行。大家可以用它來測試自己的函式!
1. 智慧合約ABIs
合約目錄中包含與函式互動的合約ABIs。在示例專案中,它包含DummyStorage合約的ABI。
├──contracts/│├──abis/││└──DummyStorage.json│├──abis.js│└──addresses.js
我們可以將ABI看作是智慧合約的公共API規範,類似於OpenAPI規範。我們需要用ABI來呼叫合約函式。
合約/目錄結構能協助我們匯入合約ABI和地址:
//functions/exec.jsconst{abis,addresses}=require('../contracts');constDummyStorageABI=abis.DummyStorage;constDummyStorageAddress=addresses.DummyStorage
這都是我們在接下來的函式部分中將要用到的。
2. Functions
exec函式利用Ethers來載入合約ABI並呼叫智慧合約:
//Initializecontractconstcontract=newethers.Contract(DummyStorageAddress,DummyStorageABI,wallet,)//Callsmartcontractfunction`put(uint)`constRANDOM_INTEGER=Math.floor(Math.random()*100);//returnsarandomintegerfrom0to99consttx=awaitcontract.put(RANDOM_INTEGER)
載入合約ABI和地址後,我們將得到一個具備智慧合約所有函式的ethers.Contract抽象,包括get()和put().
在示例exec函式中,我們用一個隨機整數來呼叫contract.put()。
3. serverless.yml
在執行exec函式之前,我們需要在serverless.yml中指定幾個環境變數:
#serverless.ymlservice:serverless-ethersprovider:name:awsruntime:nodejs12.xregion:ap-southeast-1timeout:30environment:DEFAULT_GAS_PRICE:60000000000MNEMONIC:...SLACK_HOOK_URL:...
serverless-ethers使用了以下環境變數:
DEFAULT_GAS_PRICE:事務寫入時使用的預設gas價格。
MNEMONIC:用於匯出Ethereum地址的12個助記詞。如果打算將資料寫入Ethereum的話,要確保確保其由Ether進行支付。
SLACK_HOOK_URL:示例中使用Incoming Webhooks向Slack傳送訊息。你可以從自己的Slack儀表板上獲取此URL。(可選項)
你可以從AWS Lambda控制檯更改已部署函式的環境變數。
注意:切記不要在構建過程中用明文儲存金鑰。在儲存助記詞和API金鑰等憑證時,要使用安全的引數儲存,如AWS Secrets Manager。因為每個專案的安全需求和設定不同,所以請根據自身實際情況來決定密碼儲存方式。
本地執行
我們可以使用無伺服器CLI命令在本地執行函式。
>serverlessinvokelocal-fexecStarting...ContractABIsloadedEtherswalletloadedContractloadedSendingtransaction...:white_check_mark:Transactionsenthttps://ropsten.etherscan.io/tx/0x72204f07911a319b4e5f7eb54ad15ed666cfc1403b53def40c9d60188b176383CompletedTrue
部署到AWS
執行serverless deploy即可輕鬆實現部署:
>serverlessdeployServerless:Packagingservice...Serverless:Excludingdevelopmentdependencies...Serverless:CreatingStack...Serverless:CheckingStackcreateprogress...........Serverless:Stackcreatefinished...Serverless:UploadingCloudFormationfiletoS3...Serverless:Uploadingartifacts...Serverless:Uploadingserviceserverless-ethers.zipfiletoS3(2.95MB)...Serverless:Validatingtemplate...Serverless:UpdatingStack...Serverless:CheckingStackupdateprogress........................Serverless:Stackupdatefinished...ServiceInformationservice:serverless-ethersstage:devregion:ap-southeast-1stack:serverless-ethers-devresources:8apikeys:Noneendpoints:Nonefunctions:exec:serverless-ethers-dev-execlayers:None
現在,一個無伺服器函式就完成了,我們可以用它來對智慧合約進行自動化和監控,並且可以在這個示例專案的基礎上構建屬於自己的智慧合約自動化。
寫在最後
祝賀你學會了以下內容:
為什麼需要鏈下智慧合約自動化
智慧合約自動化的用例
Serverless架構
serverless-ethers示例應用程式的執行原理
補充:用Slack 實現ChatOps
除了serverless-ethers,我們還可以透過postToSlack函式來整合Slack。
constsuccessMessage=`:white_check_mark:Transactionsenthttps://ropsten.etherscan.io/tx/${tx.hash}`;awaitpostToSlack(successMessage);
postToSlack函式利用了你從Slack console獲取的SLACK_HOOK_URL環境變數。設定完成後,只要交易成功傳送,就會馬上通知Slack,輕輕鬆鬆監控函式。
補充:監控智慧合約事件
截至目前,我們只介紹了“自動觸發”用例,那要怎樣監控智慧合約狀態和事件呢?
我們可以使用Ethers v5 Events API來定期監控特定事件。可以在函式中執行以下操作:
//GiventhefollowingEvent://eventTransfer(bytes32indexednode,addressowner)//Getthefilter(thesecondnullcouldbeomitted)constfilter=contract.filters.Transfer(userAccount,null);//Querythefilterconstlogs=contract.queryFilter(filter,0,"latest");//fromblock0tolatestblock//Printoutallthevalues:logs.forEach((log)=>{console.log(log.args._to,log.args._value);}
假設我們希望讓這個函式週期性執行(例如每5分鐘一次),還需要儲存一個標記,對該函式自上次執行後所看到的最後一個塊進行跟蹤。
該智慧合約在監控Access Control白名單時非常有用。有了事件監控功能,可以在白名單中新增新地址時通知Slack。你用過嗎?