利用ChainlinkVRF实现100Token抽奖:从名单中随机选出幸运得主的完整指南在区块链应用中,公平和不可预测的随机性是实现透明抽奖和激励机制的关键。ChainlinkVRF(可验证随机函数)为智能合约提供了一个可验证且公正的随机数生成方案,使得合约能够在不牺牲安全性的前提下进行随
在区块链应用中,公平和不可预测的随机性是实现透明抽奖和激励机制的关键。Chainlink VRF(可验证随机函数)为智能合约提供了一个可验证且公正的随机数生成方案,使得合约能够在不牺牲安全性的前提下进行随机值的生成和验证。
本文将展示如何利用 Chainlink VRF 实现一个简单的抽奖合约,该合约从预设的抽奖名单中随机选出一名幸运地址,并为其颁发100 Token。我们将详细介绍 Chainlink VRF 的工作原理、相关函数和实现步骤。
Chainlink VRF (Verifiable Random Function) is a provably fair and verifiable random number generator (RNG) that enables smart contracts to access random values without compromising security or usability. For each request, Chainlink VRF generates one or more random values and cryptographic proof of how those values were determined. The proof is published and verified onchain before any consuming applications can use it. This process ensures that results cannot be tampered with or manipulated by any single entity including oracle operators, miners, users, or smart contract developers.
Chainlink VRF(可验证随机函数) 是一种可证明公平且可验证的随机数生成器(RNG),它使智能合约能够在不影响安全性或可用性的情况下访问随机值。对于每个请求, Chainlink VRF 生成一个或多个随机值以及如何确定这些值的加密证明。在任何 consumer 应用程序可以使用该证明之前,该证明将在链上发布和验证。此过程确保结果不会被任何单个实体篡改或操纵,包括预言机运营商、矿工、用户或智能合约开发者。
使用Chainlink VRF来建立可靠的智能合约,用于任何依赖不可预测结果的应用:
Similarly to VRF v2, VRF v2.5 will offer two methods for requesting randomness:
Chainlink VRF (Verifiable Random Function) 。生成的随机数是密码学中的伪随机数。
产品具有以下特点:
证明的作用:在数学上可证明得到的随机数是不可预测的
uint private _counter = 0;
function getRandomWithTen() external returns (uint) {
++_counter;
return uint(keccak256(abi.encode(
blockhash(1),
gasleft(),
block.number,
_counter
))) % 10;
}
存证哪些问题?
易受矿工操控。
随机性依赖于可预测的区块链参数。
大家信任一个地址,这个地址可以是一个合约,也可以是一个EOA地址。信誉保证我取得的每一个随机数都是链下取得真实的随机数。取到之后每隔半个小时、每隔十分钟发送到链上,如有需要直接去读取链上某个合约的地址即可。也可以多个人发送取平均值等等
存证的问题
如果三个地址都是一个人发送
依赖可信源生成随机数并定期将其发布到链上。
多个源由单一实体控制时可能会出现问题。
我们将展示如何基于 Chainlink VRF 构建一个简单的抽奖合约。该合约将从提供的抽奖名单中随机选择一名地址,并奖励其100 Token。
forge install smartcontractkit/chainlink --no-commit
QiaoToken
合约代码// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
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";
import {IVRFCoordinatorV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/interfaces/IVRFCoordinatorV2Plus.sol";
contract QiaoToken is ERC20, ERC20Permit, VRFConsumerBaseV2Plus {
uint256 private constant ROLL_IN_PROGRESS = 42;
address[] list;
uint256 s_subscriptionId;
address vrfCoordinator = 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B;
bytes32 s_keyHash = 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae;
uint32 callbackGasLimit = 2_500_000;
uint16 requestConfirmations = 3;
uint32 numWords = 1;
mapping(uint256 => address) private s_rollers;
mapping(address => uint256) private s_results;
// events
event DiceRolled(uint256 indexed requestId, address indexed roller);
event DiceLanded(uint256 indexed requestId, uint256 indexed result);
constructor(uint256 subscriptionId)
ERC20("QiaoToken", "QTK")
ERC20Permit("QiaoToken")
VRFConsumerBaseV2Plus(vrfCoordinator)
{
s_subscriptionId = subscriptionId;
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
// rollDice function
function rollDice(address roller, address[] memory newLists) public onlyOwner returns (uint256 requestId) {
require(s_results[roller] == 0, "Already rolled");
// Will revert if subscription is not set and funded.
list = newLists;
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: s_keyHash,
subId: s_subscriptionId,
requestConfirmations: requestConfirmations,
callbackGasLimit: callbackGasLimit,
numWords: numWords,
// Set nativePayment to true to pay for VRF requests with Sepolia ETH instead of LINK
extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false}))
})
);
s_rollers[requestId] = roller;
s_results[roller] = ROLL_IN_PROGRESS;
emit DiceRolled(requestId, roller);
}
// fulfillRandomWords function
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
uint256 random = randomWords[0] % list.length;
_mint(list[random], 100 * 10 ** decimals());
// assign the transformed value to the address in the s_results mapping variable
s_results[s_rollers[requestId]] = random;
// emitting event to signal that dice landed
emit DiceLanded(requestId, random);
}
}
这段Solidity代码实现了一个名为QiaoToken的ERC20代币合约,它继承了OpenZeppelin的ERC20、ERC20Permit和VRFConsumerBaseV2Plus合约。这个合约的主要功能是允许合约所有者铸造代币,并允许用户掷骰子来获得代币。
导入所需的库和接口:
定义合约及构造函数:
定义合约的变量:
定义合约的事件:
实现合约的方法:
注意:这个合约使用了Chainlink VRF服务来生成随机数,需要预先设置VRF订阅并支付相应的费用。
<https://docs.chain.link/vrf/v2-5/supported-networks#sepolia-testnet>
<https://vrf.chain.link/sepolia>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script, console} from "forge-std/Script.sol";
import {QiaoToken} from "../src/QiaoToken.sol";
contract QiaoTokenScript is Script {
QiaoToken public qiaotoken;
function setUp() public {}
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
uint256 subscriptionId = vm.envUint("SUBSCRIPTION_ID");
vm.startBroadcast(deployerPrivateKey);
qiaotoken = new QiaoToken(subscriptionId);
console.log("QiaoToken deployed to:", address(qiaotoken));
vm.stopBroadcast();
}
}
DynamicNFT on main [!+?] via 🅒 base
➜ source .env
DynamicNFT on main [!+?] via 🅒 base
➜ forge script --chain sepolia QiaoTokenScript --rpc-url $SEPOLIA_RPC_URL --broadcast --verify -vvvv
[⠊] Compiling...
[⠒] Compiling 2 files with Solc 0.8.20
[⠑] Solc 0.8.20 finished in 1.50s
Compiler run successful!
Traces:
[1555073] QiaoTokenScript::run()
├─ [0] VM::envUint("PRIVATE_KEY") [staticcall]
│ └─ ← [Return] <env var value>
├─ [0] VM::envUint("SUBSCRIPTION_ID") [staticcall]
│ └─ ← [Return] <env var value>
├─ [0] VM::startBroadcast(<pk>)
│ └─ ← [Return]
├─ [1508576] → new QiaoToken@0xC668D79A54694C4AA212dE50178A7c3b265b6373
│ └─ ← [Return] 6638 bytes of code
├─ [0] console::log("QiaoToken deployed to:", QiaoToken: [0xC668D79A54694C4AA212dE50178A7c3b265b6373]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::stopBroadcast()
│ └─ ← [Return]
└─ ← [Stop]
Script ran successfully.
== Logs ==
QiaoToken deployed to: 0xC668D79A54694C4AA212dE50178A7c3b265b6373
## Setting up 1 EVM.
==========================
Simulated On-chain Traces:
[1508576] → new QiaoToken@0xC668D79A54694C4AA212dE50178A7c3b265b6373
└─ ← [Return] 6638 bytes of code
==========================
Chain 11155111
Estimated gas price: 4.309264446 gwei
Estimated total gas used for script: 2195060
Estimated amount required: 0.00945909401483676 ETH
==========================
##### sepolia
✅ [Success]Hash: 0x9e67b83b715dfac3d2a2a7f550e643656e580744b06a5a6fc6aa049093c909a0
Contract Address: 0xC668D79A54694C4AA212dE50178A7c3b265b6373
Block: 6470414
Paid: 0.004397538979531588 ETH (1689028 gas * 2.603591521 gwei)
✅ Sequence #1 on sepolia | Total Paid: 0.004397538979531588 ETH (1689028 gas * avg 2.603591521 gwei)
==========================
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
##
Start verification for (1) contracts
Start verifying contract `0xC668D79A54694C4AA212dE50178A7c3b265b6373` deployed on sepolia
Submitting verification for [src/QiaoToken.sol:QiaoToken] 0xC668D79A54694C4AA212dE50178A7c3b265b6373.
Submitted contract for verification:
Response: `OK`
GUID: `7waicaa49l6cgbaa91qp76itveixtvnfarsnegnztweig9u554`
URL: https://sepolia.etherscan.io/address/0xc668d79a54694c4aa212de50178a7c3b265b6373
Contract verification status:
Response: `NOTOK`
Details: `Pending in queue`
Contract verification status:
Response: `OK`
Details: `Pass - Verified`
Contract successfully verified
All (1) contracts were verified!
Transactions saved to: /Users/qiaopengjun/Code/solidity-code/DynamicNFT/broadcast/QiaoToken.s.sol/11155111/run-latest.json
Sensitive values saved to: /Users/qiaopengjun/Code/solidity-code/DynamicNFT/cache/QiaoToken.s.sol/11155111/run-latest.json
DynamicNFT on main [!+?] via 🅒 base took 1m 1.4s
➜
<https://sepolia.etherscan.io/address/0xc668d79a54694c4aa212de50178a7c3b265b6373#code>
<https://sepolia.etherscan.io/tx/0xbfb07f08edb17ce6a79a49d162b9ea4217c8f2458c29bbd318c3f2bdefe5bd45>
newLists 为三个地址
<https://sepolia.etherscan.io/address/0xc668d79a54694c4aa212de50178a7c3b265b6373#writeContract>
<https://sepolia.etherscan.io/tx/0x6ced5cf21944ed315c5a06c4c34c4429452949977de72ef15e80eb93da844b1a>
<https://sepolia.etherscan.io/tx/0xebf4f77bb14e11c61d82a4c3cfc6d957415ee785574e6a9a9cb5c75b2d32ab88>
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!