Created
May 22, 2023 10:53
-
-
Save ChristianOConnor/7b0b1e65590404a0af9a9076d67414af to your computer and use it in GitHub Desktop.
RandomSurfaceReachT1 05/21/23
This file contains hidden or 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.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