{"content":{"title":"预言机chainlink的使用——喂价、VRF","body":"本节内容：\r\n - [预言机](https:\/\/learnblockchain.cn\/article\/808)是什么 \r\n - 获取 Goerli 测试币\r\n - chainlink获取交易对价钱\r\n - VRF准备步骤\r\n - VRF使用\r\n\r\n# 一、chainlink 使用准备\r\n## 1.1 什么是预言机\r\n\r\n在智能合约中，无法获取区块链意外的数据，或外部数据不可靠，从而产生了预言机。预言机可使外部数据可信并保持唯一性。\r\n\r\n在使用 chainlink 之前要注意，（2022年9月29日） 之前很多资料所述在测试网需要使用 Rinkeby 网络，但由于 Ethereum的协议更改：Rinkeby、Ropsten和Kovan测试网络可能无法可靠地工作，很快就会被弃用。所以在此我们需要使用  Goerli 测试网络（当然chainlink 也支持其他网络）：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/e621ff07d98a442793450a988eb04863.png)\r\n\r\n## 1.2 获取预言机测试币 \r\n\r\n在 Goerli 测试网络下，需要获取 Link 以及对应的 eth 测试币 ，获取方式如下。\r\n\r\n首先导入 Link 的代币 ，导入步骤为：\r\n\r\n - 1.点击导入代币\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/798e229c11eb4be68655d49326e15d6a.png)\r\n\r\n - 2.点击后在如下页面中输入合约地 0x326C977E6efc84E512bB9C30f76E30c160eD06FB 以及 LINK\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/c4d7441bfb2b4a86a69c8247748c9756.png)\r\n\r\n - 3.最后进行添加即可，添加之后转到 [https:\/\/faucets.chain.link\/](https:\/\/faucets.chain.link\/)，随后在页面上点击连接钱包，出现如下界面：\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/b45698823ddd457e9f93591a8813828f.png)\r\n在此需要有一个推特账号并且有发布一条推特，最后点击验证后即可获取测试币 。\r\n\r\n#  二、 获取交易对价格\r\n\r\n使用 chainlink 预言机在 solidity 中需要引入 chainlink 的 sol 文件：\r\n\r\n```go\r\nimport \"@chainlink\/contracts\/src\/v0.8\/interfaces\/AggregatorV3Interface.sol\";\r\n```\r\n随后需要在 solidity 中创建一个 AggregatorV3Interface  对象：\r\n\r\n```go\r\nAggregatorV3Interface internal priceFeed;\r\n```\r\n\r\n可以创建一个构造函数用来对这个 reserveFeed 初始化：\r\n\r\n```go\r\nconstructor() {\r\n    reserveFeed = AggregatorV3Interface(0x779877A7B0D9E8603169DdbD7836e478b4624789);\r\n}\r\n```\r\n其中 AggregatorV3Interface 中所填入的地址为你需要对应数据的地址，这个地址可以在 chainlink文档中查询不同价格的地址。接下来创建一个方法，通过 priceFeed 调取最新 BTC \/ ETH 价格的价格：\r\n\r\n```go\r\npriceFeed.latestRoundData();\r\n```\r\n\r\n由于这个 latestRoundData 方法返回多个参数，在此我们仅获取价格即可，所有函数写成：\r\n\r\n```go\r\nfunction getLatestPrice() public view returns (int) {\r\n    (\r\n        \/*uint80 roundID*\/,\r\n        int price,\r\n        \/*uint startedAt*\/,\r\n        \/*uint timeStamp*\/,\r\n        \/*uint80 answeredInRound*\/\r\n    ) = priceFeed.latestRoundData();\r\n    return price;\r\n}\r\n```\r\n调取后即可得到最新价格：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/3d06d4a8f23f4bd5833313ac9eb7ef25.png)\r\n\r\n以上返回的数值是一个 18位的小数，也就是 14.615977000000000000\r\n完整合约代码如下（参考chainlink 官方示例）：\r\n\r\n```go\r\n\/\/ SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.7;\r\n\r\nimport \"@chainlink\/contracts\/src\/v0.8\/interfaces\/AggregatorV3Interface.sol\";\r\n\r\ncontract PriceConsumerV3 {\r\n\r\n    AggregatorV3Interface internal priceFeed;\r\n\r\n    \/**\r\n     * Network: Goerli\r\n     * Aggregator: ETH\/USD\r\n     * Address: 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e\r\n     *\/\r\n    constructor() {\r\n        priceFeed = AggregatorV3Interface(0x779877A7B0D9E8603169DdbD7836e478b4624789);\r\n    }\r\n\r\n    \/**\r\n     * Returns the latest price\r\n     *\/\r\n    function getLatestPrice() public view returns (int) {\r\n        (\r\n            \/*uint80 roundID*\/,\r\n            int price,\r\n            \/*uint startedAt*\/,\r\n            \/*uint timeStamp*\/,\r\n            \/*uint80 answeredInRound*\/\r\n        ) = priceFeed.latestRoundData();\r\n        return price;\r\n    }\r\n}\r\n```\r\n\r\n对于以上所述的地址就等于一个接口，这个接口可以通过 chainlink 的 doc 进行查看，链接为：[https:\/\/docs.chain.link\/docs\/data-feeds\/price-feeds\/addresses\/](https:\/\/docs.chain.link\/docs\/data-feeds\/price-feeds\/addresses\/)\r\n\r\n打开链接后，page 拉到下面，可以找到对应的测试网地址：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/66026370444e4f09a8267fbf764932d0.png)\r\n\r\n点击地址进入，选择合约，可以查看当前合约：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/5111040aa2554a4cb3e906bf54ac3fb3.png)\r\n接下来还可以直接点击 read Contract 查看相关读取的方法，找到我们所使用的的 latestRoundData 为我们所调用的方法：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/ba4d153c9ba943378ca1314fbeec065a.png)\r\n点击 query 就等于我们调用了该方法：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/4d408b0740fb448eb8cbc2bea5eb6cb0.png)\r\n之后将会出现最新的返回结果：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/a11b7257eb9347248f675f69f63252ba.png)\r\n其中参数：\r\n\r\n - roundid 表示第几轮价格（价格是一轮轮更新的） \r\n - answer 表示当前价格 \r\n - startedAt 什么时候开始更新的价格\r\n - updatedAt 什么时候开始更新结束的 \r\n - answeredlnRound 是第几轮更新的当前价格\r\n\r\n\r\n\r\n# 三、VRF 确定性的随机数使用准备\r\n## 3.1 创建订阅\r\nVRF 是 chainlink 提供的可验证随机数，在以往的随机数中，每个节点生成的随机数是不一样的，此时所有节点的某个结果不一致，是在区块链中是不允许的，chainlink 的 VRF 即可解决这个问题。\r\n\r\n在此我们只探讨 VRF 的使用方法，并不验证 VRF 的可验证性。\r\n\r\n使用 VRF 分为以下几个步骤（之后将会详细讲解）：\r\n\r\n 1. 创建一个订阅，有了订阅后才可以使用VRF （相当于注册个账号，方便接下来的使用）\r\n 2. 创建订阅后将会有一个 订阅ID，\r\n 3. 获取 订阅ID 获取到ID后，部署合约通过 订阅ID 初始化（构造函数完成）VRF的接口 \r\n 4. 部署合约后将合约地址通过网页指定调用随机数的合约\r\n\r\n由于我们使用搞得是 goerli 测试网络，在 chainlink 文档中找到 goerli 网络的 VRF 订阅地址，doc 链接为：\r\n[https:\/\/docs.chain.link\/docs\/vrf\/v2\/subscription\/supported-networks\/](https:\/\/docs.chain.link\/docs\/vrf\/v2\/subscription\/supported-networks\/)\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/38d2a412b02245ae89749446fd8c1b85.png)\r\n\r\n找到 goerli 网络的 VRF 订阅链接 [https:\/\/vrf.chain.link\/goerli](https:\/\/vrf.chain.link\/goerli) 通过地址进行链接订阅。\r\n\r\n接着打开 goerli 的订阅链接，在 web 页 中点击 Create Subscription 对 开始创建订阅：\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/a9a2f460fa34409983e68408d3e0c438.png)\r\n\r\n点击创建订阅后需要通过钱包账户进行链接，本文中使用小狐狸 metamask，点击按钮连接钱包：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/9b858553c1aa40448c4e1924381de67a.png)\r\n\r\n接着会获取到你钱包地址，点击 Create subscription 创建订阅：\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/25f0b220f91f42ee89072b49191d1a02.png)\r\n\r\n此时会发生一个交易，要注意你的钱包提示（有些同学可能会不直接弹出钱包，在此需要点击钱包后会弹出交易窗），点击确认交易（我点太快了）：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/5e4273e8258841859c2667137d82e055.png)\r\n随后稍等一会，将会出现已订阅提示，并且会出现一个 transaction 交易，可以点击查看：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/e74908c98b924620923634cf620e5b8d.png)\r\n点击查看交易后可以看到对应的交易信息（当然你可以不看，可以稍等片刻后交易完成）：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/8665e589ddf94d50a7009d8b223ed3c8.png)\r\n\r\n接着会出现一个窗口，叫你往里面存一点 LINK，这个 LINK 是用于生成随机数的花费，你可以选择10个就可以了，做个测试嘛，不需要太多，10个够十来次了（LINK 之前在前几节有说怎么获取）：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/b48f8da5e7bb4a26a925a896ed989e1a.png)\r\n随后小狐狸会弹出交易窗，交易完毕后即设置随机数生成花费成功：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/8d61a52b0c3c4b43b5fcc8d178841ae1.png)\r\n\r\n接下来就需要添加你的合约账户了，在这里你还可以看到这个ID，这个ID很重要，注意：\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/5946d059664f4dd59ec2b7bed914749f.png)\r\n\r\n此时我们只需要记录下这个订阅 ID 后就可以去写代码了，这个页面先保留，别急着叉掉。\r\n\r\n\r\n## 3.2 VRF 获取真随机数合约编写\r\n\r\n在合约中使用 VRF 需要 import 引入对应的合约，合约所在位置可以通过 chainlink 的 GitHub 进行查看，地址为：\r\n[https:\/\/github.com\/smartcontractkit\/chainlink\/blob\/develop\/contracts\/](https:\/\/github.com\/smartcontractkit\/chainlink\/blob\/develop\/contracts\/)\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/3ef3af93e37e4dd09acac6c815aca124.png)\r\n\r\n在此处我们一共要 import 两个合约：\r\n\r\n - chainlink\/contracts\/src\/v0.8\/interfaces\/VRFCoordinatorV2Interface.sol\r\n - chainlink\/contracts\/src\/v0.8\/VRFConsumerBaseV2.sol\r\n\r\n这些合约可以在这个项目的 src 查看，由于我们是 0.8 版本，所以之后查看将会在 v0.8 目录下查看，以下是对应不同版本的 合约：\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/0a821ac503864f7fbf810e8d9abfd893.png)\r\n我们选择 v0.8 版本：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/e81aae64f7d94b2bb3294597d737967f.png)\r\n\r\n我们往下拉，找到 VRF 相关的合约内容，在此 就是所需引入的 VRF 第一个合约，这个合约是需要咱们继承后重写接收预言机下所产生的随机数接口（之后将会详细讲解）：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/4278c6617e7d4214949c5028cf2c4b8f.png)\r\n\r\n接着还有一个合约在 interface 目录下：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/cd1304e4a6a84a42a3f41012678c36b8.png)\r\n此时需要进入到 interface 才能查看此合约代码：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/1cc72c2148f5481da1c7c89cca27d969.png)\r\n\r\n接下来所实现 VRF 的步骤为：\r\n\r\n 1. 创建一个类继承 VRFConsumerBaseV2 合约，重写方法\r\n 2. 引入 VRF 的 interface ，在继承的 VRFConsumerBaseV2 合约下使用 VRFCoordinatorV2Interface 申请随机数（对随机数进行获取）\r\n\r\n接着我们创建一个合约后，在合约中使用 import 引入这两个合约（示例引用 chainlink 官方示例）：\r\n\r\n```go\r\n\/\/ SPDX-License-Identifier: GPL-3.0\r\n\r\npragma solidity >=0.8.7;\r\n\r\nimport \"@chainlink\/contracts\/src\/v0.8\/interfaces\/VRFCoordinatorV2Interface.sol\";\r\nimport \"@chainlink\/contracts\/src\/v0.8\/VRFConsumerBaseV2.sol\";\r\n```\r\n\r\n注意，引入合约路径须在前面加一个“@”。\r\n\r\n接着创建一个合约名为 ChainLinkVRFDemo 继承自 VRFConsumerBaseV2：\r\n\r\n```go\r\ncontract ChainLinkVRFDemo is VRFConsumerBaseV2{\r\n\r\n}\r\n```\r\n\r\n接着编写一个构造方法，这个构造方法需要调用 父合约 构造函数，传入  VRF 的调用地址，在此我们可以查看 goerli 测试网络下的地址，查看链接为：[https:\/\/docs.chain.link\/docs\/vrf\/v2\/subscription\/supported-networks\/#avalanche-mainnet](https:\/\/docs.chain.link\/docs\/vrf\/v2\/subscription\/supported-networks\/#avalanche-mainnet)\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/206ed3b0ba6643798b31adfa61770849.png)\r\n此时构造函数代码如下：\r\n\r\n```go\r\naddress vrfCoordinatorAddr=0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;\/\/调用地址\r\nconstructor()VRFConsumerBaseV2(vrfCoordinatorAddr){\r\n}\r\n```\r\n由于接下来我们还需要使用对应的订阅ID，通过这个订阅ID 对预言机发送请求获取随机数，所以此时我们得需要设置这个订阅号，那么在构造函数时传入参数，记录订阅号ID。此时新建一个变量 subId，修改构造函数接收订阅 ID：\r\n\r\n```go\r\nuint64 subId;\/\/订阅号\r\nconstructor(uint64 _subId)VRFConsumerBaseV2(vrfCoordinatorAddr){\r\n    subId=_subId;\r\n}\r\n```\r\n由于我们在创建另一个 import 的合约 VRFCoordinatorV2Interface 获取随机数时也需要对应的知道 VRF 的调用地址，所以此时咱们直接在构造函数中对其对象进行创建，并设定地址：\r\n\r\n```go\r\nVRFCoordinatorV2Interface VRFInterface;\/\/当然要声明一个 VRFCoordinatorV2Interface 类型的变量，接着在构造函数中实例化即可\r\nconstructor(uint64 _subId)VRFConsumerBaseV2(vrfCoordinatorAddr){\r\n    VRFInterface=VRFCoordinatorV2Interface(vrfCoordinatorAddr);\r\n    subId=_subId;\r\n}\r\n```\r\n\r\n此时你若编译肯定会报错，那是因为你还未重写一个接收方法，这个方法叫做 fulfillRandomWords，咱们在 GitHub 的 VRFConsumerBaseV2.sol 源码中应该可以看得到：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/1a095a8f539d43d6870bcfe8f4a22340.png)\r\n这个方法需要重写，作用是当我们请求了随机数后，将会返回对应的随机数（也就是参数 randomWords），我们需要在这个方法中编写代码将这个随机数进行存储。可能看到这里你会疑惑，直接调用请求不就可以获取了？为什么还要这个方法？而且这个方法是 internal 修饰的，是内部的方法呀。\r\n\r\n在此我们并不讨论 chainlink VRF 随机数生成后返回的原力，咱们只需要知道在调用随机数请求后，chainlink 的节点开始生成随机数，并经过一些列的操作（投票随机数）最后得出最终结果，这个结果将通过调用咱们这个合约中的一个接口进行随机数的“返回”。\r\n\r\n可能你还会说，这个接口不是 internal ？外部怎么调用？\r\n其实最开始我也并不理解 VRF 这个接口的实现逻辑，在我查看他代码时发现，fulfillRandomWords 这个方法在 rawFulfillRandomWords 中进行了调用，你看下图：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/07e75bd15bb34b7d900b9481a1d2ba31.png)\r\n此时 rawFulfillRandomWords 方法是 external 进行修饰的，并且接收两个参数，其中我肯定的告诉你 randomWords 就是我们请求后的随机数。\r\n\r\n那么此时你应该明白了大体逻辑：在我们继承了 VRFConsumerBaseV2 合约后，并不需要重写 rawFulfillRandomWords 方法，只需要重写 fulfillRandomWords 方法，在其内部编写将 randomWords 存储到何处（哪个变量接收）即可，这样就得到了随机数。\r\n\r\n现在你应该明白了吧。那么接下来我们直接从 GitHub 中复制这个需要重写的 fulfillRandomWords，用一个变量接收 randomWords 即可：\r\n\r\n```go\r\n\/\/存储随机数\r\nuint256[] public s_randomWords;\r\n\/\/接收方法\r\nfunction fulfillRandomWords(uint256 requestID,uint256[] memory randomWords) internal override{\r\n    s_randomWords=randomWords;\r\n} \r\n```\r\n记得，一定要 把 virtual 改成了 override。\r\n\r\n接下来咱们开始编写请求方法吧，请求方法也超击简单，通过 另外一个 import 的合约 VRFCoordinatorV2Interface 的对象调用 requestRandomWords 方法，并且传入参数即可。\r\n\r\n我们可以在 Github 上的合约代码内看到这个接口：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/f258c2ec4dad4ccfbe1c1f7b3e76abcf.png)\r\n这个方法接收 5 个参数，分别是：\r\n\r\n - keyHash：一个hash值，不同的 hash 值表示你这个“生成随机数的快慢” \r\n - subId：订阅ID（已获得）\r\n -  requestConfirmations：多少个交易后表示成功，越小值越快 \r\n - callbackGasLimit：gas limit上限官方示例给出的值是 100000 \r\n - numWords：请求多少个随机数，上限是500\r\n\r\n这样一看是不是就觉得很简单了？\r\n\r\n其中的 keyHash 是有官方给出的，查看文档： [https:\/\/docs.chain.link\/docs\/vrf\/v2\/subscription\/supported-networks\/#avalanche-mainnet](https:\/\/docs.chain.link\/docs\/vrf\/v2\/subscription\/supported-networks\/#avalanche-mainnet)\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/ea28dcc35243402897563e414f88e2ba.png)\r\n\r\n上图中的 30 gwei key hash 就是这个 hash 值了，在这里由于是测试网只有一个，这个 hash 的作用就是给得越高，当网络繁忙时，你的“交易”会比其他的快，低一点慢 高一点快。\r\n\r\n你可以看到其他网络中的 gwei key hash 的值有不同的选择：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/079792fcf2994c31bd84d171ed3801ca.png)\r\n\r\n剩下的参数我们就知道怎么创建了，一个是 requestConfirmations 就给个 3，表示 3个确认后我们就认为交易success了、callbackGasLimit 默认给官方文档的 100000、numWords 请求随机数就请求 3 个即可；那么此时我们先创建一个方法叫做  requestRandomWords（必须这个名，至于为啥还在了解），并且使用在构造函数中所创建的 VRFInterface 对象对接口 requestRandomWords 进行调用，并且传入对应的参数：\r\n\r\n```go\r\n\/\/选择不同的 gwei  可以快和慢，低一点慢 高一点快\r\nbytes32 keyHash=0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;\r\n\/\/认为多少个交易以后交易就成功了，一般是12\r\nuint16 requestConfirmations=3;\r\n\/\/gas limit上限\r\nuint32 callbackGasLimit=100000;\r\n\/\/请求多少个随机数，上限是500\r\nuint32 numWords=3;\r\n\r\nfunction requestRandomWords() external{\r\n    VRFInterface.requestRandomWords(\r\n        keyHash,\r\n        subId,\r\n        requestConfirmations,\r\n        callbackGasLimit,\r\n        numWords\r\n    );\r\n}\r\n```\r\n\r\n是不是这样就ok了？不过我们需要，这个请求随机数的方法是一个交易，会存在花费，我们需要对应的为其设置只能合约者可以调用，那么添加 require：\r\n\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/7149da6e90ad4746b27fe2bca2ee7e3f.png)\r\n\r\n在这里还要提一嘴，这个 requestRandomWords 方法会返回一个值 requestID，表示你这个随机数是第几轮产生的，你也可以进行接收：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/4ca718f5c4ea4c0a803fff2512645ab5.png)\r\n\r\n整理一下后最终整个合约代码如下：\r\n\r\n```go\r\n\/\/ SPDX-License-Identifier: GPL-3.0\r\n\r\npragma solidity >=0.8.7;\r\n\r\nimport \"@chainlink\/contracts\/src\/v0.8\/interfaces\/VRFCoordinatorV2Interface.sol\";\r\nimport \"@chainlink\/contracts\/src\/v0.8\/VRFConsumerBaseV2.sol\";\r\n\r\ncontract ChainLinkVRFDemo is VRFConsumerBaseV2{\r\n    VRFCoordinatorV2Interface VRFInterface;\r\n    uint64 subId;\/\/订阅号\r\n    address owner;\r\n    address vrfCoordinatorAddr=0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;\r\n    \/\/选择不同的 gwei  可以快和慢，低一点慢 高一点快\r\n    bytes32 keyHash=0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;\r\n    \/\/认为多少个交易以后交易就成功了，一般是12\r\n    uint16 requestConfirmations=3;\r\n    \/\/gas limit上限\r\n    uint32 callbackGasLimit=100000;\r\n    \/\/请求多少个随机数，上限是500\r\n    uint32 numWords=3;\r\n    \/\/存储随机数\r\n    uint256[] public s_randomWords;\r\n    \/\/第几次请求的数据\r\n    uint256 public requestID;\r\n    \r\n    constructor(uint64 _subId)VRFConsumerBaseV2(vrfCoordinatorAddr){\r\n        VRFInterface=VRFCoordinatorV2Interface(vrfCoordinatorAddr);\r\n        subId=_subId;\r\n    }\r\n\r\n    \/\/接收方法\r\n    function fulfillRandomWords(uint256 requestID,uint256[] memory randomWords) internal override{\r\n        s_randomWords=randomWords;\r\n    }\r\n  \r\n    function requestRandomWords() external{\r\n        require(msg.sender==owner);\r\n        requestID=VRFInterface.requestRandomWords(\r\n            keyHash,\r\n            subId,\r\n            requestConfirmations,\r\n            callbackGasLimit,\r\n            numWords\r\n        );\r\n    }\r\n}\r\n```\r\n我们开始部署合约，选择 Metamask 的网络，传入之前我生成的那个订阅ID号（请忽略一下地址不同，因为之前敲错代码了后又重新贴图了一次）：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/a76c300107cc4a7783b27de74adf7ab2.png)\r\n\r\n点击部署后将会有一个交易产生：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/3fd01656711d4eaa87bd806b15ccfe48.png)\r\n确定支付后可以对其查看：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/471ba7f3f6b643bba06415d1aa22074a.png)\r\n\r\n回到之前订阅成功的页面，点击 添加合约地址：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/3ce2915eb8c34b5eba0103218548faa5.png)\r\n复制自己的合约地址：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/f4e34fa2617c483e85ddea5bcaca9d4e.png)\r\n\r\n\r\n添加合约：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/50e095dbb5f14ee4a3e45e6444e83fda.png)\r\n\r\n接着确认小狐狸的交易：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/2e1355ff874f42959454d5d8a720ac00.png)\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/a6fabe54abba48079322fd501b9b8edc.png)\r\n\r\n接着进行等待交易完成（你的可能和我的不一样）。\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/cefd18e8106d453cadac642a0f83d60e.png)\r\n\r\n接着点击请求随机数方法：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/b1e4e2ee774d40398e551ea60407dd19.png)\r\n\r\n\r\n接着会弹出交易提示，我们点击确认：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/13b2bc074132442d8566693a1538ddf8.png)\r\n\r\n接着我们可以看到一个随机数的请求：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/7a560cce199245a587feed77795b6e2b.png)\r\n等 Pending 编程 success 即可完成随机数获取。\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/6e65af50af9f434c914c631c2f63935f.png)\r\n\r\nsuccess 之后，点击合约中的存储状态变量，输入索引获取随机数，由于我们设置的是3个随机数，所以索引为0、1、2：\r\n![在这里插入图片描述](https:\/\/img-blog.csdnimg.cn\/da9d3991c59b494bb2aeb770e1b0b92e.png)"},"author":{"user":"https:\/\/learnblockchain.cn\/people\/11898","address":null},"history":"QmNoA6k5ubhfotXYSE64QqRvjW7yP1DbxB3KKZBXnmC3eb","timestamp":1665476760,"version":1}