Last active
May 17, 2024 13:52
-
-
Save larrythecucumber321/c90ffc35664038fb41bb547da1411332 to your computer and use it in GitHub Desktop.
Pyth Entropy Testing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
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(address minter, uint256 tokenId); | |
event Result(bytes32 randomNumber); | |
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 _provider, | |
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 Result(randomNumber); | |
} | |
// This method is required by the IEntropyConsumer interface | |
function getEntropy() internal view override returns (address) { | |
return address(entropy); | |
} | |
// Initialize array of available token IDs | |
function initializeAvailableTokenIds() private { | |
for (uint256 i = 0; i < MAX_SUPPLY; i++) { | |
availableTokenIds.push(i); | |
} | |
} | |
receive() external payable {} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { Web3 } = require("web3"); | |
const EntropyNFTAbi = require("../out/EntropyNFT.sol/EntropyNFT.json"); | |
const EntropyAbi = require("@pythnetwork/entropy-sdk-solidity/abis/IEntropy.json"); | |
const fs = require("fs").promises; | |
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}`); | |
// Poll for new Minted events emitted by EntropyNFT | |
// Stops polling when same sequenceNumber is fulfilled | |
let fromBlock = requestReceipt.blockNumber; | |
const intervalId = setInterval(async () => { | |
const currentBlock = await web3.eth.getBlockNumber(); | |
if (fromBlock > currentBlock) { | |
return; | |
} | |
console.log("Waiting at block", currentBlock) | |
// Get 'FlipResult' events emitted by the CoinFlip contract for given block range. | |
const events = await entropyNFTContract.getPastEvents("Minted", { | |
fromBlock: fromBlock, | |
toBlock: currentBlock, | |
}); | |
fromBlock = currentBlock + 1n; | |
// 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) { | |
console.log( | |
`Minted tokenId: ${event.returnValues.tokenId}` | |
); | |
clearInterval(intervalId); | |
} | |
}, 1000); | |
} | |
requestMint(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment