
“智能合约”这个名字其实并不确切。尽管名字中有“智能”二字,但 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服务,部署后即可直接使用。
gitclonegit@github.com:yosriady/serverless-ethers.gitcdserverless-ethersnvmusenpminstall我们可以将此项目作为构建自定义智能合约自动化的基础。其预先配置的是AWS,但修改后也适用于其他云提供商(如GCP、Azure等)。
serverless-ethers项目的结构如下:
├──contracts/│├──abis/│├──abis.js│└──addresses.js├──functions/│└──exec.js└──serverless.ymlcontracts/包含智能合约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/$`;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。
eToro宣布推出Solana和Ethereum质押服务:用户现可赚取加密货币奖励
俄罗斯银行建议对NFT、智能合约进行监管
Horizen Labs公布ApeCoin DAO质押系统界面
Avalanche基金会为交易平台GMX推出400万美元奖励计划
外媒:Do Kwon目前可能住在欧洲
Solana上的Lido将于11月中旬升级至V2版本
1inch已支持直接从Rocket Pool智能合约铸造或销毁rETH
新加坡正试点带智能合约功能的数字新加坡元
跨链基础设施Celer Network推出全链流动性协议Peti
香港证监会:支持元宇宙、NFT、GameFi创新,积极探索适当的代币化资产监管框架
香港金管局总裁:对代币化资产产权和智能合约合法性的审查持开放态度
Fantom部署智能合约监控系统Watchdog
《自然医学》杂志论文披露以太坊区块链正被用于全球抗击癌症
币安推出原生预言机服务,首先支持BNB Chain
挪威政府将在Decentraland设立元宇宙税务局和办事处
三亚:以数字人民币智能合约消费券模式发放2000万元汽车消费券
桑坦德银行启动用于二手车所有权转让的区块链平台测试
GnosisDAO:向GNO持有者分发约900万枚SAFE提案已获通过
Dogechain (DC)发起Token销毁提案,过去7天涨幅超250%
Animoca Brands联创:The Sandbox月活超20万,熊市对链游影响较小



