Skip to content

Instantly share code, notes, and snippets.

@ChristianOConnor
Created May 22, 2023 10:53
Show Gist options
  • Save ChristianOConnor/7b0b1e65590404a0af9a9076d67414af to your computer and use it in GitHub Desktop.
Save ChristianOConnor/7b0b1e65590404a0af9a9076d67414af to your computer and use it in GitHub Desktop.
RandomSurfaceReachT1 05/21/23
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@api3/airnode-protocol/contracts/rrp/requesters/RrpRequesterV0.sol";
contract RandomSurfaceReachT1 is ERC721URIStorage, Ownable, RrpRequesterV0 {
event RequestedRandom(bytes32 indexed requestId);
event MintedRandomNFT(bytes32 indexed requestId, uint256 response);
event MintCostChanged(uint256 newCost);
event Withdrawn(address indexed to, uint256 amount);
address public airnode;
bytes32 public endpointIdUint256;
address public sponsorWallet;
address public authorizedAccount;
uint256 public tokenCounter;
uint256 public mintCost = 0.01 ether;
uint256 public constant MAX_MINTS_PER_ADDRESS = 3;
enum Classifier {FIRST, SECOND, THIRD}
mapping(uint256 => Classifier) public tokenIdToClassifier;
mapping(bytes32 => bool) public awaitingFulfillment;
mapping(bytes32 => address) public requestToMinter;
mapping(address => uint256) public minterToMintCount;
struct RandomNft {
uint256 nonce;
address from;
}
string public firstUri;
string public secondUri;
string public thirdUri;
string private constant ERR_INVALID_SIGNER = "INVALID_SIGNER";
string private constant ERR_REQUEST_ID_UNKNOWN = "Request ID not known";
string private constant ERR_MINT_COST_NOT_MET = "Minting cost not met";
string private constant ERR_MINT_LIMIT_REACHED = "Mint limit reached";
string private constant ERR_VERIFICATION_FAILED = "Verification failed";
constructor(address _airnodeRrp) RrpRequesterV0(_airnodeRrp) ERC721("PRIVATE MINT RANDOM NFT", "PMRNFT") {}
function setParameters(
address _airnode,
bytes32 _endpointIdUint256,
address _sponsorWallet,
address _authorizedAccount
) external onlyOwner() {
airnode = _airnode;
endpointIdUint256 = _endpointIdUint256;
sponsorWallet = _sponsorWallet;
authorizedAccount = _authorizedAccount;
}
function setURIs(
string calldata _firstUri,
string calldata _secondUri,
string calldata _thirdUri
) external onlyOwner() {
firstUri = _firstUri;
secondUri = _secondUri;
thirdUri = _thirdUri;
}
function setMintCost(uint256 _newCost) public onlyOwner() {
mintCost = _newCost;
emit MintCostChanged(_newCost);
}
function requestRandomNFT(
RandomNft memory nft,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) external payable {
require(msg.value == mintCost, ERR_MINT_COST_NOT_MET);
require(minterToMintCount[msg.sender] < MAX_MINTS_PER_ADDRESS, ERR_MINT_LIMIT_REACHED);
require(verify(authorizedAccount, nft, sigR, sigS, sigV), ERR_VERIFICATION_FAILED);
bytes32 requestId = airnodeRrp.makeFullRequest(
airnode,
endpointIdUint256,
address(this),
sponsorWallet,
address(this),
this.fulfill.selector,
""
);
awaitingFulfillment[requestId] = true;
requestToMinter[requestId] = msg.sender;
emit RequestedRandom(requestId);
}
function fulfill(bytes32 requestId, bytes calldata data)
external
onlyAirnodeRrp
{
require(awaitingFulfillment[requestId], ERR_REQUEST_ID_UNKNOWN);
uint256 newId = tokenCounter++;
uint256 randomUint256 = abi.decode(data, (uint256));
Classifier classifier = Classifier(randomUint256 % 3);
tokenIdToClassifier[newId] = classifier;
_safeMint(requestToMinter[requestId], newId);
minterToMintCount[requestToMinter[requestId]]++;
awaitingFulfillment[requestId] = false;
if (classifier == Classifier.FIRST) {
_setTokenURI(newId, firstUri);
} else if (classifier == Classifier.SECOND) {
_setTokenURI(newId, secondUri);
} else if (classifier == Classifier.THIRD) {
_setTokenURI(newId, thirdUri);
}
emit MintedRandomNFT(requestId, randomUint256);
}
function verify(
address signer,
RandomNft memory nft,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) internal pure returns (bool) {
require(signer != address(0), ERR_INVALID_SIGNER);
return
signer ==
ecrecover(
keccak256(abi.encode(nft.nonce, nft.from)),
sigV,
sigR,
sigS
);
}
function withdraw() external onlyOwner() {
uint balance = address(this).balance;
payable(owner()).transfer(balance);
emit Withdrawn(owner(), balance);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment