You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署彩票合约无法添加为Chainlink VRF V2订阅者及Mock使用咨询

订阅添加失败的核心原因

Chainlink VRF V2的addConsumer方法要求调用者必须是订阅的拥有者或者被订阅授权的地址。你当前合约中的addSubscription函数是让合约自身作为调用者去调用COORDINATOR.addConsumer,但合约地址并没有被赋予订阅的管理权限,所以调用会失败。

修复方案推荐直接用订阅管理员钱包操作:部署完成后,用管理员钱包直接调用Goerli测试网的VRF Coordinator合约(地址0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D)的addConsumer方法,参数传入你的订阅ID和彩票合约地址即可,无需依赖合约内的addSubscription函数。


合约关键问题修复

除订阅问题外,合约还有几个功能失效的隐患:

  1. keyHash未初始化
    Goerli测试网的Chainlink VRF V2 keyHash为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
}
  1. 异步随机数逻辑错误
    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];
}
  1. 入口费用与奖池逻辑缺失
    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

本地测试(如Ganache)时,用VRFCoordinatorV2Mock模拟完整流程:

  1. 部署Mock合约:构造函数传入baseFee和gasPriceLink,测试时可设为0
  2. 创建订阅:调用createSubscription()获取订阅ID
  3. 充值订阅:调用fundSubscription(subscriptionId, amount),测试时充任意ETH即可
  4. 添加消费者:调用addConsumer(subscriptionId, consumerAddress),将你的合约地址加入
  5. 发起随机数请求:调用合约的requestRandomWords
  6. 模拟回调:调用Mock的fulfillRandomWords(requestId, consumerAddress),触发合约的fulfillRandomWords完成随机数赋值

内容的提问来源于stack exchange,提问作者user6942041

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 00:05:23