|
| 1 | +//SPDX-License-Identifier: MIT |
| 2 | +pragma solidity ^0.8.7; |
| 3 | + |
| 4 | +import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; |
| 5 | +import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; |
| 6 | +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; |
| 7 | +import "@openzeppelin/contracts/utils/Counters.sol"; |
| 8 | +import "@openzeppelin/contracts/utils/Strings.sol"; |
| 9 | +import "@openzeppelin/contracts/utils/Base64.sol"; |
| 10 | + |
| 11 | +contract EmojiNFT is ERC721URIStorage, VRFConsumerBaseV2 { |
| 12 | + using Counters for Counters.Counter; |
| 13 | + Counters.Counter private tokenIds; |
| 14 | + |
| 15 | + string[] private emojis = [ |
| 16 | + unicode"😁", |
| 17 | + unicode"😂", |
| 18 | + unicode"😍", |
| 19 | + unicode"😭", |
| 20 | + unicode"😴", |
| 21 | + unicode"😎", |
| 22 | + unicode"🤑", |
| 23 | + unicode"🥳", |
| 24 | + unicode"😱", |
| 25 | + unicode"🙄" |
| 26 | + ]; |
| 27 | + |
| 28 | + VRFCoordinatorV2Interface internal immutable vrfCoordinator; |
| 29 | + bytes32 internal immutable keyHash; |
| 30 | + uint64 internal immutable subscriptionId; |
| 31 | + uint32 internal immutable callbackGasLimit; |
| 32 | + uint32 internal immutable numWords; |
| 33 | + uint16 internal immutable requestConfirmations; |
| 34 | + |
| 35 | + mapping(uint256 => address) requestToSender; |
| 36 | + |
| 37 | + event RandomnessRequested(uint256 indexed requestId); |
| 38 | + |
| 39 | + constructor( |
| 40 | + address _vrfCoordinator, |
| 41 | + bytes32 _keyHash, |
| 42 | + uint64 _subscriptionId, |
| 43 | + uint32 _callbackGasLimit, |
| 44 | + uint16 _requestConfirmations |
| 45 | + ) VRFConsumerBaseV2(_vrfCoordinator) ERC721("EmojiNFT", "EMOJI") { |
| 46 | + vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator); |
| 47 | + keyHash = _keyHash; |
| 48 | + subscriptionId = _subscriptionId; |
| 49 | + callbackGasLimit = _callbackGasLimit; |
| 50 | + numWords = 4; |
| 51 | + requestConfirmations = _requestConfirmations; |
| 52 | + } |
| 53 | + |
| 54 | + function mint() public returns (uint256 requestId) { |
| 55 | + requestId = vrfCoordinator.requestRandomWords( |
| 56 | + keyHash, |
| 57 | + subscriptionId, |
| 58 | + requestConfirmations, |
| 59 | + callbackGasLimit, |
| 60 | + numWords |
| 61 | + ); |
| 62 | + |
| 63 | + requestToSender[requestId] = msg.sender; |
| 64 | + |
| 65 | + emit RandomnessRequested(requestId); |
| 66 | + } |
| 67 | + |
| 68 | + function pickRandomColor(uint256 firstRandomNumber, uint256 secondRandomNumber, uint256 thirdRandomNumber) |
| 69 | + internal |
| 70 | + pure |
| 71 | + returns (string memory) |
| 72 | + { |
| 73 | + uint256 r = firstRandomNumber % 256; |
| 74 | + uint256 g = secondRandomNumber % 256; |
| 75 | + uint256 b = thirdRandomNumber % 256; |
| 76 | + |
| 77 | + return |
| 78 | + string( |
| 79 | + abi.encodePacked( |
| 80 | + "rgb(", |
| 81 | + Strings.toString(r), |
| 82 | + ", ", |
| 83 | + Strings.toString(g), |
| 84 | + ", ", |
| 85 | + Strings.toString(b), |
| 86 | + ");" |
| 87 | + ) |
| 88 | + ); |
| 89 | + } |
| 90 | + |
| 91 | + function createOnChainSvg(string memory emoji, string memory color) internal pure returns(string memory svg) { |
| 92 | + string memory baseSvg = "<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 350 350'><style>.base { font-size: 100px; }</style><rect width='100%' height='100%' style='fill:"; |
| 93 | + string memory afterColorSvg = "' /><text x='50%' y='50%' class='base' dominant-baseline='middle' text-anchor='middle'>"; |
| 94 | + |
| 95 | + svg = string(abi.encodePacked(baseSvg, color, afterColorSvg, emoji, "</text></svg>")); |
| 96 | + } |
| 97 | + |
| 98 | + function createTokenUri(string memory emoji, string memory svg) internal pure returns(string memory tokenUri) { |
| 99 | + string memory json = Base64.encode( |
| 100 | + bytes( |
| 101 | + string( |
| 102 | + abi.encodePacked( |
| 103 | + '{"name": "', |
| 104 | + emoji, |
| 105 | + '", "description": "Random Emoji NFT Collection Powered by Chainlink VRF", "image": "data:image/svg+xml;base64,', |
| 106 | + Base64.encode(bytes(svg)), |
| 107 | + '"}' |
| 108 | + ) |
| 109 | + ) |
| 110 | + ) |
| 111 | + ); |
| 112 | + |
| 113 | + tokenUri = string( |
| 114 | + abi.encodePacked("data:application/json;base64,", json) |
| 115 | + ); |
| 116 | + } |
| 117 | + |
| 118 | + function fulfillRandomWords(uint256 requestId, uint256[] memory randomNumbers) |
| 119 | + internal |
| 120 | + override |
| 121 | + { |
| 122 | + uint256 tokenId = tokenIds.current(); |
| 123 | + |
| 124 | + uint256 emojiIndex = (randomNumbers[0] % emojis.length) + 1; |
| 125 | + string memory emoji = emojis[emojiIndex]; |
| 126 | + string memory color = pickRandomColor(randomNumbers[1], randomNumbers[2], randomNumbers[3]); |
| 127 | + string memory svg = createOnChainSvg(emoji, color); |
| 128 | + string memory tokenUri = createTokenUri(emoji, svg); |
| 129 | + |
| 130 | + _safeMint(requestToSender[requestId], tokenId); |
| 131 | + _setTokenURI(tokenId, tokenUri); |
| 132 | + |
| 133 | + tokenIds.increment(); |
| 134 | + } |
| 135 | +} |
0 commit comments