06.Chainlink_oracle
2024-04-14 14:40:22 # 12.DeFi

Chainlink_oracle

预言机

定义

  • 定义:智能合约无法获取区块链以外的数据,它是一个沙盒,外部API提供的数据和任何其他链下资源都无法获取。那么,预言机就充当这个角色:将真实世界的数据输送到智能合约中
    • 例子1:用户在质押平台质押了代币,那么平台就需要知道代币的价格,而价格无法在链上获取,需要从外部输入。
    • 例子2:保险公司说如果明天下雨,则赔偿给你,而明天下不下雨这件事,需要从外部输入
  • 由于区块链的节点之间,执行程序的结果都是相同的,因此不存在随机这个东西,否则无法达到共识。比如:生成随机数的函数,这是不存在的,只能是伪随机数。并且合约主动获取链下数据存在API不稳定、URL更新等一系列问题,因此必须有一个第三方合约来主动给合约来提供链下数据

工作流程

中心化预言机

运行一个中心化节点,然后提供数据给合约。

image-20230807213044503

问题:单点失败。如果整个区块链的信息依赖于某个节点,如果这个节点挂了,那么整个区块链数据就无法获取了

去中心化预言机

多个数据节点形成去中心预言机网络,每个节点都会收集数据,达成共识(聚合)后输入到区块链上的智能合约。达成公式:比如取中位数、平均数等

  1. 技术上,避免了单点失败风险
  2. 数据上,通过网络对多个数据源进行验证

chainlink就搞了一个这样的去中心化预言机网络,提供喂价、随机数等等,目前chainlink的共识机制是取中位数

喂价

原理

  • 喂价:Chainlink Data Fee

image-20230807213640925

  • 业务流程图:数据提供商和预言机节点是不一样的,数据提供商只负责收集数据,预言机节点会和其他节点聚合数据,达成共识

image-20230807213821074

  • 技术架构

image-20230807214057548

  • 使用chainlink预言机的项目

image-20230807214144131

使用

获得币对地址:https://docs.chain.link/data-feeds/price-feeds/addresses

remix

使用solidity获得goerli测试网中币对的价格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

// Proxy的接口
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract PriceFeed{
AggregatorV3Interface internal priceFeed;

constructor(){
priceFeed = AggregatorV3Interface(0x779877A7B0D9E8603169DdbD7836e478b4624789);
}

function getPrice() public view returns (int256){
// latestRoundData() returns
// uint80 roundId, // 价格的更新是一轮一轮的,每次更新+1
// int256 answer, // 价格数据
// uint256 startedAt, // 这个价格什么时候开始更新
// uint256 updatedAt, // 这个价格什么时候结束
// uint80 answeredInRound // 这个价格在第几轮更新
(,int256 answer,,,) = priceFeed.latestRoundData();
return answer; // BTC/ETH价格 = 15962257405504495000
}
}

hardhat

  1. npm init -yes
  2. npm install —save-dev hardhat
  3. npx hardhat init:选择空的
  4. 安装依赖:
    1. npm install —save-dev @chainlink/contracts
    2. npm install —save-dev chai-bn
    3. pm install —save-dev @nomicfoundation/hardhat-toolbox

DataFeedDemo.sol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

// Proxy的接口
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract DataFeedDemo{
AggregatorV3Interface internal priceFeed;

constructor(){
// https://docs.chain.link/data-feeds/price-feeds/addresses
priceFeed = AggregatorV3Interface(0x779877A7B0D9E8603169DdbD7836e478b4624789);
}

function getPrice() public view returns (int256){
// latestRoundData() returns
// uint80 roundId, // 价格的更新是一轮一轮的,每次更新+1
// int256 answer, // 价格数据
// uint256 startedAt, // 这个价格什么时候开始更新
// uint256 updatedAt, // 这个价格什么时候结束
// uint80 answeredInRound // 这个价格在第几轮更新
(,int256 answer,,,) = priceFeed.latestRoundData();
return answer; // BTC/ETH价格 = 15962257405504495000
}
}

deployDataFeed.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const { ethers } = require("hardhat") // ethers封装在hardhat里面

async function deployDataFeed(){
const DataFeed = await ethers.getContractFactory("DataFeedDemo")
const DataFeedContract = await DataFeed.deploy()
await DataFeedContract.waitForDeployment()
console.log("the contract is deployed successfully")

let dataFeedContractAddr = DataFeedContract.target
console.log("address of the contract is:" + dataFeedContractAddr)
}

deployDataFeed().then(()=>{
process.exit(0)
}).catch(err =>{
console.log(err)
})

DataFeedDemo.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const { ethers } = require("hardhat") // ethers封装在hardhat里面
const { expect } = require("chai")

describe("Data Feed Demo test", function(){
it("check if the price data return by oracle is greater than 0", async function(){
// 部署合约
const DataFeed = await ethers.getContractFactory("DataFeedDemo")
const DataFeedContract = await DataFeed.deploy()
await DataFeedContract.waitForDeployment()
console.log("the contract is deployed successfully")

// 获得价格数据
const result = await DataFeedContract.getPrice()

console.log("the price is :" + ethers.formatUnits(result, "ether"))

// 检查数据是否正常
expect(result).to.be.greaterThan("0")

})
})

hardhat.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** @type import('hardhat/config').HardhatUserConfig */
require("@nomicfoundation/hardhat-toolbox")

const privatekey = "xxx"
const goerliapi = "https://eth-goerli.g.alchemy.com/v2/xxx"

module.exports = {
solidity: "0.8.19",
networks: {
goerli: {
accounts: [privatekey],
url: goerliapi,
}
}
};

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "datafeeddemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@chainlink/contracts": "^0.6.1",
"@nomicfoundation/hardhat-toolbox": "^3.0.0",
"chai-bn": "^0.3.1",
"hardhat": "^2.17.1"
}
}

VRF

原理

  • 随机数生成器(RNG)-链上方案:挑选新生成的n个区块,计算他们的hash作为随机数。但某种程度上可被矿工控制,不可行。

  • 随机数生成器(RNG)-预言机:链下预言机生成,随机数需要被证明因此需要Proof进行验证(验证是否通过约定的随机算法生成,种子是不是当时链上的随机数hash),虽然随机数也是跟区块hash有关,但是矿工无法提前预知

image-20230808141723068

  • 可验证随机数(VRF)定义
    • 可证明性:验证者要查看chainlink没有作弊
    • 独特性:一个种子对应一个输出,chainlink无法选择一个对自己有利的输出
    • 伪随机性:数学算法

随机数算法VRF(90年代提出的密码学算法,论文内容)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.密钥生成函数(Key Gen)
G(r) = (PK,SK)
PK: public key
SK: secret key
# chainlink生成一个新的账户,用私钥来操作,把公钥写在合约变量中

2.随机数生达函数(Evaluate)
E(SK,seed)=>(Randomness,Proof)
seed: RNG的种子
Randomness: 随机数
Proof: 证明

3.验证函数(Verify)
V(PK,seed,Randomness,Proff) => (True or false)
  • 业务流程

image-20230808142813601

  • 技术架构
    1. 用户调用自己创建的Consumer合约的函数请求随机数,用户创建的合约需要实现RequestRandomWords()FulfillRandomWords()
    2. Consumer合约进一步调用Coordinator函数请求随机数
    3. 将PreSeed写入Event log
    4. 预言机读取Event log中的Preseed和blockhash
    5. 预言机通过VRF生成随机数和Proof
    6. 预言机将rc和proof写入Coordinator
    7. Coordinator进行验证&将随机数写入Consumer合约

image-20230808143033968

  • 使用场景

image-20230808143355734

使用

步骤

  1. 注册一个VRF,订阅它,告诉chainlink我们要用它的服务,并且可以查看随机数情况:https://vrf.chain.link/
  2. 将consumer合约(用户合约)加入到订阅
  3. consumer合约请求随机数
  4. consumer合约接收随机数

合约地址:https://docs.chain.link/vrf/v2/direct-funding/supported-networks和https://docs.chain.link/vrf/v2/subscription/supported-networks

等待我们实现的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pragma solidity ^0.8.0;

abstract contract VRFConsumerBaseV2 {
error OnlyCoordinatorCanFulfill(address have, address want);
address private immutable vrfCoordinator;

constructor(address _vrfCoordinator) {
vrfCoordinator = _vrfCoordinator;
}

// 在我们的consumer合约需要实现这个方法
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;

// Coordinator会回调这个方法进行写入
function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
// 查看调用者是不是vrfCoordinator
if (msg.sender != vrfCoordinator) {
revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
}
fulfillRandomWords(requestId, randomWords);
}
}

remix

获取subId

image-20230808151612289

image-20230808151725617

image-20230808151832779

image-20230808152012937

完成订阅,然后可以调用requestRandomWords()申请随机数

image-20230808152115900

需要稍微等一下,我们才能查看到s_randomWords随机数数据(在此之前也可以可以查询到s_requestId)

image-20230808152300396

成功

image-20230808154528133

随机数很大,实际应用中可以取模操作使他变小

image-20230808154705208

坑:uint32 callbackGasLimit = 5_200_00;设置太高或者太低都会失败。并且,如果你的link太少,Coordinator在回调的时候,无法成功,因此需要在页面中添加更多的link

image-20230808154544157

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract ChainlinkVRFDemo is VRFConsumerBaseV2{

// 1.获取Coordinator的接口
VRFCoordinatorV2Interface COORDINATOR;
address vrfCoordinatorAddr = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;
// 2.获取keyHash,服务质量
bytes32 keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
// 3.订阅ID号
uint64 s_subId;
// 4.我们认为3个区块之后获取VRF,实验方便起见,选3
uint16 requestConfirmations = 3;
// 5.gas回调设置,太小会导致无法写入随机数
uint32 callbackGasLimit = 5_200_00; // 太高或太小都会fail
// 6.我们请求4个随机数
uint32 numWords = 4;
address owner;

// 7.第几轮的随机数
uint256 public s_requestId;
// 8.随机数结果
uint256[] public s_randomWords;

constructor(uint64 subId) VRFConsumerBaseV2(vrfCoordinatorAddr){
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinatorAddr);
s_subId = subId;
owner = msg.sender;
}

function requestRandomWords() public {
// 我们给Coordinator发送请求,Coordinator又调用预言机节点返回随机数,预言机节点需要花费gas
// 因此gas不仅仅是调用者花费gas,预言机节点也会花费gas,因此做一点限制
require(msg.sender == owner);
// bytes32 keyHash, 其实就是公钥信息。不同的keyHash代表我支付不同的gas费用,比如我想要更好更快的,则选择质量更高的keyHash,服务商会提供你更好的服务
// uint64 subId, 订阅ID号
// uint16 requestConfirmations, 认为多少个区块之后能成功获取VRF,L1一般是12
// uint32 callbackGasLimit, VRF调用我们的fulfillrandomWords()时使用的gas上限
// uint32 numWords 请求多少个随机数,目前上限是500
// 返回一个requestID,告诉你是哪次请求的
// 9.请求随机数
s_requestId = COORDINATOR.requestRandomWords(keyHash, s_subId, requestConfirmations, callbackGasLimit, numWords);
}

function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override{
// 10.接收随机数
s_randomWords = randomWords;
}

}

hardhat

mock合约地址:https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol

  1. npm install —save-dev hardhat

  2. npx hardhat init选择空的

  3. 依赖

    • npm install —save-dev @chainlink/contracts
    • npm install —save-dev hardhat-deploy
    • npm install —save ethers@5.7
    • npm install —save-dev chai
    • npm install —save-dev ethereum-waffle:给chai加了一些方法
    • yarn add @nomiclabs/hardhat-waffle -D
    • npm install —save @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers:原因

    https://github.com/wighawag/hardhat-deploy

    if you use ethers.js we recommend you also install hardhat-deploy-ethers which add extra features to access deployments as ethers contract.

    Since hardhat-deploy-ethers is a fork of @nomiclabs/hardhat-ethers and that other plugin might have a hardcoded dependency on @nomiclabs/hardhat-ethers the best way to install hardhat-deploy-ethers and ensure compatibility is the following:

    1
    npm install --save-dev  @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers ethers

注意,下面的代码存在包版本错误的问题,并没有跑起来(deploy可以跑,test跑不了),主要理解思路

  • VRFConsume.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract ChainlinkVRFDemo is VRFConsumerBaseV2{

// 1.获取Coordinator的接口
VRFCoordinatorV2Interface COORDINATOR;
// 使用本地网络Mock
address vrfCoordinatorAddr;
// 2.获取keyHash,服务质量
bytes32 keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
// 3.订阅ID号
uint64 s_subId;
// 4.我们认为3个区块之后获取VRF,实验方便起见,选3
uint16 requestConfirmations = 3;
// 5.gas回调设置,太小会导致无法写入随机数
uint32 callbackGasLimit = 5_200_00; // 太高或太小都会fail
// 6.我们请求4个随机数
uint32 numWords = 4;
address owner;

// 7.第几轮的随机数
uint256 public s_requestId;
// 8.随机数结果
uint256[] public s_randomWords;

constructor(address _vrfCoordinatorAddr, uint64 subId) VRFConsumerBaseV2(vrfCoordinatorAddr){
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinatorAddr);
s_subId = subId;
owner = msg.sender;
vrfCoordinatorAddr = _vrfCoordinatorAddr;
}

function requestRandomWords() public {
// 我们给Coordinator发送请求,Coordinator又调用预言机节点返回随机数,预言机节点需要花费gas
// 因此gas不仅仅是调用者花费gas,预言机节点也会花费gas,因此做一点限制
require(msg.sender == owner);
// bytes32 keyHash, 其实就是公钥信息。不同的keyHash代表我支付不同的gas费用,比如我想要更好更快的,则选择质量更高的keyHash,服务商会提供你更好的服务
// uint64 subId, 订阅ID号
// uint16 requestConfirmations, 认为多少个区块之后能成功获取VRF,L1一般是12
// uint32 callbackGasLimit, VRF调用我们的fulfillrandomWords()时使用的gas上限
// uint32 numWords 请求多少个随机数,目前上限是500
// 返回一个requestID,告诉你是哪次请求的
// 9.请求随机数
s_requestId = COORDINATOR.requestRandomWords(keyHash, s_subId, requestConfirmations, callbackGasLimit, numWords);
}

function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override{
// 10.接收随机数
s_randomWords = randomWords;
}

}
  • VRFCoordinatorMock.sol
1
2
3
pragma solidity 0.8.19;

import "@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol";
  • 0_deploy_mock.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 起名原因:deploy是根据名字顺序进行部署,0代表第一个部署

const {getNamedAccounts, deployments} = require("hardhat")
// getNamedAccounts: 获取谁来部署的,私钥等信息
// deployments: 部署的时候需要的一些函数

const baseFee = "10000000000000000"; // 0.1 link
const gasPriceLink = "1000000000"; // 1 gwei

// 将hardhat的deploy这个插件所需要的一些函数、对象等进行exports,这样的话我们就可以识别我们已经输出的函数,对合约进行部署
module.exports = async ({getNamedAccounts, deployments}) =>{
const {deploy} = deployments
// 选择一个私钥来部署,在配置文件中设置
const {deployer} = await getNamedAccounts()

await deploy("VRFCoordinatorV2Mock",{
from: deployer,
// 合约构造函数的参数
// _baseFee: 运营商收取用户的基础费用
// _gasPriceLink:用户愿意支付的gasPrice
args: [baseFee, gasPriceLink],
log: true,
})
}

module.exports.tags = ["mock"]
  • 1_deploy_vrf_consumer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 起名原因:deploy是根据名字顺序进行部署,1代表第二个部署

const {getNamedAccounts, deployments} = require("hardhat")
// getNamedAccounts: 获取谁来部署的,私钥等信息
// deployments: 部署的时候需要的一些函数

// 将hardhat的deploy这个插件所需要的一些函数、对象等进行exports,这样的话我们就可以识别我们已经输出的函数,对合约进行部署
module.exports = async ({getNamedAccounts, deployments}) =>{
const {deploy} = deployments
// 选择一个私钥来部署,在配置文件中设置
const {deployer} = await getNamedAccounts()

let vrfCoordinatorAddr
let subId

// 得到Coordinator地址
let vrfCoordinator = await ethers.getContract("VRFCoordinatorV2Mock")
vrfCoordinatorAddr = vrfCoordinator.target

// 获得 subId
const tx = await vrfCoordinator.createSubscription()
const txReceipt = await tx.wait(1)
subId = txReceipt.logs[0].topics[1]

// 充值 link
await vrfCoordinator.fundSubscription(subId,"10000000000000000000")

// 部署
await deploy("ChainlinkVRFDemo",{
from: deployer,
// 合约构造函数的参数
// _baseFee: 运营商收取用户的基础费用
// _gasPriceLink:用户愿意支付的gasPrice
args: [vrfCoordinatorAddr, subId],
log: true,
})
}

module.exports.tags = ["vrf"]
  • test_vrf_consumer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const { expect,assert } = require("chai")
const { deployments, ethers } = require("hardhat")

describe("test VRFConsumer", async function(){
// 让前两个合约进行部署

let vrfCoordinator
let vrfConsumer

beforeEach(async() =>{
// 找到我们部署的合约,找到之后复制下来,相当于一个副本
await deployments.fixture(["mock","vrf"])
vrfCoordinator = await ethers.getContract("VRFCoordinatorV2Mock")
vrfConsumer = await ethers.getContract("ChainlinkVRFDemo")
})

it("check if we can request random number", async() =>{
expect(vrfConsumer.requestRandomWords()).to.emit(
vrfCoordinator, // 哪个合约发出的
"RandomWordsRequested" // 发出哪个事件
)
})

it("check if we can receive random number", async() =>{
console.log("test1")
await vrfConsumer.requestRandomWords()
console.log("test2")
const requestId = vrfConsumer.requestId()
let vrfConsumerAddr = vrfConsumer.target

// 我们需要自己触发回调函数写随机数
await vrfCoordinator.fulfillRandomWords(requestId,vrfConsumerAddr)

const randomWords0 = await vrfConsumer.s_randomWords(0)
const randomWords1 = await vrfConsumer.s_randomWords(1)
const randomWords2 = await vrfConsumer.s_randomWords(2)

assert(randomWords0.gt(ethers.constant.Zero),"first random wrong")
assert(randomWords1.gt(ethers.constant.Zero),"second random wrong")
assert(randomWords2.gt(ethers.constant.Zero),"third random wrong")
})
})
  • hardhat .config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
/** @type import('hardhat/config').HardhatUserConfig */
require("@nomiclabs/hardhat-ethers")
require("hardhat-deploy")
require("@nomiclabs/hardhat-waffle")

module.exports = {
solidity: "0.8.19",
namedAccounts: {
deployer: {
default: 0
}
}
};
  • package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"name": "vrfdemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@chainlink/contracts": "^0.6.1",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.1",
"@nomicfoundation/hardhat-ethers": "^3.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.6",
"chai": "^4.3.7",
"ethereum-waffle": "^4.0.10",
"hardhat": "^2.17.1",
"hardhat-deploy": "^0.11.34"
},
"dependencies": {
"@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers",
"ethers": "^6.7.0"
}
}

Keepers

原理

合约自动化执行

如果是如下的两种方式,存在一定的问题

image-20230808213240543

chainlink的方案

image-20230808213527191

流程

  1. Oracle检查CheckUpkeep,判断是否满足条件
  2. 如果CheckUpkeep条件不满足,则下个区块继续检查
  3. 如果CheckUpkeep条件满足,oracle 则调用keepers注册合约
  4. Keepers注册合约调用用户合约performUpkeep

image-20230808213556773

  • 技术架构

image-20230808213933885

  • 使用场景

image-20230808214100891

使用

remix

网站:https://automation.chain.link/goerli/new

image-20230809122501013

image-20230809122532171

image-20230809122727589

完成注册

image-20230809122853065

成功实验

image-20230809123204641

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// array [1000, 1000, 1000, ...]
// function => array [1000, 900, 1000, 900, ...]
// checkupkeep: update => performUpkeep
// checkupkeep: not updated => next round check

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/automation/interfaces/KeeperCompatibleInterface.sol";

contract KeepersDemo is KeeperCompatibleInterface{

uint256 public constant SIZE = 10;
uint256 public constant INITIAL_BALANCE = 1000;
uint256[SIZE] public balances;

constructor(){
for(uint256 i = 0; i < SIZE; i++){
balances[i] = INITIAL_BALANCE;
}
}

function withdraw(uint256 amount,uint256[] memory indexs) public {
for(uint256 i = 0; i < indexs.length; i++){
balances[indexs[i]] -= amount;
}
}

// chainlink预言机在链下执行
// 应该把绝大部分逻辑写在这里面
// performUpkeep()里面的操作应该尽可能的少,以此来减少gas消耗
// 可以通过bytes 信息告诉performUpkeep
function checkUpkeep(bytes calldata) external view override returns (bool upkeepNeeded, bytes memory performData){
upkeepNeeded = false;
for(uint256 i = 0; i < SIZE; i++){
if(balances[i] < INITIAL_BALANCE){
upkeepNeeded = true;
break;
}
}
// 如果performUpkeep()需要checkUpkeep()执行输出的一些数据,则可以在performData操作
return (upkeepNeeded,"");
}

function performUpkeep(bytes calldata) external {
for(uint256 i = 0; i < SIZE; i++){
if(balances[i] < INITIAL_BALANCE){
balances[i] = INITIAL_BALANCE;
}
}
}
}
Prev
2024-04-14 14:40:22 # 12.DeFi
Next