?
我使用的版本:chainlink-brownie-contracts version:1.3.0
?
1. Import 相關包
,,,
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2PLUS.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
,,,
?
2. 要使用的VRF功能的 contract 繼承 VRFConsumerBaseV2Plus
,,,
contract EnterDungeon is VRFConsumerBaseV2Plus{}
,,,
?
3. 在contract中 聲明下列VRF狀態變量
,,,
// Chainlink VRF Variables
uint256 private immutable i_subscriptionId;
bytes32 private immutable i_gasLane; // keyHash
uint32 private immutable i_callbackGasLimit;
uint16 private constant REQUEST_CONFIRMATIONS = 3;
uint32 private constant NUM_WORDS = 1;
bool private s_enableNativePayment; // true:ETH, false:LINK
,,,
?
4. 重構你的 contract 的 constructor
,,,
constructor(address _vrfCoordinator, uint256 _subscriptionId, bytes32 _gasLane, uint32 _callbackGasLimit) VRFConsumerBaseV2Plus(_vrfCoordinator) {
i_subscriptionId = _subscriptionId;
i_gasLane = _gasLane;
i_callbackGasLimit = _callbackGasLimit;
}
,,,
?
5. 實現 requestRandomNumber(),用來封裝隨機數請求的邏輯
,,,
/**
* @notice requestRandomNumber
* @dev 自己定義的一個函數,可以叫做任何名字,只是單獨封裝了VRF請求隨機數的邏輯
* @return requestId
*/
function requestRandomNumber() public returns (uint256 requestId) {
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: i_gasLane,
subId: i_subscriptionId,
requestConfirmations: REQUEST_CONFIRMATIONS,
callbackGasLimit: i_callbackGasLimit,
numWords: NUM_WORDS,
extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: s_enableNativePayment}))
})
);
}
,,,
?
6. 重寫 fulfillRandomWords() 里面寫你具體的業務邏輯
,,,
/**
* @notice fulfillRandomWords
* @dev 當VRF請求隨機數成功后,會調用這個函數,里面是拿到隨機數后的具體處理邏輯
* ? ? ?調用者是 VRF Coordinator,要使用requestId映射playerAddress
* @param ?requestId 請求ID
* @param randomWords 隨機數
*/
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
// TODO:
}
,,,
?
7. 本地 Mock 測試
7.1 導入 Mock 合約
,,,
import {VRFCoordinatorV2_5Mock} from "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";
,,,
7.2 部署 Mock 合約
,,,
// 1. deploy vrfCoordinatorV2_5Mock
vrfCoordinatorV2_5Mock = new VRFCoordinatorV2_5Mock(
MOCK_BASE_FEE,
MOCK_GAS_PRICE_LINK,
MOCK_WEI_PER_UINT_LINK
);
? ? ? ? // 2. create subscription
uint256 subscriptionId = vrfCoordinatorV2_5Mock.createSubscription();
? ? ? ? // 3. fund subscription
vrfCoordinatorV2_5Mock.fundSubscription(subscriptionId, FUND_LINK_AMOUNT);?
// FUND_LINK_AMOUNT = 100000000000000000000 = 100 LINK
? ? ? ? // 4. add consumer
vrfCoordinatorV2_5Mock.addConsumer(chainConfig.subscriptionId, address(consumer_addr));
,,,
7.3 常見報錯 & 解決
報錯信息?? ?原因?? ?解決辦法
Arithmetic underflow in createSubscription()?? ?mock 里用 blockhash(block.number - 1),而本地鏈把前一區塊 hash 置 0?? ?把內部實現改成 blockhash(block.number)(已在下方補丁)
錯誤信息:
VRFCoordinatorV2_5Mock::createSubscription()
│ └─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
└─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
補丁示例:
// SubscriptionApi.sol 片段
subId = uint256(
keccak256(
abi.encodePacked(
msg.sender,
blockhash(block.number), // ← 修改
address(this),
s_currentSubNonce
)
)
);
?
🔗 線上部署別忘記
在 [Chainlink-VRF-UI](https://vrf.chain.link/) 手動 Add Consumer,否則主網請求將被拒絕。
?