|
| 1 | +# NFT Growth with Pyth Entropy |
| 2 | + |
| 3 | +This example demonstrates how to build an interactive NFT growth system using Pyth Entropy for verifiable randomness. The application combines smart contracts with a Next.js frontend to create an engaging experience where NFTs can "grow" and level up based on provably fair random outcomes. |
| 4 | + |
| 5 | +## What This Example Does |
| 6 | + |
| 7 | +The NFT Growth example showcases a game-like mechanic where users can mint NFTs and attempt to grow them through multiple levels. Each growth attempt uses Pyth Entropy to generate verifiable randomness, determining one of three possible outcomes: |
| 8 | + |
| 9 | +- **Success (40% chance)**: The NFT levels up by 1 |
| 10 | +- **Failure (40% chance)**: The NFT remains at the current level |
| 11 | +- **Death (20% chance)**: The NFT permanently dies and cannot grow further |
| 12 | + |
| 13 | +NFTs start at level 1 and can grow up to level 5. Each growth attempt requires paying a fee to the Entropy provider and locks the NFT during the randomness request to prevent double-spending or race conditions. Once the random number is delivered via callback, the NFT is automatically unlocked and the result is applied. |
| 14 | + |
| 15 | +### Key Components |
| 16 | + |
| 17 | +**Smart Contracts** (`contract/`): |
| 18 | +- **NFTGrowth.sol**: Main contract implementing the growth mechanics with Entropy integration. It extends a basic NFT contract and uses the `IEntropyConsumer` interface to receive random numbers via callbacks. |
| 19 | +- **NFT.sol**: Base ERC721A NFT contract with ownership management and utilities. |
| 20 | + |
| 21 | +**Frontend Application** (`app/`): |
| 22 | +- Next.js 14 application built with React, Wagmi, Tailwind CSS, and shadcn/ui components. |
| 23 | +- Provides an intuitive interface for minting NFTs and initiating growth attempts. |
| 24 | +- Listens to blockchain events to update the UI in real-time as growth results are processed. |
| 25 | + |
| 26 | +### How the Entropy Integration Works |
| 27 | + |
| 28 | +1. **User Initiates Growth**: The user calls the `grow()` function with their NFT token ID and contributes user-generated randomness. |
| 29 | + |
| 30 | +2. **Request Sent to Entropy**: The contract requests randomness from Pyth Entropy by calling `entropy.requestWithCallback()`, paying the required fee. The NFT is locked to prevent concurrent requests. |
| 31 | + |
| 32 | +3. **Callback Delivers Randomness**: After the Entropy provider generates randomness, it calls back the contract's `entropyCallback()` function with the random number. |
| 33 | + |
| 34 | +4. **Result Applied**: The contract uses the random number to determine the outcome (success, failure, or death), updates the NFT's state, unlocks it, and emits an event. |
| 35 | + |
| 36 | +5. **Frontend Updates**: The application listens for the `NFTResult` event and updates the UI to show the user the outcome. |
| 37 | + |
| 38 | +This commit-reveal pattern ensures that neither the user nor the contract can predict or manipulate the outcome, providing provably fair randomness for the growth mechanic. |
| 39 | + |
| 40 | +## Project Structure |
| 41 | + |
| 42 | +``` |
| 43 | +entropy/growing/ |
| 44 | +├── contract/ # Smart contracts built with Hardhat |
| 45 | +│ ├── contracts/ |
| 46 | +│ │ ├── NFTGrowth.sol # Main growth contract with Entropy integration |
| 47 | +│ │ └── NFT.sol # Base ERC721A NFT contract |
| 48 | +│ ├── ignition/ |
| 49 | +│ │ └── modules/ |
| 50 | +│ │ └── App.ts # Deployment configuration |
| 51 | +│ ├── package.json |
| 52 | +│ └── hardhat.config.ts |
| 53 | +│ |
| 54 | +└── app/ # Next.js frontend application |
| 55 | + ├── app/ |
| 56 | + │ └── (home)/ |
| 57 | + │ └── page.tsx # Main application page |
| 58 | + ├── components/ # UI components |
| 59 | + ├── contracts/ # Generated contract ABIs and types |
| 60 | + ├── providers/ # Wagmi and React Query providers |
| 61 | + ├── package.json |
| 62 | + └── wagmi.config.ts |
| 63 | +``` |
| 64 | + |
| 65 | +## Prerequisites |
| 66 | + |
| 67 | +Before running this example, ensure you have: |
| 68 | + |
| 69 | +- **Node.js** (v18 or later) |
| 70 | +- **Bun** package manager installed (https://bun.sh) |
| 71 | +- A Web3 wallet (e.g., MetaMask) with funds on the target network |
| 72 | +- Access to the Pyth Entropy service on your chosen network |
| 73 | + |
| 74 | +## Running the Example |
| 75 | + |
| 76 | +### Step 1: Deploy the Smart Contracts |
| 77 | + |
| 78 | +Navigate to the contract directory and install dependencies: |
| 79 | + |
| 80 | +```bash |
| 81 | +cd contract |
| 82 | +bun install |
| 83 | +``` |
| 84 | + |
| 85 | +Deploy the contracts to Blast Sepolia testnet: |
| 86 | + |
| 87 | +```bash |
| 88 | +bunx hardhat ignition deploy ignition/modules/App.ts --network blast-sepolia --verify |
| 89 | +``` |
| 90 | + |
| 91 | +The deployment script in `ignition/modules/App.ts` uses the following addresses: |
| 92 | +- **Entropy Contract**: `0x98046Bd286715D3B0BC227Dd7a956b83D8978603` |
| 93 | +- **Entropy Provider**: `0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344` |
| 94 | + |
| 95 | +After deployment, note the deployed contract address, as you'll need to update it in the frontend configuration. |
| 96 | + |
| 97 | +### Step 2: Configure the Frontend |
| 98 | + |
| 99 | +Navigate to the app directory and install dependencies: |
| 100 | + |
| 101 | +```bash |
| 102 | +cd ../app |
| 103 | +bun install |
| 104 | +``` |
| 105 | + |
| 106 | +Update the contract address in `contracts/addresses.ts` with your deployed contract address. |
| 107 | + |
| 108 | +If deploying to a different network, also update: |
| 109 | +- `config.ts` with the correct chain configuration |
| 110 | +- `ignition/modules/App.ts` with the appropriate Entropy contract and provider addresses for your network |
| 111 | + |
| 112 | +### Step 3: Run the Frontend |
| 113 | + |
| 114 | +Start the development server: |
| 115 | + |
| 116 | +```bash |
| 117 | +bun dev |
| 118 | +``` |
| 119 | + |
| 120 | +The application will be available at http://localhost:3000. |
| 121 | + |
| 122 | +### Step 4: Interact with the Application |
| 123 | + |
| 124 | +1. **Connect Wallet**: Click the wallet button to connect your Web3 wallet to the application. |
| 125 | + |
| 126 | +2. **Mint an NFT**: Navigate to the "Mint" tab and click the mint button to create your first NFT. It will start at level 1. |
| 127 | + |
| 128 | +3. **Grow Your NFT**: Switch to the "Grow" tab, select your NFT, and click "Grow" to attempt leveling it up. The application will prompt you to pay the Entropy fee. |
| 129 | + |
| 130 | +4. **Wait for Results**: After submitting the transaction, wait for the Entropy callback to complete. The UI will update automatically when the result is available, showing whether your NFT succeeded, failed, or died. |
| 131 | + |
| 132 | +5. **Unlock if Needed**: If a growth request gets stuck (e.g., the callback fails), you can manually unlock your NFT after 10 minutes using the unlock function. |
| 133 | + |
| 134 | +## Key Contract Functions |
| 135 | + |
| 136 | +### NFTGrowth Contract |
| 137 | + |
| 138 | +- **`mint()`**: Mints a new NFT starting at level 1 |
| 139 | +- **`grow(uint256 tokenId, bytes32 userRandomNumber)`**: Initiates a growth request using Entropy randomness |
| 140 | +- **`getGrowFee()`**: Returns the current fee required for a growth attempt |
| 141 | +- **`unlock(uint256 tokenId)`**: Manually unlocks an NFT after the 10-minute lock period |
| 142 | +- **`ownerUnlock(uint256 tokenId)`**: Owner-only function to unlock any NFT |
| 143 | + |
| 144 | +### Events |
| 145 | + |
| 146 | +- **`NftGrowthRequested`**: Emitted when a growth request is submitted |
| 147 | +- **`NFTResult`**: Emitted when the Entropy callback completes with the growth result |
| 148 | + |
| 149 | +## Development Notes |
| 150 | + |
| 151 | +### Technology Stack |
| 152 | + |
| 153 | +**Smart Contracts**: |
| 154 | +- Solidity ^0.8.24 |
| 155 | +- Hardhat for development and deployment |
| 156 | +- OpenZeppelin contracts for security |
| 157 | +- ERC721A for gas-efficient NFT minting |
| 158 | +- Pyth Entropy SDK for randomness |
| 159 | + |
| 160 | +**Frontend**: |
| 161 | +- Next.js 14 with App Router |
| 162 | +- React 18 |
| 163 | +- Wagmi v2 for Ethereum interactions |
| 164 | +- Viem for contract interactions |
| 165 | +- TanStack React Query for state management |
| 166 | +- Tailwind CSS for styling |
| 167 | +- shadcn/ui for UI components |
| 168 | + |
| 169 | +### Locking Mechanism |
| 170 | + |
| 171 | +The contract implements a locking system to prevent callback spam and ensure sequential growth attempts. When a user initiates growth, the NFT is locked. The lock prevents new growth requests until either: |
| 172 | +- The callback completes and automatically unlocks the NFT, or |
| 173 | +- 10 minutes pass, allowing manual unlock |
| 174 | + |
| 175 | +This ensures that each growth step completes before the next one begins, maintaining the integrity of the NFT's state. |
| 176 | + |
| 177 | +### Testing Locally |
| 178 | + |
| 179 | +To test the contracts without deploying: |
| 180 | + |
| 181 | +```bash |
| 182 | +cd contract |
| 183 | +bunx hardhat test |
| 184 | +``` |
| 185 | + |
| 186 | +For frontend development with a local blockchain: |
| 187 | + |
| 188 | +1. Start a local Hardhat node: `bunx hardhat node` |
| 189 | +2. Deploy contracts locally: `bunx hardhat ignition deploy ignition/modules/App.ts --network localhost` |
| 190 | +3. Update the frontend configuration to use the local network |
| 191 | +4. Run the frontend: `cd ../app && bun dev` |
| 192 | + |
| 193 | +Note that testing with actual Entropy requires deploying to a network where Pyth Entropy is available, as the randomness provider operates off-chain. |
| 194 | + |
| 195 | +## Supported Networks |
| 196 | + |
| 197 | +This example is configured for **Blast Sepolia** testnet, but can be adapted for any EVM network that supports Pyth Entropy. You'll need to: |
| 198 | + |
| 199 | +1. Find the Entropy contract and provider addresses for your target network in the Pyth documentation |
| 200 | +2. Update `ignition/modules/App.ts` with the correct addresses |
| 201 | +3. Configure the network in `hardhat.config.ts` |
| 202 | +4. Update the frontend's `config.ts` with the chain configuration |
| 203 | + |
| 204 | +For available networks and addresses, see the Pyth Entropy documentation at https://docs.pyth.network/entropy. |
| 205 | + |
| 206 | +## Acknowledgments |
| 207 | + |
| 208 | +This example was created by [lualabs.xyz](https://lualabs.xyz) to demonstrate the capabilities of Pyth Entropy for NFT gaming mechanics. It showcases how verifiable randomness can create fair and engaging on-chain experiences. |
| 209 | + |
| 210 | +## Additional Resources |
| 211 | + |
| 212 | +- **Pyth Entropy Documentation**: https://docs.pyth.network/entropy |
| 213 | +- **Pyth Network**: https://pyth.network |
| 214 | +- **Source Repository**: https://github.com/pyth-network/pyth-examples |
0 commit comments