部署彩票合约无法添加为Chainlink VRF V2订阅者及Mock使用咨询
订阅添加失败的核心原因
Chainlink VRF V2的addConsumer方法要求调用者必须是订阅的拥有者或者被订阅授权的地址。你当前合约中的addSubscription函数是让合约自身作为调用者去调用COORDINATOR.addConsumer,但合约地址并没有被赋予订阅的管理权限,所以调用会失败。
修复方案推荐直接用订阅管理员钱包操作:部署完成后,用管理员钱包直接调用Goerli测试网的VRF Coordinator合约(地址0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D)的addConsumer方法,参数传入你的订阅ID和彩票合约地址即可,无需依赖合约内的addSubscription函数。
合约关键问题修复
除订阅问题外,合约还有几个功能失效的隐患:
keyHash未初始化
Goerli测试网的Chainlink VRF V2keyHash为0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15,需在构造函数中赋值:
constructor(address _priceFeed, uint64 _subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { priceFeed = AggregatorV3Interface(_priceFeed); COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); owner = msg.sender; usdEntryFee = 50 * (10**18); subscriptionId = _subscriptionId; keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15; // 初始化Goerli的keyHash }
- 异步随机数逻辑错误
Chainlink VRF是异步流程,调用requestRandomWords后需等待fulfillRandomWords回调完成才能获取随机数,不能在endLottery中直接使用randomWords[0]。调整逻辑:
- 在
endLottery中仅发起随机数请求,标记开奖状态 - 在
fulfillRandomWords回调中完成中奖者计算
bool public isLotteryEnded; // 添加状态标记 function endLottery() public onlyOwner { require(block.timestamp > endTime, "Auction ending date not arrived yet!"); require(!isLotteryEnded, "Lottery already ended"); available = false; isLotteryEnded = true; requestRandomWords(); // 仅发起请求 } function fulfillRandomWords( uint256, // requestId uint256[] memory _randomWords ) internal override { randomWords = _randomWords; uint256 randomIndex = randomWords[0] % buyers.length; winner = buyers[randomIndex]; }
- 入口费用与奖池逻辑缺失
enterLottery需验证支付金额,并累加奖池:
function enterLottery() public payable returns (bool success) { require(msg.value >= getEntranceFee(), "Insufficient ETH to enter"); require(available, "Lottery is not active"); buyers.push(msg.sender); prizePool += msg.value; return true; }
Brownie测试调整
测试中需适配VRF的异步特性,本地测试用Mock模拟流程:
from brownie import Lottery, accounts, config, network, VRFCoordinatorV2Mock from scripts.helpful_scripts import get_account, LOCAL_BLOCKCHAIN_ENVIRONMENTS import pytest def test_random_number(): if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS: pytest.skip("Only for local testing") account = get_account() # 部署VRF Mock mock_vrf = VRFCoordinatorV2Mock.deploy(0, 0, {"from": account}) subscription_id = mock_vrf.createSubscription()["subscriptionId"] mock_vrf.fundSubscription(subscription_id, 10**18, {"from": account}) # 部署彩票合约 lottery = Lottery.deploy( config["networks"][network.show_active()]["eth_usd_price_feed"], subscription_id, {"from": account} ) # 添加合约为消费者 mock_vrf.addConsumer(subscription_id, lottery.address, {"from": account}) # 发起随机数请求并模拟回调 tx = lottery.requestRandomWords({"from": account}) mock_vrf.fulfillRandomWords(tx.events["RequestedRandomness"]["requestId"], lottery.address, {"from": account}) assert lottery.randomWords[0] != 0
Chainlink VRF V2 Mock使用方法
本地测试(如Ganache)时,用VRFCoordinatorV2Mock模拟完整流程:
- 部署Mock合约:构造函数传入
baseFee和gasPriceLink,测试时可设为0 - 创建订阅:调用
createSubscription()获取订阅ID - 充值订阅:调用
fundSubscription(subscriptionId, amount),测试时充任意ETH即可 - 添加消费者:调用
addConsumer(subscriptionId, consumerAddress),将你的合约地址加入 - 发起随机数请求:调用合约的
requestRandomWords - 模拟回调:调用Mock的
fulfillRandomWords(requestId, consumerAddress),触发合约的fulfillRandomWords完成随机数赋值
内容的提问来源于stack exchange,提问作者user6942041
相关产品推荐
相关产品推荐

