1
0

Pythのエントロピーを活用したBerachainにおける公平なNFTランダムミント [Berachain翻訳]

Last updated at Posted at 2024-09-10

本記事は下記の翻訳となります。
『Fair Random NFT Mints with Pyth Entropy on Berachain』

image.png

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

Installing Foundry To Your Computer

インストールの詳細については、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

依存関係のインストール

いくつかの依存関係を利用するので、以下にインストールします:

# 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

Install Contract Dependencies & 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 File For Remapping

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);
    }
}

では、このコントラクトを詳しく見ていきましょう:

EntropyNFT コントラクトは ERC721Enumerable を継承し、標準的な NFT 機能を提供しています。さらに、このコントラクトは IEntropyConsumer を継承しており、これはランダム数のリクエストが満たされた時のコールバックを実装します。
IEntropy.sol は、Pyth Entropy コントラクトと相互作用するためのインターフェースを提供します。コンストラクタの引数_entropy は、デプロイされた Entropy コントラクトをこのインターフェースに接続します。
コントラクトは最大供給量を 500 NFT と定義し、availableTokenIds 配列を[0…499]で初期化します。
requestMint関数は、ユーザーがユーザーコミットメント(詳細は後述)を提供し、必要な手数料を支払うことで、Entropy からランダム数をリクエストすることを可能にします。返された sequenceNumber は、そのシーケンス番号に対応するランダム数で NFT をミントする権利を呼び出し元にインデックス付けします。
entropyCallback関数は、分散型キーパーボットがランダム数のリクエストを満たした時に呼び出され、最終的に固有のトークン ID で NFT をミントします。これは、ランダム数をミントされるべき残りの NFT 数で剰余を取ることで達成され、ランダムなインデックスが availableTokenIds 配列の範囲内にあることを保証します。使用済みの tokenIds は配列の最後の要素と交換され、利用可能な tokenIds のリストを維持します。

プロジェクトと共に生成された 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 Testnet へのデプロイ

まず、スマート・コントラクトをコンパイルします:

# FROM: ./pyth-entropy

forge build;

./outディレクトリに多くのビルド出力があることに気づくでしょう。

Berachain Testnet に新しいコントラクトをデプロイするために、forge createコマンドを活用する予定です(コマンドについての詳細はこちら):

# 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>

.env ファイルの ENTROPY_NFT_ADDRESS に、デプロイしたコントラクトのアドレスを入力してください。
デプロイメント手数料を支払うには$BERA が必要です。フォーセット(無料配布)の資金はhttps://artio.faucet.berachain.com/で入手できます。

ランダム化された NFT のミント

さて、JavaScript を使用して新しい NFT コントラクトと対話しましょう。スクリプトを格納する新しいフォルダを作成しましょう(2 つのスクリプトを作成します):

# FROM ./pyth-entropy

mkdir app;
cd app;
touch requestMint.js;

./app/requestMint.js`に以下のコードを貼り付けます:

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スクリプトは主に 2 つの手順を実行します:

ステップ 1: まず、ユーザーウォレットと NFT および Entropy コントラクトと対話するための web3 コントラクトを初期化します。

ステップ 2: ランダムな数字が生成され、必要な手数料と共にEntropyNFTrequestMint関数に渡されます。その見返りとして、Entropy コントラクトからsequenceNumberを受け取ります。

ステップ 3: リクエストを送信した後、自動化された Pyth サービスがランダムな数字を生成し、NFT コントラクトのentropyCallback関数を実行します。このスクリプトの部分は、コントラクトのMintedイベントを監視します。このイベントは、Entropy リクエストが満たされ、そのランダムな数字を使用して検証可能なランダム NFT があなたのウォレットにミントされたことを示します。

おめでとうございます!Pyth の Entropy サービスを活用して、証明可能な公平な NFT ミントプロセスを提供することに成功しました 🎉

Entropy の概要 🔮

Pyth Entropy は、ランダムな数字を生成するための古典的なコミット/リビールスキームを拡張しています。まず、2 つの当事者が個別に秘密の値(生成されたランダムな数字)にコミットします。リビールフェーズでは、2 つのランダムな数字のハッシュに基づいて最終的なランダムな数字が生成されます。

NFT コントラクトの文脈では、ランダムな数字の生成は以下のように機能し、関与する異なる当事者に重点を置いています:

  1. ユーザーが NFT コントラクトのrequestMint呼び出しでランダムな数字にコミットします
  2. オフチェーンサービス(ボット)が 2 番目のランダムな数字を生成します
  3. ボットは 2 つのランダムな数字を Pyth のEntropyコントラクトにrevealWithCallBack呼び出しを通じて送信します。これにより最終的に、NFT コントラクトのentropyCallbackが呼び出され、ランダム化された NFT がユーザーにミントされます

注意: ユーザーの視点からは、ランダムな数字をリクエストする最初のトランザクション(requestMint)は NFT をミントしません。むしろ、ユーザーはボットによって開始される後続の呼び出しで NFT を受け取ります。

多くの動く部分があるので、ここに相互作用を分解するのに役立つフロー図があります:


Pyth Entropy Sequence Diagram

Entropy の設計の詳細は、Pyth's docsを参照してください。


🐻 Full Code Repository

最終的なコードや他のガイドを見たい場合は、Berachain Pyth Entropy Guide Code をチェックしてください。


🛠️ さらに開発を進めたいですか?

Berachain でさらに開発を進めたい、より多くの実装例を見たいとお考えですか?私たちBerachain GitHub Guides Repo をぜひご覧ください。このリポジトリには、NextJS、Hardhat、Viem、Foundry など、様々な技術を使用した幅広い実装例が用意されています。

より詳細な情報を探求したい場合は、Berachain ドキュメントをご覧ください。

開発者サポートをお探しですか?

質問をするために、Berachain Discordサーバーに参加し、開発者チャンネルをチェックしてください。

❤️ この記事に対して愛を示すのを忘れないでください 👏🏼



【Sunrise とは】
Sunrise は Proof of Liquidity(PoL)と Fee Abstraction(手数料抽象化)を備えたデータ可用性レイヤーです。 私たちは DA の体験を再構築し、多様なエコシステムからのモジュラー型流動性を活用してロールアップを立ち上げています。

【Social Links】

【お問合せ】
Sunrise へのお問い合わせはこちらから 👉 Google Form

1080x360.jpeg

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0