Skip to content

Instantly share code, notes, and snippets.

@larrythecucumber321
Last active May 17, 2024 13:52
Show Gist options
  • Save larrythecucumber321/c90ffc35664038fb41bb547da1411332 to your computer and use it in GitHub Desktop.
Save larrythecucumber321/c90ffc35664038fb41bb547da1411332 to your computer and use it in GitHub Desktop.
Pyth Entropy Testing
// 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 {}
}
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