智能合约并不智能?本文将通过构建可行的解决方案来解决该问题。 = lastTriggeredAt + 1 hour);"],[20,"\n","24:\"Y7Ln\"|36:177"],[20," ..."],[20,"\n","24:\"Eqxc\"|36:177"],[20,"}"],[20,"\n","24:\"pKDU\"|36:177"],[20,"\t以上“require()”语句可确保两次执行至少间隔一小时。但是,仍需要在开始时人为触发智能合约,然后代码才会正常运行。"],[20,"\n","24:\"2Fgq\"|7:3"],[20,"来谈一谈“自动e执行”","0:\"%23ed7d31\""],[20,"\n","24:\"0Qrh\"|7:3"],[20,"\t从技术层面上来看,有一些操作是可以使用函数修改器来自动执行的。比如说Compound Governance的COMP分配。一旦地址获得了0.001COMP,之后所有的Compound交易(例如提供资产,或转移cToken)都会自动将COMP转到其钱包中。"],[20,"\n","24:\"gcwW\"|7:3"],[20,"\t我们可以在函数修改器中实现上述逻辑,将修改器放在函数前,并在调用函数时自动执行逻辑。由调用方来支付相关的附加费用。"],[20,"\n","24:\"SdCR\"|7:3"],[20,"\t然而,并不是所有的智能合约系统都可以采用这种方法。由于这些修改器只能在特定条件下运行,因此可能会导致意料之外的gas费用。同时,还可能会向用户随机收取额外的gas费用,以实现合约“平衡性”。"],[20,"\n","24:\"UE1L\"|7:3"],[20,"\t并且,代码的运行仍然需要通过人为调用智能合约才能实现。"],[20,"\n","24:\"84Wo\"|7:3"],[20,"智能合约自动化的常见用例","0:\"%23ed7d31\""],[20,"\n","24:\"ylI9\"|7:3"],[20,"\tDeFi协议依赖于某种链下智能合约自动化。MakerDAO依赖第三方来监控债务头寸的抵押担保比率,并清算担保不足的头寸。其他的DeFi协议也都有类似的需求。"],[20,"\n","24:\"kzHE\"|7:3"],[20,"\t在链下智能合约自动化方面,有两个常见用例:"],[20,"\n","24:\"4E62\"|7:3"],[20,"自动触发器(Automated Triggers):在特定情况下执行合约。","27:\"12\"|8:1"],[20,"\n","24:\"BRHr\"|27:\"12\"|bullet-id:\"IFIv\"|bullet:\"circle\""],[20,"状态和事件监控(State and Event Monitoring):了解合约在何时出现特定状态。","27:\"12\"|8:1"],[20,"\n","24:\"WLyc\"|27:\"12\"|bullet-id:\"IFIv\"|bullet:\"circle\""],[20,"1. 自动触发器","27:\"12\""],[20,"\n","24:\"voxY\""],[20,"\t我们经常需要定期、或在特定条件下执行合约,例如:"],[20,"\n","24:\"9BtC\"|7:3"],[20,"周期性地恢复平衡池","27:\"12\""],[20,"\n","24:\"rocz\"|27:\"12\"|bullet-id:\"i378\"|bullet:\"circle\""],[20,"结束DAO/治理过程中的投票","27:\"12\""],[20,"\n","24:\"DCLG\"|27:\"12\"|bullet-id:\"i378\"|bullet:\"circle\""],[20,"按比例支付安全代币股息","27:\"12\""],[20,"\n","24:\"q1Ie\"|27:\"12\"|bullet-id:\"i378\"|bullet:\"circle\""],[20,"2. 状态和事件监控"],[20,"\n","24:\"QSw5\"|7:3"],[20,"\t有时我们需要了解合约是否满足了某些条件,例如:"],[20,"\n","24:\"lBSi\"|7:3"],[20,"了解智能合约的价值是否发生了变化","27:\"12\""],[20,"\n","24:\"4D7H\"|27:\"12\"|bullet-id:\"6BqR\"|bullet:\"circle\""],[20,"获取所有准入限制更改的通知","27:\"12\""],[20,"\n","24:\"iyGl\"|27:\"12\"|bullet-id:\"6BqR\"|bullet:\"circle\""],[20,"了解何时发出特定的智能合约事件","27:\"12\""],[20,"\n","24:\"HPdh\"|27:\"12\"|bullet-id:\"6BqR\"|bullet:\"circle\""],[20,"解决方案:无服务器函数?","0:\"%23ed7d31\""],[20,"\n","24:\"ymMx\"|7:3"],[20,"\t实际上,无服务器功能刚好适用于上面提到的这几个用例。有了无服务器化,我们便无需在部署代码之前预配任何东西,并且之后也不需要费心管理,极大地简化了问题的解决方案。"],[20,"\n","24:\"dmzk\"|7:3"],[20,"快速入门:借助Serverless Framework来实现无服务器化","0:\"%23ed7d31\""],[20,"\n","24:\"CkoA\"|7:3"],[20,"\t无服务器架构(Serverless Framework)为我们提供了开发、部署、监控和保护无服务器应用程序所需的一切内容。让我们一起来看看如何能够以最简单的方式完成开发吧。"],[20,"\n","24:\"wFzK\"|7:3"],[20,"> npm install -g serverless"],[20,"\n","24:\"5pRP\"|36:177"],[20,"> serverless -v"],[20,"\n","24:\"xNab\"|36:177"],[20,"x.x.x"],[20,"\n","24:\"tuIh\"|36:177"],[20,"\t首先,我们来快速了解一下Serverless Framework的运作方式。"],[20,"\n","24:\"4lZj\"|7:3"],[20,"0. serverless.yml","8:1"],[20,"\n","24:\"Fszg\"|7:3"],[20,"\t所有Serverless服务中的Lambda函数和事件都可以在名为serverless.yml的配置文件中找到。该文件对服务(包含Functions和Events)进行了定义。"],[20,"\n","24:\"yVl2\"|7:3"],[20,"service: serverless-ethers"],[20,"\n","24:\"h3IV\"|36:177"],[20,"provider:"],[20,"\n","24:\"vX6G\"|36:177"],[20," name: aws"],[20,"\n","24:\"BYPY\"|36:177"],[20," runtime: nodejs12.x"],[20,"\n","24:\"KQv7\"|36:177"],[20," environment:"],[20,"\n","24:\"HTbp\"|36:177"],[20," CHAIN_ID: 3"],[20,"\n","24:\"tTpR\"|36:177"],[20," DEFAULT_GAS_PRICE: 60000000000"],[20,"\n","24:\"Tti9\"|36:177"],[20,"\n","24:\"P7rJ\"|36:177"],[20,"functions:"],[20,"\n","24:\"70LP\"|36:177"],[20," myFunc:"],[20,"\n","24:\"ZhNL\"|36:177"],[20," handler: functions/myFunc.handler"],[20,"\n","24:\"x4KH\"|36:177"],[20," events:"],[20,"\n","24:\"G2yQ\"|36:177"],[20," - schedule: rate(2 hours)"],[20,"\n","24:\"SMlj\"|36:177"],[20,"\t我们可以在"],[20,"function","1:\"%23d9d9d9\""],[20,"属性下,对无服务器函数进行定义。在上面的例子中:"],[20,"\n","24:\"VexS\"|7:3"],[20,"我们有名为","27:\"12\""],[20,"myFunc","1:\"%23d9d9d9\"|27:\"12\""],[20,"的Function","27:\"12\""],[20,"\n","24:\"YhJ3\"|27:\"12\"|bullet-id:\"zkiM\"|bullet:\"circle\""],[20,"handler","1:\"%23d9d9d9\"|27:\"12\""],[20,"属性指向包含你想在函数中运行的代码的文件和模块","27:\"12\""],[20,"\n","24:\"fA9U\"|27:\"12\"|bullet-id:\"zkiM\"|bullet:\"circle\""],[20,"events","1:\"%23d9d9d9\"|27:\"12\""],[20,"属性为要执行的函数指定Event触发器","27:\"12\""],[20,"\n","24:\"JzbL\"|27:\"12\"|bullet-id:\"zkiM\"|bullet:\"circle\""],[20,"\t一个服务中可以包含多个函数。"],[20,"\n","24:\"ZJuS\"|7:3"],[20,"1.","8:1"],[20," ","27:\"9\"|31:12|8:1"],[20,"Functions","8:1"],[20," "],[20,"\n","24:\"jXu3\"|7:3"],[20,"\tFunction是AWS Lambda函数,是一个类似于微服务的独立部署单元。作为一段部署在云中的代码,通常被用于执行单个作业。"],[20,"\n","24:\"Uiga\"|7:3"],[20,"// functions/myFunc.js"],[20,"\n","24:\"6gxf\"|36:177"],[20,"exports.handler = async function(event, context) {"],[20,"\n","24:\"FHen\"|36:177"],[20," // Do anything"],[20,"\n","24:\"RfVh\"|36:177"],[20,"};"],[20,"\n","24:\"Kd4L\"|36:177"],[20,"\tFunctions只是普通的JS函数,可以将事件对象作为有效负载。"],[20,"\n","24:\"yBt8\"|7:3"],[20,"2. Events","8:1"],[20,"\n","24:\"wEKC\""],[20,"\tEvents是触发函数运行的事件,隶属于每个Function,可以在"],[20,"serverless.yml","1:\"%23d9d9d9\""],[20,"中的事件属性中找到。"],[20,"\n","24:\"zwtB\"|7:3"],[20,"\t我们可以使用Scheduled Events触发器来定期自动执行函数。例如,我们指定每2小时运行一次myFunc函数:"],[20,"\n","24:\"NIt6\"|7:3"],[20,"# serverless.yml"],[20,"\n","24:\"yeei\"|36:177"],[20,"\n","24:\"J49m\"|36:177"],[20,"functions:"],[20,"\n","24:\"1H9B\"|36:177"],[20," myFunc:"],[20,"\n","24:\"UOXm\"|36:177"],[20," handler: functions/myFunc.handler"],[20,"\n","24:\"CH2K\"|36:177"],[20," events:"],[20,"\n","24:\"6WuV\"|36:177"],[20," - schedule: rate(2 hours)"],[20,"\n","24:\"PvRI\"|36:177"],[20,"\t我们还可以借助cron schedule expression来指定安排计划"],[20,"\n","24:\"cAJX\"|7:3"],[20,"# serverless.yml"],[20,"\n","24:\"Pxmm\"|36:177"],[20,"\n","24:\"XyAV\"|36:177"],[20,"events:"],[20,"\n","24:\"S7Td\"|36:177"],[20," - schedule: cron(0 12 * * ? *) # 12PM UTC"],[20,"\n","24:\"HbwP\"|36:177"],[20,"\t"],[20,"如果你使用的是AWS的话,事件即为AWS中可以出发AWS Lambda函数的任意事件,比如:","0:\"%233c3c3b\""],[20,"\n","24:\"KG8P\"|7:3"],[20,"AWS API Gateway HTTP端点请求(例如,REST API)","27:\"12\""],[20,"\n","24:\"ho8L\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"AWS S3存储桶上传(例如,图像)","27:\"12\""],[20,"\n","24:\"Rsbw\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"CloudWatch计时器(例如,每5分钟运行一次)","27:\"12\""],[20,"\n","24:\"QgLs\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"AWS SNS主题(例如,信息)","27:\"12\""],[20,"\n","24:\"vKv9\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"等等……","27:\"12\""],[20,"\n","24:\"KUSR\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"\t就目前而言,知道这些就够了。如果还想了解更多关于Serverless framework的内容的话,可以看一下这个文件("],[20,"https://www.serverless.com/framework/docs/?ref=hackernoon.com","16:\"https%3A%2F%2Fwww.serverless.com%2Fframework%2Fdocs%2F%3Fref%3Dhackernoon.com\""],[20,")"],[20,"\n","24:\"IWS8\"|7:3"],[20,"\t在了解了Serverless Framework的基础知识后,我们来看一看"],[20,"serverless-ethers","1:\"%23d9d9d9\""],[20,"服务吧。"],[20,"\n","24:\"dVqI\"|7:3"],[20,"serverless-ethers是什么","0:\"%23ed7d31\""],[20,"\n","24:\"nKbm\"|7:3"],[20,"\t"],[20,"serverless-ethers","1:\"%23d9d9d9\""],[20,"是一个全功能Serverless服务,部署后即可直接使用。"],[20,"\n","24:\"AFON\"|7:3"],[20,"git clone [email protected]:yosriady/serverless-ethers.git"],[20,"\n","24:\"z9Oa\"|36:177"],[20,"cd serverless-ethers"],[20,"\n","24:\"azhd\"|36:177"],[20,"nvm use"],[20,"\n","24:\"Ielg\"|36:177"],[20,"npm install"],[20,"\n","24:\"R4Wt\"|36:177"],[20,"\t我们可以将此项目作为构建自定义智能合约自动化的基础。其预先配置的是AWS,但修改后也适用于其他云提供商(如GCP、Azure等)。"],[20,"\n","24:\"35qT\"|7:3"],[20,"\t"],[20,"serverless-ethers","1:\"%23d9d9d9\""],[20,"项目的结构如下:"],[20,"\n","24:\"OgTX\"|7:3"],[20,"├── contracts/"],[20,"\n","24:\"OITE\"|36:177"],[20,"│ ├── abis/"],[20,"\n","24:\"XGcf\"|36:177"],[20,"│ ├── abis.js"],[20,"\n","24:\"Md6M\"|36:177"],[20,"│ └── addresses.js"],[20,"\n","24:\"GNCQ\"|36:177"],[20,"├── functions/"],[20,"\n","24:\"At8I\"|36:177"],[20,"│ └── exec.js"],[20,"\n","24:\"L8Fs\"|36:177"],[20,"└── serverless.yml"],[20,"\n","24:\"4z7D\"|36:177"],[20,"contracts/","1:\"%23d9d9d9\"|27:\"12\""],[20,"包含智能合约ABI和地址。","27:\"12\""],[20,"\n","24:\"4tGl\"|27:\"12\"|bullet-id:\"8NAu\"|bullet:\"circle\""],[20,"functions/","1:\"%23d9d9d9\"|27:\"12\""],[20,"包含实现业务逻辑的JS函数。","27:\"12\""],[20,"\n","24:\"MhNX\"|27:\"12\"|bullet-id:\"8NAu\"|bullet:\"circle\""],[20,"serverless.yml","1:\"%23d9d9d9\"|27:\"12\""],[20,"描述服务配置。","27:\"12\""],[20,"\n","24:\"bBLp\"|27:\"12\"|bullet-id:\"8NAu\"|bullet:\"circle\""],[20,"\t接下来,我们将深入了解一下各个部分。"],[20,"\n","24:\"rZcI\"|7:3"],[20,"合约样本示例","0:\"%23ed7d31\""],[20,"\n","24:\"hIq4\"|7:3"],[20,"\t为了进行测试,我编写并部署了一个智能合约示例:"],[20,"\n","24:\"crAe\"|7:3"],[20,"// SPDX-License-Identifier: GPL-3.0"],[20,"\n","24:\"3Dqz\"|36:177"],[20,"pragma solidity ^0.6.10;"],[20,"\n","24:\"3aVT\"|36:177"],[20,"\n","24:\"lu48\"|36:177"],[20,"contract DummyStorage {"],[20,"\n","24:\"NmsT\"|36:177"],[20," event Write(address indexed source, uint256 value);"],[20,"\n","24:\"o9F4\"|36:177"],[20,"\n","24:\"DsWP\"|36:177"],[20," uint internal _currentValue;"],[20,"\n","24:\"C1AR\"|36:177"],[20,"\n","24:\"7FlN\"|36:177"],[20," function get() public view returns (uint) {"],[20,"\n","24:\"aIgA\"|36:177"],[20," return _currentValue;"],[20,"\n","24:\"y1EZ\"|36:177"],[20," }"],[20,"\n","24:\"LlaB\"|36:177"],[20,"\n","24:\"63K3\"|36:177"],[20," function put(uint value) public {"],[20,"\n","24:\"eMtM\"|36:177"],[20," emit Write(msg.sender, value);"],[20,"\n","24:\"Bffc\"|36:177"],[20," _currentValue = value;"],[20,"\n","24:\"Eyqo\"|36:177"],[20," }"],[20,"\n","24:\"D2So\"|36:177"],[20,"}"],[20,"\n","24:\"e0Zd\"|36:177"],[20,"\t该"],[20,"DummyStorage","1:\"%23d9d9d9\""],[20,"智能合约具有以下功能:"],[20,"\n","24:\"0E5G\"|7:3"],[20,"get","1:\"%23d9d9d9\"|27:\"12\""],[20,"是一个能反馈合约当前值的只读函数。","27:\"12\""],[20,"\n","24:\"oXQz\"|27:\"12\"|bullet-id:\"btx3\"|bullet:\"circle\""],[20,"put","1:\"%23d9d9d9\"|27:\"12\""],[20,"是一个用于更新合约当前值的写入函数。","27:\"12\""],[20,"\n","24:\"PiqO\"|27:\"12\"|bullet-id:\"btx3\"|bullet:\"circle\""],[20,"\t该示例合约已经过验证并在Ropsten上运行。大家可以用它来测试自己的函数!"],[20,"\n","24:\"6vSy\"|7:3"],[20,"1. 智能合约ABIs","8:1"],[20,"\n","24:\"lfyS\"|7:3"],[20,"\t合约目录中包含与函数交互的合约ABIs。在示例项目中,它包含DummyStorage合约的ABI。"],[20,"\n","24:\"riWS\"|7:3"],[20,"├── contracts/"],[20,"\n","24:\"k1ZQ\"|36:177"],[20,"│ ├── abis/"],[20,"\n","24:\"8hQs\"|36:177"],[20,"│ │ └── DummyStorage.json"],[20,"\n","24:\"8nBq\"|36:177"],[20,"│ ├── abis.js"],[20,"\n","24:\"swUN\"|36:177"],[20,"│ └── addresses.js"],[20,"\n","24:\"HCGl\"|36:177"],[20,"\t我们可以将ABI看作是智能合约的公共API规范,类似于OpenAPI规范。我们需要用ABI来调用合约函数。"],[20,"\n","24:\"7RrB\"|7:3"],[20,"\t合约/目录结构能协助我们导入合约ABI和地址:"],[20,"\n","24:\"R4S1\"|7:3"],[20,"// functions/exec.js"],[20,"\n","24:\"gzSP\"|36:177"],[20,"\n","24:\"Om1h\"|36:177"],[20,"const { abis, addresses } = require('../contracts');"],[20,"\n","24:\"O8vg\"|36:177"],[20,"\n","24:\"PuXe\"|36:177"],[20,"const DummyStorageABI = abis.DummyStorage;"],[20,"\n","24:\"dbLw\"|36:177"],[20,"const DummyStorageAddress = addresses.DummyStorage;"],[20,"\n","24:\"cgdY\"|36:177"],[20,"这都是我们在接下来的函数部分中将要用到的。"],[20,"\n","24:\"v7Q0\"|7:3"],[20,"2. Functions","8:1"],[20,"\n","24:\"4tT7\"|7:3"],[20,"exec函数利用Ethers来加载合约ABI并调用智能合约:"],[20,"\n","24:\"WkSd\"|7:3"],[20,"// Initialize contract"],[20,"\n","24:\"qLNG\"|36:177"],[20,"const contract = new ethers.Contract("],[20,"\n","24:\"DKmh\"|36:177"],[20," DummyStorageAddress,"],[20,"\n","24:\"buxw\"|36:177"],[20," DummyStorageABI,"],[20,"\n","24:\"wIm9\"|36:177"],[20," wallet,"],[20,"\n","24:\"VLwx\"|36:177"],[20,")"],[20,"\n","24:\"8Bgu\"|36:177"],[20,"\n","24:\"Ay9g\"|36:177"],[20,"// Call smart contract function " put="put" random_integer="Math.floor(Math.random()" returns="returns" a="a" random="random" integer="integer" from="from" to="to" quot="quot" tx="await" contract="contract" serverless="serverless" serverless-ethers="serverless-ethers" name:="name:" aws="aws" runtime:="runtime:" nodejs12="nodejs12" region:="region:" ap-southeast-1="ap-southeast-1" timeout:="timeout:" environment:="environment:" default_gas_price:="default_gas_price:" mnemonic:="mnemonic:" slack_hook_url:="slack_hook_url:" webhooks="webhooks" lambda="lambda" secrets="secrets" manager="manager" invoke="invoke" local="local" f="f" exec="exec" abis="abis" loaded="loaded" wallet="wallet" transaction="transaction" sent="sent" https:="https:" deploy="deploy" packaging="packaging" service="service" excluding="excluding" development="development" dependencies="dependencies" creating="creating" stack="stack" checking="checking" create="create" progress="progress" finished="finished" uploading="uploading" cloudformation="cloudformation" file="file" s3="s3" artifacts="artifacts" mb="mb" validating="validating" template="template" updating="updating" update="update" information="information" dev="dev" serverless-ethers-dev="serverless-ethers-dev" keys:="keys:" none="none" exec:="exec:" serverless-ethers-dev-exec="serverless-ethers-dev-exec" chatops="chatops" successmessage=":white_check_mark: Transaction sent https://ropsten.etherscan.io/tx/${tx.hash}" posttoslack="posttoslack" console="console" v5="v5" events="events" api="api" given="given" the="the" following="following" event:="event:" event="event" transfer="transfer" indexed="indexed" node="node" address="address" owner="owner" get="get" filter="filter" second="second" null="null" could="could" be="be" omitted="omitted" query="query" logs="contract.queryFilter(filter," block="block" latest="latest" print="print" out="out" all="all" values:=">" log="log" control="control"> 作者 | Yos Riady 译者 | 火火酱,责编 | 李雪敬 头图 | CSDN 下载自视觉中国 “智能合约”这个名字其实并不确切。尽管名字中有“智能”二字,但 Ethereum 上的智能合约并不能全自动执行。智能合约代码的运行需要借助外力的触发。换句话说,我们需要通过一些外部流程来触发智能合约。 在本文中,我们将通过构建可行的解决方案来解决该问题,了解一下: 为什么需要链下智能合约自动化 智能合约自动化的用例 如何借助无服务器架构来部署无服务器功能 最后,我们还将介绍serverless-ethers——全功能的智能合约自动化服务,部署后可直接使用。我们可以以此服务为基础,构建符合自己需求的自定义智能合约自动化项目。 问题是:名叫智能合约,却无法自动执行 假设我们想要实现一个能够每小时自动执行一次的智能合约。要怎么做呢? 现实就是:根本做不到。仅靠 Solidity 智能合约是做不到这一点的。尽管名叫“智能合约”,但 Ethereum 中的智能合约并不能自动执行,我们需要借助外部源(人或机器)来调用智能合约并执行其代码。 合约最多能做到的是:在不同任务间插入一小时间隔,例如: function runMe() public { 来谈一谈“自动e执行” 从技术层面上来看,有一些操作是可以使用函数修改器来自动执行的。比如说Compound Governance的COMP分配。一旦地址获得了0.001COMP,之后所有的Compound交易(例如提供资产,或转移cToken)都会自动将COMP转到其钱包中。 我们可以在函数修改器中实现上述逻辑,将修改器放在函数前,并在调用函数时自动执行逻辑。由调用方来支付相关的附加费用。 然而,并不是所有的智能合约系统都可以采用这种方法。由于这些修改器只能在特定条件下运行,因此可能会导致意料之外的gas费用。同时,还可能会向用户随机收取额外的gas费用,以实现合约“平衡性”。 并且,代码的运行仍然需要通过人为调用智能合约才能实现。 智能合约自动化的常见用例 DeFi协议依赖于某种链下智能合约自动化。MakerDAO依赖第三方来监控债务头寸的抵押担保比率,并清算担保不足的头寸。其他的DeFi协议也都有类似的需求。 在链下智能合约自动化方面,有两个常见用例: 自动触发器(Automated Triggers):在特定情况下执行合约。 状态和事件监控(State and Event Monitoring):了解合约在何时出现特定状态。1. 自动触发器我们经常需要定期、或在特定条件下执行合约,例如: 周期性地恢复平衡池 结束DAO/治理过程中的投票 按比例支付安全代币股息2. 状态和事件监控有时我们需要了解合约是否满足了某些条件,例如: 了解智能合约的价值是否发生了变化 获取所有准入限制更改的通知 了解何时发出特定的智能合约事件 解决方案:无服务器函数? 实际上,无服务器功能刚好适用于上面提到的这几个用例。有了无服务器化,我们便无需在部署代码之前预配任何东西,并且之后也不需要费心管理,极大地简化了问题的解决方案。 = lastTriggeredAt + 1 hour);"],[20,"\n","24:\"Y7Ln\"|36:177"],[20," ..."],[20,"\n","24:\"Eqxc\"|36:177"],[20,"}"],[20,"\n","24:\"pKDU\"|36:177"],[20,"\t以上“require()”语句可确保两次执行至少间隔一小时。但是,仍需要在开始时人为触发智能合约,然后代码才会正常运行。"],[20,"\n","24:\"2Fgq\"|7:3"],[20,"来谈一谈“自动e执行”","0:\"%23ed7d31\""],[20,"\n","24:\"0Qrh\"|7:3"],[20,"\t从技术层面上来看,有一些操作是可以使用函数修改器来自动执行的。比如说Compound Governance的COMP分配。一旦地址获得了0.001COMP,之后所有的Compound交易(例如提供资产,或转移cToken)都会自动将COMP转到其钱包中。"],[20,"\n","24:\"gcwW\"|7:3"],[20,"\t我们可以在函数修改器中实现上述逻辑,将修改器放在函数前,并在调用函数时自动执行逻辑。由调用方来支付相关的附加费用。"],[20,"\n","24:\"SdCR\"|7:3"],[20,"\t然而,并不是所有的智能合约系统都可以采用这种方法。由于这些修改器只能在特定条件下运行,因此可能会导致意料之外的gas费用。同时,还可能会向用户随机收取额外的gas费用,以实现合约“平衡性”。"],[20,"\n","24:\"UE1L\"|7:3"],[20,"\t并且,代码的运行仍然需要通过人为调用智能合约才能实现。"],[20,"\n","24:\"84Wo\"|7:3"],[20,"智能合约自动化的常见用例","0:\"%23ed7d31\""],[20,"\n","24:\"ylI9\"|7:3"],[20,"\tDeFi协议依赖于某种链下智能合约自动化。MakerDAO依赖第三方来监控债务头寸的抵押担保比率,并清算担保不足的头寸。其他的DeFi协议也都有类似的需求。"],[20,"\n","24:\"kzHE\"|7:3"],[20,"\t在链下智能合约自动化方面,有两个常见用例:"],[20,"\n","24:\"4E62\"|7:3"],[20,"自动触发器(Automated Triggers):在特定情况下执行合约。","27:\"12\"|8:1"],[20,"\n","24:\"BRHr\"|27:\"12\"|bullet-id:\"IFIv\"|bullet:\"circle\""],[20,"状态和事件监控(State and Event Monitoring):了解合约在何时出现特定状态。","27:\"12\"|8:1"],[20,"\n","24:\"WLyc\"|27:\"12\"|bullet-id:\"IFIv\"|bullet:\"circle\""],[20,"1. 自动触发器","27:\"12\""],[20,"\n","24:\"voxY\""],[20,"\t我们经常需要定期、或在特定条件下执行合约,例如:"],[20,"\n","24:\"9BtC\"|7:3"],[20,"周期性地恢复平衡池","27:\"12\""],[20,"\n","24:\"rocz\"|27:\"12\"|bullet-id:\"i378\"|bullet:\"circle\""],[20,"结束DAO/治理过程中的投票","27:\"12\""],[20,"\n","24:\"DCLG\"|27:\"12\"|bullet-id:\"i378\"|bullet:\"circle\""],[20,"按比例支付安全代币股息","27:\"12\""],[20,"\n","24:\"q1Ie\"|27:\"12\"|bullet-id:\"i378\"|bullet:\"circle\""],[20,"2. 状态和事件监控"],[20,"\n","24:\"QSw5\"|7:3"],[20,"\t有时我们需要了解合约是否满足了某些条件,例如:"],[20,"\n","24:\"lBSi\"|7:3"],[20,"了解智能合约的价值是否发生了变化","27:\"12\""],[20,"\n","24:\"4D7H\"|27:\"12\"|bullet-id:\"6BqR\"|bullet:\"circle\""],[20,"获取所有准入限制更改的通知","27:\"12\""],[20,"\n","24:\"iyGl\"|27:\"12\"|bullet-id:\"6BqR\"|bullet:\"circle\""],[20,"了解何时发出特定的智能合约事件","27:\"12\""],[20,"\n","24:\"HPdh\"|27:\"12\"|bullet-id:\"6BqR\"|bullet:\"circle\""],[20,"解决方案:无服务器函数?","0:\"%23ed7d31\""],[20,"\n","24:\"ymMx\"|7:3"],[20,"\t实际上,无服务器功能刚好适用于上面提到的这几个用例。有了无服务器化,我们便无需在部署代码之前预配任何东西,并且之后也不需要费心管理,极大地简化了问题的解决方案。"],[20,"\n","24:\"dmzk\"|7:3"],[20,"快速入门:借助Serverless Framework来实现无服务器化","0:\"%23ed7d31\""],[20,"\n","24:\"CkoA\"|7:3"],[20,"\t无服务器架构(Serverless Framework)为我们提供了开发、部署、监控和保护无服务器应用程序所需的一切内容。让我们一起来看看如何能够以最简单的方式完成开发吧。"],[20,"\n","24:\"wFzK\"|7:3"],[20,"> npm install -g serverless"],[20,"\n","24:\"5pRP\"|36:177"],[20,"> serverless -v"],[20,"\n","24:\"xNab\"|36:177"],[20,"x.x.x"],[20,"\n","24:\"tuIh\"|36:177"],[20,"\t首先,我们来快速了解一下Serverless Framework的运作方式。"],[20,"\n","24:\"4lZj\"|7:3"],[20,"0. serverless.yml","8:1"],[20,"\n","24:\"Fszg\"|7:3"],[20,"\t所有Serverless服务中的Lambda函数和事件都可以在名为serverless.yml的配置文件中找到。该文件对服务(包含Functions和Events)进行了定义。"],[20,"\n","24:\"yVl2\"|7:3"],[20,"service: serverless-ethers"],[20,"\n","24:\"h3IV\"|36:177"],[20,"provider:"],[20,"\n","24:\"vX6G\"|36:177"],[20," name: aws"],[20,"\n","24:\"BYPY\"|36:177"],[20," runtime: nodejs12.x"],[20,"\n","24:\"KQv7\"|36:177"],[20," environment:"],[20,"\n","24:\"HTbp\"|36:177"],[20," CHAIN_ID: 3"],[20,"\n","24:\"tTpR\"|36:177"],[20," DEFAULT_GAS_PRICE: 60000000000"],[20,"\n","24:\"Tti9\"|36:177"],[20,"\n","24:\"P7rJ\"|36:177"],[20,"functions:"],[20,"\n","24:\"70LP\"|36:177"],[20," myFunc:"],[20,"\n","24:\"ZhNL\"|36:177"],[20," handler: functions/myFunc.handler"],[20,"\n","24:\"x4KH\"|36:177"],[20," events:"],[20,"\n","24:\"G2yQ\"|36:177"],[20," - schedule: rate(2 hours)"],[20,"\n","24:\"SMlj\"|36:177"],[20,"\t我们可以在"],[20,"function","1:\"%23d9d9d9\""],[20,"属性下,对无服务器函数进行定义。在上面的例子中:"],[20,"\n","24:\"VexS\"|7:3"],[20,"我们有名为","27:\"12\""],[20,"myFunc","1:\"%23d9d9d9\"|27:\"12\""],[20,"的Function","27:\"12\""],[20,"\n","24:\"YhJ3\"|27:\"12\"|bullet-id:\"zkiM\"|bullet:\"circle\""],[20,"handler","1:\"%23d9d9d9\"|27:\"12\""],[20,"属性指向包含你想在函数中运行的代码的文件和模块","27:\"12\""],[20,"\n","24:\"fA9U\"|27:\"12\"|bullet-id:\"zkiM\"|bullet:\"circle\""],[20,"events","1:\"%23d9d9d9\"|27:\"12\""],[20,"属性为要执行的函数指定Event触发器","27:\"12\""],[20,"\n","24:\"JzbL\"|27:\"12\"|bullet-id:\"zkiM\"|bullet:\"circle\""],[20,"\t一个服务中可以包含多个函数。"],[20,"\n","24:\"ZJuS\"|7:3"],[20,"1.","8:1"],[20," ","27:\"9\"|31:12|8:1"],[20,"Functions","8:1"],[20," "],[20,"\n","24:\"jXu3\"|7:3"],[20,"\tFunction是AWS Lambda函数,是一个类似于微服务的独立部署单元。作为一段部署在云中的代码,通常被用于执行单个作业。"],[20,"\n","24:\"Uiga\"|7:3"],[20,"// functions/myFunc.js"],[20,"\n","24:\"6gxf\"|36:177"],[20,"exports.handler = async function(event, context) {"],[20,"\n","24:\"FHen\"|36:177"],[20," // Do anything"],[20,"\n","24:\"RfVh\"|36:177"],[20,"};"],[20,"\n","24:\"Kd4L\"|36:177"],[20,"\tFunctions只是普通的JS函数,可以将事件对象作为有效负载。"],[20,"\n","24:\"yBt8\"|7:3"],[20,"2. Events","8:1"],[20,"\n","24:\"wEKC\""],[20,"\tEvents是触发函数运行的事件,隶属于每个Function,可以在"],[20,"serverless.yml","1:\"%23d9d9d9\""],[20,"中的事件属性中找到。"],[20,"\n","24:\"zwtB\"|7:3"],[20,"\t我们可以使用Scheduled Events触发器来定期自动执行函数。例如,我们指定每2小时运行一次myFunc函数:"],[20,"\n","24:\"NIt6\"|7:3"],[20,"# serverless.yml"],[20,"\n","24:\"yeei\"|36:177"],[20,"\n","24:\"J49m\"|36:177"],[20,"functions:"],[20,"\n","24:\"1H9B\"|36:177"],[20," myFunc:"],[20,"\n","24:\"UOXm\"|36:177"],[20," handler: functions/myFunc.handler"],[20,"\n","24:\"CH2K\"|36:177"],[20," events:"],[20,"\n","24:\"6WuV\"|36:177"],[20," - schedule: rate(2 hours)"],[20,"\n","24:\"PvRI\"|36:177"],[20,"\t我们还可以借助cron schedule expression来指定安排计划"],[20,"\n","24:\"cAJX\"|7:3"],[20,"# serverless.yml"],[20,"\n","24:\"Pxmm\"|36:177"],[20,"\n","24:\"XyAV\"|36:177"],[20,"events:"],[20,"\n","24:\"S7Td\"|36:177"],[20," - schedule: cron(0 12 * * ? *) # 12PM UTC"],[20,"\n","24:\"HbwP\"|36:177"],[20,"\t"],[20,"如果你使用的是AWS的话,事件即为AWS中可以出发AWS Lambda函数的任意事件,比如:","0:\"%233c3c3b\""],[20,"\n","24:\"KG8P\"|7:3"],[20,"AWS API Gateway HTTP端点请求(例如,REST API)","27:\"12\""],[20,"\n","24:\"ho8L\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"AWS S3存储桶上传(例如,图像)","27:\"12\""],[20,"\n","24:\"Rsbw\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"CloudWatch计时器(例如,每5分钟运行一次)","27:\"12\""],[20,"\n","24:\"QgLs\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"AWS SNS主题(例如,信息)","27:\"12\""],[20,"\n","24:\"vKv9\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"等等……","27:\"12\""],[20,"\n","24:\"KUSR\"|27:\"12\"|bullet-id:\"6xWq\"|bullet:\"circle\""],[20,"\t就目前而言,知道这些就够了。如果还想了解更多关于Serverless framework的内容的话,可以看一下这个文件("],[20,"https://www.serverless.com/framework/docs/?ref=hackernoon.com","16:\"https%3A%2F%2Fwww.serverless.com%2Fframework%2Fdocs%2F%3Fref%3Dhackernoon.com\""],[20,")"],[20,"\n","24:\"IWS8\"|7:3"],[20,"\t在了解了Serverless Framework的基础知识后,我们来看一看"],[20,"serverless-ethers","1:\"%23d9d9d9\""],[20,"服务吧。"],[20,"\n","24:\"dVqI\"|7:3"],[20,"serverless-ethers是什么","0:\"%23ed7d31\""],[20,"\n","24:\"nKbm\"|7:3"],[20,"\t"],[20,"serverless-ethers","1:\"%23d9d9d9\""],[20,"是一个全功能Serverless服务,部署后即可直接使用。"],[20,"\n","24:\"AFON\"|7:3"],[20,"git clone [email protected]:yosriady/serverless-ethers.git"],[20,"\n","24:\"z9Oa\"|36:177"],[20,"cd serverless-ethers"],[20,"\n","24:\"azhd\"|36:177"],[20,"nvm use"],[20,"\n","24:\"Ielg\"|36:177"],[20,"npm install"],[20,"\n","24:\"R4Wt\"|36:177"],[20,"\t我们可以将此项目作为构建自定义智能合约自动化的基础。其预先配置的是AWS,但修改后也适用于其他云提供商(如GCP、Azure等)。"],[20,"\n","24:\"35qT\"|7:3"],[20,"\t"],[20,"serverless-ethers","1:\"%23d9d9d9\""],[20,"项目的结构如下:"],[20,"\n","24:\"OgTX\"|7:3"],[20,"├── contracts/"],[20,"\n","24:\"OITE\"|36:177"],[20,"│ ├── abis/"],[20,"\n","24:\"XGcf\"|36:177"],[20,"│ ├── abis.js"],[20,"\n","24:\"Md6M\"|36:177"],[20,"│ └── addresses.js"],[20,"\n","24:\"GNCQ\"|36:177"],[20,"├── functions/"],[20,"\n","24:\"At8I\"|36:177"],[20,"│ └── exec.js"],[20,"\n","24:\"L8Fs\"|36:177"],[20,"└── serverless.yml"],[20,"\n","24:\"4z7D\"|36:177"],[20,"contracts/","1:\"%23d9d9d9\"|27:\"12\""],[20,"包含智能合约ABI和地址。","27:\"12\""],[20,"\n","24:\"4tGl\"|27:\"12\"|bullet-id:\"8NAu\"|bullet:\"circle\""],[20,"functions/","1:\"%23d9d9d9\"|27:\"12\""],[20,"包含实现业务逻辑的JS函数。","27:\"12\""],[20,"\n","24:\"MhNX\"|27:\"12\"|bullet-id:\"8NAu\"|bullet:\"circle\""],[20,"serverless.yml","1:\"%23d9d9d9\"|27:\"12\""],[20,"描述服务配置。","27:\"12\""],[20,"\n","24:\"bBLp\"|27:\"12\"|bullet-id:\"8NAu\"|bullet:\"circle\""],[20,"\t接下来,我们将深入了解一下各个部分。"],[20,"\n","24:\"rZcI\"|7:3"],[20,"合约样本示例","0:\"%23ed7d31\""],[20,"\n","24:\"hIq4\"|7:3"],[20,"\t为了进行测试,我编写并部署了一个智能合约示例:"],[20,"\n","24:\"crAe\"|7:3"],[20,"// SPDX-License-Identifier: GPL-3.0"],[20,"\n","24:\"3Dqz\"|36:177"],[20,"pragma solidity ^0.6.10;"],[20,"\n","24:\"3aVT\"|36:177"],[20,"\n","24:\"lu48\"|36:177"],[20,"contract DummyStorage {"],[20,"\n","24:\"NmsT\"|36:177"],[20," event Write(address indexed source, uint256 value);"],[20,"\n","24:\"o9F4\"|36:177"],[20,"\n","24:\"DsWP\"|36:177"],[20," uint internal _currentValue;"],[20,"\n","24:\"C1AR\"|36:177"],[20,"\n","24:\"7FlN\"|36:177"],[20," function get() public view returns (uint) {"],[20,"\n","24:\"aIgA\"|36:177"],[20," return _currentValue;"],[20,"\n","24:\"y1EZ\"|36:177"],[20," }"],[20,"\n","24:\"LlaB\"|36:177"],[20,"\n","24:\"63K3\"|36:177"],[20," function put(uint value) public {"],[20,"\n","24:\"eMtM\"|36:177"],[20," emit Write(msg.sender, value);"],[20,"\n","24:\"Bffc\"|36:177"],[20," _currentValue = value;"],[20,"\n","24:\"Eyqo\"|36:177"],[20," }"],[20,"\n","24:\"D2So\"|36:177"],[20,"}"],[20,"\n","24:\"e0Zd\"|36:177"],[20,"\t该"],[20,"DummyStorage","1:\"%23d9d9d9\""],[20,"智能合约具有以下功能:"],[20,"\n","24:\"0E5G\"|7:3"],[20,"get","1:\"%23d9d9d9\"|27:\"12\""],[20,"是一个能反馈合约当前值的只读函数。","27:\"12\""],[20,"\n","24:\"oXQz\"|27:\"12\"|bullet-id:\"btx3\"|bullet:\"circle\""],[20,"put","1:\"%23d9d9d9\"|27:\"12\""],[20,"是一个用于更新合约当前值的写入函数。","27:\"12\""],[20,"\n","24:\"PiqO\"|27:\"12\"|bullet-id:\"btx3\"|bullet:\"circle\""],[20,"\t该示例合约已经过验证并在Ropsten上运行。大家可以用它来测试自己的函数!"],[20,"\n","24:\"6vSy\"|7:3"],[20,"1. 智能合约ABIs","8:1"],[20,"\n","24:\"lfyS\"|7:3"],[20,"\t合约目录中包含与函数交互的合约ABIs。在示例项目中,它包含DummyStorage合约的ABI。"],[20,"\n","24:\"riWS\"|7:3"],[20,"├── contracts/"],[20,"\n","24:\"k1ZQ\"|36:177"],[20,"│ ├── abis/"],[20,"\n","24:\"8hQs\"|36:177"],[20,"│ │ └── DummyStorage.json"],[20,"\n","24:\"8nBq\"|36:177"],[20,"│ ├── abis.js"],[20,"\n","24:\"swUN\"|36:177"],[20,"│ └── addresses.js"],[20,"\n","24:\"HCGl\"|36:177"],[20,"\t我们可以将ABI看作是智能合约的公共API规范,类似于OpenAPI规范。我们需要用ABI来调用合约函数。"],[20,"\n","24:\"7RrB\"|7:3"],[20,"\t合约/目录结构能协助我们导入合约ABI和地址:"],[20,"\n","24:\"R4S1\"|7:3"],[20,"// functions/exec.js"],[20,"\n","24:\"gzSP\"|36:177"],[20,"\n","24:\"Om1h\"|36:177"],[20,"const { abis, addresses } = require('../contracts');"],[20,"\n","24:\"O8vg\"|36:177"],[20,"\n","24:\"PuXe\"|36:177"],[20,"const DummyStorageABI = abis.DummyStorage;"],[20,"\n","24:\"dbLw\"|36:177"],[20,"const DummyStorageAddress = addresses.DummyStorage;"],[20,"\n","24:\"cgdY\"|36:177"],[20,"这都是我们在接下来的函数部分中将要用到的。"],[20,"\n","24:\"v7Q0\"|7:3"],[20,"2. Functions","8:1"],[20,"\n","24:\"4tT7\"|7:3"],[20,"exec函数利用Ethers来加载合约ABI并调用智能合约:"],[20,"\n","24:\"WkSd\"|7:3"],[20,"// Initialize contract"],[20,"\n","24:\"qLNG\"|36:177"],[20,"const contract = new ethers.Contract("],[20,"\n","24:\"DKmh\"|36:177"],[20," DummyStorageAddress,"],[20,"\n","24:\"buxw\"|36:177"],[20," DummyStorageABI,"],[20,"\n","24:\"wIm9\"|36:177"],[20," wallet,"],[20,"\n","24:\"VLwx\"|36:177"],[20,")"],[20,"\n","24:\"8Bgu\"|36:177"],[20,"\n","24:\"Ay9g\"|36:177"],[20,"// Call smart contract function " put="put" random_integer="Math.floor(Math.random()" returns="returns" a="a" random="random" integer="integer" from="from" to="to" quot="quot" tx="await" contract="contract" serverless="serverless" serverless-ethers="serverless-ethers" name:="name:" aws="aws" runtime:="runtime:" nodejs12="nodejs12" region:="region:" ap-southeast-1="ap-southeast-1" timeout:="timeout:" environment:="environment:" default_gas_price:="default_gas_price:" mnemonic:="mnemonic:" slack_hook_url:="slack_hook_url:" webhooks="webhooks" lambda="lambda" secrets="secrets" manager="manager" invoke="invoke" local="local" f="f" exec="exec" abis="abis" loaded="loaded" wallet="wallet" transaction="transaction" sent="sent" https:="https:" deploy="deploy" packaging="packaging" service="service" excluding="excluding" development="development" dependencies="dependencies" creating="creating" stack="stack" checking="checking" create="create" progress="progress" finished="finished" uploading="uploading" cloudformation="cloudformation" file="file" s3="s3" artifacts="artifacts" mb="mb" validating="validating" template="template" updating="updating" update="update" information="information" dev="dev" serverless-ethers-dev="serverless-ethers-dev" keys:="keys:" none="none" exec:="exec:" serverless-ethers-dev-exec="serverless-ethers-dev-exec" chatops="chatops" successmessage=":white_check_mark: Transaction sent https://ropsten.etherscan.io/tx/${tx.hash}" posttoslack="posttoslack" console="console" v5="v5" events="events" api="api" given="given" the="the" following="following" event:="event:" event="event" transfer="transfer" indexed="indexed" node="node" address="address" owner="owner" get="get" filter="filter" second="second" null="null" could="could" be="be" omitted="omitted" query="query" logs="contract.queryFilter(filter," block="block" latest="latest" print="print" out="out" all="all" values:=">" log="log" control="control">快速入门:借助Serverless Framework来实现无服务器化 无服务器架构(Serverless Framework)为我们提供了开发、部署、监控和保护无服务器应用程序所需的一切内容。让我们一起来看看如何能够以最简单的方式完成开发吧。 > npm install -g serverless service: serverless-ethers // functions/myFunc.js # serverless.yml # serverless.yml serverless-ethers是什么 serverless-ethers是一个全功能Serverless服务,部署后即可直接使用。 git clone [email protected]:yosriady/serverless-ethers.git ├── contracts/ 合约样本示例 为了进行测试,我编写并部署了一个智能合约示例: // SPDX-License-Identifier: GPL-3.0 ├── contracts/ // functions/exec.js // Initialize contract # serverless.yml 本地运行 我们可以使用无服务器CLI命令在本地运行函数。 > serverless invoke local -f exec 部署到AWS 运行serverless deploy即可轻松实现部署: > serverless deploy 写在最后 祝贺你学会了以下内容: 为什么需要链下智能合约自动化 智能合约自动化的用例 Serverless架构 serverless-ethers示例应用程序的运行原理补充:用Slack 实现ChatOps 除了serverless-ethers,我们还可以通过postToSlack函数来集成Slack。 const successMessage = `:white_check_mark: Transaction sent https://ropsten.etherscan.io/tx/${tx.hash}`; 补充:监控智能合约事件 截至目前,我们只介绍了“自动触发”用例,那要怎样监控智能合约状态和事件呢? 我们可以使用Ethers v5 Events API来定期监控特定事件。可以在函数中执行以下操作: // Given the following Event: 推荐阅读 腾讯云区块链邀您参加2020腾讯全球数字生态大会 借助 Solidity 来识别智能合约的调配模式 总计2171个BTC被盗,这个钱包漏洞的受害者越来越多 腾讯微博即将关停,十年了,你用过吗? 本文来源:区块链大本营 —- 编译者/作者:区块链大本营 玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。 |
如何构建无服务器智能合约自动化项目
2020-09-10 区块链大本营 来源:火星财经
LOADING...
相关阅读:
- 有人如何将200美元变成25万美元2020-09-10
- Tron在DeFi领域能否超越以太坊吗?2020-09-09
- 虚拟世界之战:区块链元宇宙经济学的审视IDamo精译?2020-09-09
- SuperZEC超级节点来袭,又一匹黑马出现匿名市场2020-09-09
- EPI—链接公链生态为资产流通加速2020-09-09