如何在 Berachain 上使用 Pyth Entropy 进行公平的随机 NFT 铸造 |开发者必备

本文介绍了如何在 Berachain 上使用 Pyth Entropy 生成可验证的随机数,确保 NFT 铸造的公平性和透明度。通过详细的步骤指导,开发者能够在区块链上创建完全随机且可证明公平的 NFT 铸造流程,提升用户信任和项目的可信度。

导言

在区块链生态系统中,生成可验证的随机性是许多应用(例如 NFT 铸造、彩票和去中心化游戏)公平性和透明度的核心。然而,由于区块链协议的确定性,生成真正的随机性是一项挑战。为解决这个问题,Pyth Entropy 提供了一种安全的、可验证的随机数生成机制,适用于智能合约

本文由,介绍了如何利用 Pyth Entropy 在 Berachain 上构建公平铸造的 NFT 系列,确保每个 NFT 的分配是完全随机且可验证的,从而消除铸造过程中的任何偏见或操控风险。通过本指南,开发者可以轻松实现基于区块链的公平铸造流程,增强用户信任并提升项目的透明度。

Erica


如何在 Berachain 上使用 Pyth Entropy 进行公平的随机 NFT 铸造 |开发者必备

使用 Pyth 构建公平铸造的 NFT 系列

Pyth Entropy 允许开发人员轻松生成区块链上的安全随机数。

可验证的随机性在 NFT 中有很有用的应用。如果通过内部消息或侦探手段提前知道稀有物品的位置,可能会影响铸造的公平性。解决这个问题的有效方法是完全随机地分配铸造位置。由于大多数区块链协议的确定性,本地获得真正的随机性很困难。

在本文中,我们将向您展示如何利用 Pyth Entropy 来创建对用户而言可证明公平的 NFT 铸造。


📋 要求

在继续之前,请确保您的计算机已安装并设置以下内容:

Foundry

本文需要安装 Foundry。在终端窗口中运行:

curl -L https://foundry.paradigm.xyz | bash;

foundryup;

# foundryup installs the 'forge' and 'cast' binaries, used later

在你的电脑安装 Foundry

有关更多安装说明,请参阅 Foundry 的安装指南。有关在 Berachain 上使用 Foundry 的详细信息,请参阅本指南

创建我们的 Pyth Entropy 项目

首先,我们将使用 Foundry 设置您的开发环境。

首先创建一个新的项目文件夹并初始化 Foundry:

forge init pyth-entropy --no-git --no-commit;

cd pyth-entropy;

# We observe the following basic layout
# .
# ├── foundry.toml
# ├── script
# │   └── Counter.s.sol
# ├── src
# │   └── Counter.sol
# └── test
#     └── Counter.t.sol

Initial Forge Project Template

初始 Forge 项目模板

安装依赖项

我们将使用多个依赖项,因此在下方安装它们:

# FROM: ./pyth-entropy

pnpm init;
pnpm add web3 dotenv @pythnetwork/entropy-sdk-solidity @openzeppelin/contracts --ignore-workspace; 

# web3 - interact with EVM blockchains with JavaScript
# dotenv - manage environment variables
# @pythnetwork/entropy-sdk-solidity - Pyth Entropy interfaces 
# @openzeppelin/contracts - audited smart contract libraries

安装合约依赖项和 SDKs

Forge 可以重新映射依赖项,使导入更加易读。因此,让我们重新映射我们的 Solidity 导入:

# FROM: ./pyth-entropy

echo "remappings = ['@pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity', '@openzeppelin/contracts/=node_modules/@openzeppelin/contracts']" >> ./foundry.toml

Foundry Toml 文件用于重映射

编写 NFT 铸造合约

现在我们准备进入令人兴奋的部分:使用 Pyth 在 Solidity 智能合约中生成可验证的随机数。

创建一个新文件 ./src/EntropyNFT.sol 并粘贴以下代码:

pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";

contract EntropyNFT is ERC721Enumerable, IEntropyConsumer {
    event NumberRequested(uint64 sequenceNumber, address minter);
    event Minted(uint64 sequenceNumber, address minter, uint256 tokenId);

    IEntropy entropy;
    address provider;
    uint256 public constant MAX_SUPPLY = 500;
    uint256 public nextIndex;
    uint256[] private availableTokenIds;

    // Mapping of sequence numbers to minter addresses
    mapping(uint64 => address) public sequenceNumberToMinter;

    constructor(
        address _entropy,
        address _provider
    ) ERC721("EntropyNFT", "eNFT") {
        entropy = IEntropy(_entropy);
        provider = _provider;
        initializeAvailableTokenIds();
    }

    // Step 1 of 2: Request a new random number for minting
    // Returns sequence number used to obtain random number from Pyth
    function requestMint(bytes32 userRandomNumber) external payable {
        require(nextIndex < MAX_SUPPLY, "Reached max supply");

        uint128 requestFee = entropy.getFee(provider);
        require(msg.value >= requestFee, "not enough fees");

        uint64 sequenceNumber = entropy.requestWithCallback{value: requestFee}(
            provider,
            userRandomNumber
        );

        sequenceNumberToMinter[sequenceNumber] = msg.sender;

        emit NumberRequested(sequenceNumber, msg.sender);
    }

    // Step 2 of 2: Fulfill mint request on Pyth callback
    function entropyCallback(
        uint64 sequenceNumber,
        address,
        bytes32 randomNumber
    ) internal override {
        address minter = sequenceNumberToMinter[sequenceNumber];
        uint256 randomIndex = uint256(randomNumber) % availableTokenIds.length;
        uint256 tokenId = availableTokenIds[randomIndex];

        // Swap-and-pop to replace minted tokenId
        availableTokenIds[randomIndex] = availableTokenIds[
            availableTokenIds.length - 1
        ];
        availableTokenIds.pop();
        nextIndex++;

        _safeMint(minter, tokenId);
        emit Minted(sequenceNumber, minter, tokenId);
    }

    // Initialize array of available token IDs
    function initializeAvailableTokenIds() private {
        for (uint256 i = 0; i < MAX_SUPPLY; i++) {
            availableTokenIds.push(i);
        }
    }

    // This method is required by the IEntropyConsumer interface
    function getEntropy() internal view override returns (address) {
        return address(entropy);
    }
}

现在让我们分解这个合约:

  1. EntropyNFT 合约继承自 ERC721Enumerable,提供了标准的 NFT 功能。我们的合约还继承了 IEntropyConsumer,当随机数请求完成后实现回调。
  2. IEntropy.sol 提供了与 Pyth Entropy 合约交互的接口。构造函数参数 _entropy 将已部署的 Entropy 合约连接到该接口。
  3. 合约定义了 500 个 NFT 的最大供应量,并从 [0…499] 初始化了一个数组 availableTokenIds
  4. requestMint 函数允许用户通过提供用户承诺(见下文)并支付所需费用从 Entropy 请求随机数。返回的 sequenceNumber 索引调用者有权铸造与该 sequenceNumber 对应的随机数生成的 NFT。
  5. entropyCallback 函数在去中心化的 keeper 机器人完成随机数请求时调用,最终铸造出具有唯一 tokenId 的 NFT。通过将随机数对剩余 NFT 数量取模,确保随机索引在 availableTokenIds 数组范围内。使用过的 tokenId 将被替换为数组中的最后一个元素,以保持可用 tokenId 的列表。

您可以删除项目生成的 src/Counter.soltest/Counter.t.solscript/Counter.s.sol

部署准备

在项目根目录下创建一个 .env 文件,并填入以下内容:

RPC_URL=https://artio.rpc.berachain.com/
ENTROPY_NFT_ADDRESS=YOUR_ENTROPY_NFT_ADDRESS
PRIVATE_KEY=YOUR_PRIVATE_KEY
ENTROPY_ADDRESS=0x26DD80569a8B23768A1d80869Ed7339e07595E85
PROVIDER_ADDRESS=0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344

填入您的部署者钱包 PRIVATE_KEY,然后将这些环境变量加载到终端会话中:

# FROM: ./pyth-entropy

source .env;

部署到 Berachain 测试网

首先,编译智能合约:

# FROM: ./pyth-entropy

forge build;

您会注意到多个构建输出出现在 ./out 目录中。我们将使用 forge create 命令将我们的新合约部署到 Berachain 测试网(在此处阅读更多有关该命令的信息):

# FROM: ./pyth-entropy

forge create src/EntropyNFT.sol:EntropyNFT  \
--private-key $PRIVATE_KEY \
--rpc-url $RPC_URL \
--constructor-args $ENTROPY_ADDRESS $PROVIDER_ADDRESS;

# [Example output]
# Deployer: <YOUR_WALLET_ADDRESS>
# Deployed to: <YOUR_DEPLOYED_CONTRACT>
# Transaction hash: <CONTRACT_DEPLOYMENT_TX_HASH>

您需要拥有 $BERA 以支付部署费用。可以通过以下网址获取水龙头资金:https://artio.faucet.berachain.com/。

铸造您的随机 NFT

现在让我们使用 JavaScript 与我们的新 NFT 合约进行交互。创建一个新文件夹来存放我们的脚本(我们将有两个脚本):

# FROM ./pyth-entropy

mkdir app;
cd app;
touch requestMint.js;

In ./app/requestMint.js paste in the following code:

const { Web3 } = require("web3");
const EntropyNFTAbi = require("../out/EntropyNFT.sol/EntropyNFT.json");
const EntropyAbi = require("@pythnetwork/entropy-sdk-solidity/abis/IEntropy.json");
require("dotenv").config({ path: "../.env" });

async function requestMint() {
  // Step 1: initialize wallet & web3 contracts
  const web3 = new Web3(process.env["RPC_URL"]);
  const { address } = web3.eth.accounts.wallet.add(
    process.env["PRIVATE_KEY"]
  )[0];

  const entropyNFTContract = new web3.eth.Contract(
    EntropyNFTAbi.abi,
    process.env["ENTROPY_NFT_ADDRESS"]
  );

  const entropyContract = new web3.eth.Contract(
    EntropyAbi,
    process.env["ENTROPY_ADDRESS"]
  );

  // Step 2: generate user random number, request mint
  console.log("Generating user random number and commitment...");
  const userRandomNumber = web3.utils.randomHex(32);
  console.log(`User Random Number: ${userRandomNumber}`);

  console.log("Fetching request fee...");
  const fee = await entropyContract.methods
    .getFee(process.env["PROVIDER_ADDRESS"])
    .call();
  console.log(`Request Fee: ${fee}`);

  console.log("Requesting NFT mint...");
  const requestReceipt = await entropyNFTContract.methods
    .requestMint(userRandomNumber)
    .send({ value: fee, from: address });
  console.log(`Request Transaction Hash: ${requestReceipt.transactionHash}`);

  const sequenceNumber =
    requestReceipt.events.NumberRequested.returnValues.sequenceNumber;
  console.log(`Sequence Number: ${sequenceNumber}`);

  // Step 3: Poll for new Minted events emitted by EntropyNFT
  // Stops polling when same sequenceNumber is fulfilled 
  const intervalId = setInterval(async () => {
    currentBlock = await web3.eth.getBlockNumber();

    const events = await entropyNFTContract.getPastEvents("Minted", {
      fromBlock: currentBlock - 5n,
      toBlock: currentBlock,
    });
    
    // Find the event with the same sequence number as the request.
    const event = events.find(
      (event) => event.returnValues.sequenceNumber === sequenceNumber
    );
    
    // If the event is found, log the result and stop polling.
    if (event !== undefined) {
      const values = events[0].returnValues
      console.log(`✅ NFT ID ${values.tokenId} minted to ${values.minter}, based on sequenceNumber ${values.sequenceNumber}`)
      clearInterval(intervalId);
    }
  }, 2000);

}

requestMint();

现在运行:

# FROM: ./pyth-entropy/app

node requestMint.js;

# [Expected Output]:
# Generating user random number and commitment...
# User Random Number: <0xUSER_RANDOM_NUMBER>
# Fetching request fee...
# Request Fee: 101
# Requesting NFT mint...
# Request Transaction Hash: <0xTX_HASH>
# Sequence Number: 116
# ✅ NFT ID 168 minted to <YOUR_WALLET_ADDRESS>, based on sequenceNumber 116

requestMint 脚本执行两个主要步骤:

步骤 1:我们首先初始化用户钱包和用于与 NFT 及 Entropy 合约交互的 web3 合约。

步骤 2:生成一个随机数并将其与所需费用一起传递给 EntropyNFT 的 requestMint 函数。我们从 Entropy 合约中收到一个 sequenceNumber 作为返回值。

步骤 3:在提交请求后,自动化的 Pyth 服务将生成一个随机数,并执行我们 NFT 合约中的 entropyCallback 函数。脚本的这一部分监控合约中的 Minted 事件,表明 Entropy 请求已完成,且基于随机数为您的钱包铸造了一个可验证的随机 NFT。

恭喜! 您已成功利用 Pyth 的 Entropy 服务提供了一个可证明公平的 NFT 铸造流程 🎉

Entropy 回顾 🔮

Pyth Entropy 扩展了经典的提交/揭示机制以生成随机数。首先,两个参与方分别提交一个秘密值(生成的随机数)。在揭示阶段,基于两个随机数的哈希生成最终的随机数。

在我们的 NFT 合约中,随机数生成流程如下,重点放在不同的参与方上:

  • 用户在 NFT 合约的 requestMint 调用中提交一个随机数。
  • 一个链下服务(机器人)生成第二个随机数。
  • 机器人通过 revealWithCallBack 调用将这两个随机数发送到 Pyth 的 Entropy 合约。这最终调用我们的 NFT 合约中的 entropyCallback 函数,将随机化的 NFT 铸造给用户。

注意:从用户的角度来看,他们最初的请求随机数交易(requestMint)不会直接铸造 NFT。相反,用户将在由机器人发起的后续调用中收到 NFT。

此过程中有很多步骤,以下是一个流程图帮助解释这些交互:

如何在 Berachain 上使用 Pyth Entropy 进行公平的随机 NFT 铸造 |开发者必备

Pyth Entropy Sequence Diagram

Pyth 的文档中了解 Entropy 设计的详细信息。

🐻 完整代码库

如果您想查看最终代码并查看其他指南,请查阅 Berachain Pyth Entropy 指南代码。

https://github.com/berachain/guides/tree/main/apps/pyth-entropy

🛠️ 想构建更多项目吗?

想在 Berachain 上构建更多项目并查看更多示例吗?请查看我们的 Berachain GitHub 指南库,其中包含各种实现,包括 NextJS、Hardhat、Viem、Foundry 等。

如果您想深入了解更多细节,请查看我们的 Berachain 文档

寻找开发支持?

请务必加入我们的 Berachain Discord 服务器,并查看我们的开发者频道以提问。

❤️ 别忘了通过分享和点赞这篇文章来表达支持。


原创文章,作者:Erica,如若转载,请注明出处:https://www.dappchaser.com/fair-random-nft-mints-with-pyth-entropy-on-berachain/

发表评论

邮箱地址不会被公开。 必填项已用*标注

联系我们

邮件:contact@dappchaser.com

QR code