Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/Hyfins.sol": {
"content": "// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
// I think the animals in these images really exist and one of them has been visiting me.
import "./HyfinsRenderer.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
contract Hyfins is ERC721, Ownable, ReentrancyGuard {
using Base64 for bytes;
HyfinsRenderer public renderer;
uint256 public constant MAX_SUPPLY = 240;
uint256 private _totalSupply;
uint256 public mintPrice = 0.004 ether;
bool public allowlistMintIsActive = false;
bool public publicMintIsActive = false;
mapping(address => bool) public allowlist;
mapping(address => bool) public freeMintList;
// STRUCTS
struct TraitNames {
string coloring;
string ear;
string tail;
string pattern;
string eyes;
string gem;
string parentA;
string parentB;
}
// EVENTS
event AllowlistUpdated(address[] addresses, bool added);
event FreeMintListUpdated(address[] addresses, bool added);
event TokenMinted(address indexed minter, uint256 tokenId, uint256 parentATokenId, uint256 parentBTokenId);
event BalanceWithdrawn(uint256 amount);
event MintPriceUpdated(uint256 newMintPrice);
event AllowlistMintActivated();
event PublicMintActivated();
// CONSTRUCTOR
constructor() ERC721("Hyfins", "HYFINS") Ownable(msg.sender) {
renderer = new HyfinsRenderer();
}
// MINTING FUNCTIONS
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
function activateAllowlistMint() external onlyOwner {
allowlistMintIsActive = true;
emit AllowlistMintActivated();
}
function activatePublicMint() external onlyOwner {
publicMintIsActive = true;
emit PublicMintActivated();
}
function setMintPrice(uint256 newMintPrice) external onlyOwner {
mintPrice = newMintPrice;
emit MintPriceUpdated(newMintPrice);
}
function mint(uint256 parentATokenId, uint256 parentBTokenId) external payable nonReentrant {
bool isFreeMint = false;
require(allowlistMintIsActive || publicMintIsActive, "Minting is not active");
if (!publicMintIsActive) { require(allowlist[msg.sender], "Not on allowlist"); }
_checkIfAnyTokensAreLeft();
if (msg.value == 0) {
require(freeMintList[msg.sender], "Not on free mint list");
isFreeMint = true;
} else {
require(msg.value == mintPrice, "Incorrect ETH amount");
}
_checkParents(parentATokenId, parentBTokenId);
uint256 tokenId = _getTokenId(parentATokenId, parentBTokenId);
_checkIfTokenHasBeenMinted(tokenId);
if (isFreeMint) {
freeMintList[msg.sender] = false;
address[] memory addressToRemove = new address[](1);
addressToRemove[0] = msg.sender;
emit FreeMintListUpdated(addressToRemove, false);
}
_totalSupply++;
_safeMint(msg.sender, tokenId);
emit TokenMinted(msg.sender, tokenId, parentATokenId, parentBTokenId);
}
function bellboyMint(uint256[2][] calldata parentPairsTokenIds) external onlyOwner nonReentrant {
for (uint256 i = 0; i < parentPairsTokenIds.length; i++) {
_checkIfAnyTokensAreLeft();
uint256 parentATokenId = parentPairsTokenIds[i][0];
uint256 parentBTokenId = parentPairsTokenIds[i][1];
_checkParents(parentATokenId, parentBTokenId);
uint256 tokenId = _getTokenId(parentATokenId, parentBTokenId);
_checkIfTokenHasBeenMinted(tokenId);
_totalSupply++;
_safeMint(msg.sender, tokenId);
emit TokenMinted(msg.sender, tokenId, parentATokenId, parentBTokenId);
}
}
function _getTokenId(uint256 parentATokenId, uint256 parentBTokenId) internal pure returns (uint256) {
uint256 tokenId = 16 * parentATokenId + parentBTokenId;
uint256 numberToSubtract = parentATokenId < parentBTokenId ? parentATokenId + 1 : parentATokenId;
return tokenId - numberToSubtract;
}
function _checkIfAnyTokensAreLeft() internal view {
require(_totalSupply < MAX_SUPPLY, "All tokens minted");
}
function _checkIfTokenHasBeenMinted(uint256 tokenId) internal view {
require(_ownerOf(tokenId) == address(0), "Token already minted");
}
function _checkParents(uint256 parentATokenId, uint256 parentBTokenId) internal pure {
require(parentATokenId < 16 && parentBTokenId < 16, "First Contact token IDs only go up to 15");
require(parentATokenId != parentBTokenId, "Must choose two different parents");
}
// WITHDRAW FUNCTION
function withdraw() external onlyOwner nonReentrant {
uint256 balance = address(this).balance;
require(balance > 0, "No ETH to withdraw");
(bool success, ) = payable(owner()).call{value: balance}("");
require(success, "ETH withdrawal failed");
emit BalanceWithdrawn(balance);
}
// ALLOWLIST FUNCTIONS
function addToAllowlist(address[] calldata addresses, bool getsFreeMint) external onlyOwner {
_checkAddressesLength(addresses);
for (uint256 i = 0; i < addresses.length; i++) {
_checkAddressNotZero(addresses[i]);
require(!allowlist[addresses[i]], "Address already on allowlist");
allowlist[addresses[i]] = true;
freeMintList[addresses[i]] = getsFreeMint;
}
emit AllowlistUpdated(addresses, true);
if (getsFreeMint) { emit FreeMintListUpdated(addresses, true); }
}
function removeFromAllowlist(address[] calldata addresses) external onlyOwner {
_checkAddressesLength(addresses);
uint256 addressesAlsoOnFMLCount = 0;
for (uint256 i = 0; i < addresses.length; i++) {
_checkAddressNotZero(addresses[i]);
require(allowlist[addresses[i]], "Address not on allowlist");
allowlist[addresses[i]] = false;
if (freeMintList[addresses[i]]) { addressesAlsoOnFMLCount++; }
}
emit AllowlistUpdated(addresses, false);
address[] memory addressesAlsoOnFML = new address[](addressesAlsoOnFMLCount);
uint256 fmlIndex = 0;
for (uint256 i = 0; i < addresses.length; i++) {
if (freeMintList[addresses[i]]) {
freeMintList[addresses[i]] = false;
addressesAlsoOnFML[fmlIndex] = addresses[i];
fmlIndex++;
}
}
if (addressesAlsoOnFMLCount > 0) { emit FreeMintListUpdated(addressesAlsoOnFML, false); }
}
function removeFromFreeMintList(address[] calldata addresses) external onlyOwner {
_checkAddressesLength(addresses);
for (uint256 i = 0; i < addresses.length; i++) {
_checkAddressNotZero(addresses[i]);
require(freeMintList[addresses[i]], "Address not on free mint list");
freeMintList[addresses[i]] = false;
}
emit FreeMintListUpdated(addresses, false);
}
function _checkAddressesLength(address[] calldata addresses) internal pure {
require(addresses.length > 0, "Must provide at least one address");
require(addresses.length <= 50, "Batch size too large");
}
function _checkAddressNotZero(address addr) internal pure {
require(addr != address(0), "Address cannot be 0");
}
// TOKENURI FUNCTIONS
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_ownerOf(tokenId) != address(0), "Token does not exist");
HyfinsRenderer.DNA memory dna = renderer.getDNA(tokenId);
TraitNames memory traitNames = _getTraitNames(dna);
string memory svg = renderer.generateSVG(dna);
string memory json = _generateJSON(traitNames, svg);
return string(abi.encodePacked(
"data:application/json;base64,",
Base64.encode(bytes(json))
));
}
function _generateJSON(TraitNames memory traitNames, string memory svg) internal pure returns (string memory) {
return string(abi.encodePacked(
'{"name": "', traitNames.parentA, traitNames.parentB, '",',
'"description": "HYFINS ARE REAL",',
'"attributes": [',
'{"trait_type": "Coloring", "value": "', traitNames.coloring, '"},',
'{"trait_type": "Ears", "value": "', traitNames.ear, '"},',
'{"trait_type": "Tail", "value": "', traitNames.tail, '"},',
'{"trait_type": "Pattern", "value": "', traitNames.pattern, '"},',
'{"trait_type": "Eyes", "value": "', traitNames.eyes, '"},',
'{"trait_type": "Gem", "value": "', traitNames.gem, '"},',
'{"trait_type": "Parent A", "value": "', traitNames.parentA, '"},',
'{"trait_type": "Parent B", "value": "', traitNames.parentB, '"}',
'],',
'"image": "data:image/svg+xml;base64,', Base64.encode(bytes(svg)), '"',
'}'
));
}
function _getTraitNames(HyfinsRenderer.DNA memory dna) internal pure returns (TraitNames memory) {
TraitNames memory traitNames;
// COLORINGS
traitNames.coloring =
(dna.coloring == 0) ? "Milk" :
(dna.coloring == 1) ? "Stone" :
(dna.coloring == 2) ? "Coyote" :
(dna.coloring == 3) ? "Ink" :
(dna.coloring == 4) ? "Sky" :
(dna.coloring == 5) ? "Blush" :
(dna.coloring == 6) ? "Potion" :
(dna.coloring == 7) ? "Bear" :
(dna.coloring == 8) ? "Martian" :
(dna.coloring == 9) ? "Navy" :
(dna.coloring == 10) ? "Moon" :
(dna.coloring == 11) ? "Star" :
(dna.coloring == 12) ? "Army" :
(dna.coloring == 13) ? "Silver" :
(dna.coloring == 14) ? "Cow" :
(dna.coloring == 15) ? "Coral mut." :
(dna.coloring == 16) ? "Jade mut." :
(dna.coloring == 17) ? "Fox mut." :
"Energy mut.";
// EARS
traitNames.ear =
(dna.ears == 0) ? "Fairy" :
(dna.ears == 1) ? "Owl" :
(dna.ears == 2) ? "Bat" :
(dna.ears == 3) ? "Pup" :
(dna.ears == 4) ? "Cat mut." :
(dna.ears == 5) ? "Imp mut." :
(dna.ears == 6) ? "Pantheon mut." :
"Morph mut.";
// TAILS
traitNames.tail =
(dna.tail == 0) ? "None" :
(dna.tail == 1) ? "Fox" :
(dna.tail == 2) ? "Twist" :
(dna.tail == 3) ? "Monkey" :
"Twin";
// PATTERNS
traitNames.pattern =
(dna.pattern == 0) ? "Pure" :
(dna.pattern == 1) ? "Core" :
(dna.pattern == 2) ? "Mime" :
(dna.pattern == 3) ? "Racer" :
(dna.pattern == 4) ? "Socks" :
(dna.pattern == 5) ? "Collar" :
(dna.pattern == 6) ? "Blot" :
(dna.pattern == 7) ? "Pure mut." :
(dna.pattern == 8) ? "Core mut." :
(dna.pattern == 9) ? "Mime mut." :
(dna.pattern == 10) ? "Mime-Pure mut." :
(dna.pattern == 11) ? "Racer mut." :
(dna.pattern == 12) ? "Socks mut." :
(dna.pattern == 13) ? "Collar mut." :
(dna.pattern == 14) ? "Blot mut." :
(dna.pattern == 15) ? "Flipped mut." :
(dna.pattern == 16) ? "Flipped-Mime mut." :
(dna.pattern == 17) ? "Flipped-Socks mut." :
(dna.pattern == 18) ? "Bandit mut." :
(dna.pattern == 19) ? "Bandit-Pure mut." :
(dna.pattern == 20) ? "Bandit-Socks mut." :
(dna.pattern == 21) ? "Bandit-Collar mut." :
(dna.pattern == 22) ? "Bandit-Separator mut." :
(dna.pattern == 23) ? "Cheetah mut." :
(dna.pattern == 24) ? "Cheetah-Pure mut." :
(dna.pattern == 25) ? "Cheetah-Socks mut." :
(dna.pattern == 26) ? "Cheetah-Collar mut." :
(dna.pattern == 27) ? "Vitiligo mut." :
(dna.pattern == 28) ? "Vitiligo-Pure mut." :
(dna.pattern == 29) ? "Vitiligo-Collar mut." :
(dna.pattern == 30) ? "Vitiligo-Separator mut." :
(dna.pattern == 31) ? "Separator mut." :
(dna.pattern == 32) ? "Separator-Mime mut." :
(dna.pattern == 33) ? "Separator-Racer mut." :
(dna.pattern == 34) ? "Separator-Socks mut." :
"Separator-Collar mut.";
// EYES
traitNames.eyes =
(dna.eyes == 0) ? "Yellow" :
(dna.eyes == 1) ? "Pale Blue" :
(dna.eyes == 2) ? "Blue" :
(dna.eyes == 3) ? "Gray" :
(dna.eyes == 4) ? "Green" :
(dna.eyes == 5) ? "Yellow mut." :
(dna.eyes == 6) ? "Blue mut." :
(dna.eyes == 7) ? "Gray mut." :
"Green mut.";
// GEMS
traitNames.gem =
(dna.gem == 0) ? "Sapphire" :
(dna.gem == 1) ? "Emerald" :
"Ruby";
traitNames.parentA = _getParentName(dna.parentA);
traitNames.parentB = _getParentName(dna.parentB);
return traitNames;
}
function _getParentName(uint256 parentTokenId) internal pure returns (string memory) {
string memory letter = parentTokenId < 4 ? "A" : parentTokenId < 8 ? "B" : parentTokenId < 12 ? "C" : "D";
string memory number = parentTokenId % 4 == 0 ? "1" : parentTokenId % 4 == 1 ? "2" : parentTokenId % 4 == 2 ? "3" : "4";
return string(abi.encodePacked(letter, number));
}
}
"
},
"src/HyfinsRenderer.sol": {
"content": "// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/utils/Strings.sol";
contract HyfinsRenderer {
using Strings for uint256;
// COLORS
string constant STONE = "#C3D2D7";
string constant COYOTE = "#E6D7C3";
string constant COYOTE_LIGHT = "#FAF0E1";
string constant COYOTE_DARK = "#8C6E5A";
string constant INK = "#696E73";
string constant SKY = "#DCFAFF";
string constant BLUSH = "#FFDCFA";
string constant POTION = "#96A0DC";
string constant POTION_LIGHT = "#D7E1FF";
string constant BEAR = "#BE967D";
string constant MARTIAN = "#C3F0C8";
string constant MARTIAN_LIGHT = "#E6FFEB";
string constant NAVY = "#6996BE";
string constant NAVY_LIGHT = "#C8D7E1";
string constant STAR = "#FFFFBE";
string constant ARMY = "#A5BE96";
string constant ARMY_LIGHT = "#D7E1D7";
string constant CORAL = "#FFA0A0";
string constant CORAL_LIGHT = "#FFDCDC";
string constant FOX = "#FFCD9B";
string constant JADE = "#96D7B9";
string constant ENERGY = "#D7FFB9";
string constant YELLOW = "#FFFF9B";
string constant BLUE = "#82EBFF";
string constant GRAY = "#F0FFFF";
string constant GREEN = "#9BFF9B";
string constant SAPPHIRE = "#4BE1FF";
string constant EMERALD = "#82FF82";
string constant RUBY = "#FF9BFF";
// SVG SNIPPETS
bytes constant HEADER = '<svg width="208" height="304" viewBox="0 0 52 76" fill="#000" fill-rule="evenodd" shape-rendering="crispEdges" xmlns="http://www.w3.org/2000/svg">';
bytes constant SCENE = '\
\
<g id="scene">\
<rect x="0" y="0" width="52" height="76" fill="#FFF"/>\
<rect x="2" y="2" width="48" height="74" fill="#DCE6EB"/>\
<path d="M50 72H52V73H0V72H2V71H12V70H40V71H50V72Z"/>\
<path d="M50 73H52V76H0V73H2V72H12V71H40V72H50V73Z" fill="#FFF"/>\
<path d="M37 76H15V75H18V74H34V75H37V76Z" class="shadow"/>\
<path d="M42 40H43V41H42V42H41V41H40V40H41V39H42V40ZM39 35H40V36H39V37H38V36H37V35H38V34H39V35ZM45 35H46V36H45V37H44V36H43V35H44V34H45V35ZM42 30H43V31H42V32H41V31H40V30H41V29H42V30Z" fill="#FFF"/>\
</g>';
bytes constant TAIL_PREFIX = '\
\
<g id="tail" transform="translate(7,40)">';
bytes constant TAIL_2_PREFIX = '\
\
<g id="tail-2" transform="translate(45,40) scale(-1,1)">';
bytes constant FOX_TAIL = '\
<path d="M9 2H8V3H9V2ZM8 3H6V4H8V3ZM6 4H4V5H6V4ZM4 5H3V6H4V5ZM3 6H2V7H3V6ZM5 17H6V16H7V14H8V12H9V11H10V10H11V9H2V7H1V9H0V15H1V17H2V18H5V17Z"/>\
<path d="M9 3H8V4H6V5H4V6H3V7H2V9H1V15H2V17H5V16H6V14H7V12H8V11H9V10H10V7H9V3Z" class="base-color"/>\
<path d="M10 10V7H9V3H8V8H9V10H10Z" class="shadow"/>';
bytes constant TWIST_TAIL = '\
<path d="M7 13H4V12H3V11H2V7H3V5H4V4H5V5H9V7H7V8H6V11H7V13ZM7 4H5V3H7V4ZM9 3H7V2H9V3Z"/>\
<path d="M6 12H4V11H3V7H4V5H5V4H7V3H9V6H7V7H6V8H5V11H6V12Z" class="base-color"/>\
<rect x="8" y="3" width="1" height="3" class="shadow"/>';
bytes constant MONKEY_TAIL = '\
<path d="M9 5H8V6H9V5ZM8 6H7V7H8V6ZM6 23H7V21H6V19H5V15H6V13H7V11H8V10H9V9H10V8H7V7H6V8H5V9H4V10H3V12H2V14H1V20H2V22H3V23H4V24H6V23Z"/>\
<path d="M9 6H8V7H7V8H6V9H5V10H4V12H3V14H2V20H3V22H4V23H6V21H5V19H4V15H5V13H6V11H7V10H8V9H9V8H10V7H9V6Z" class="base-color"/>\
<path d="M8 8H10V7H9V6H8V8Z" class="shadow"/>';
bytes constant BODY = '\
\
<g id="body" transform="translate(14,21)">\
<path d="M20 43H15V42H14V41H13V31H14V29H10V31H11V41H10V42H9V43H4V42H3V41H2V36H3V31H4V28H3V26H2V21H3V19H4V18H5V14H6V13H7V11H8V10H6V9H5V8H2V7H1V5H0V0H24V5H23V7H22V8H19V9H18V10H16V11H17V13H18V14H19V18H20V19H21V21H22V26H21V28H20V31H21V36H22V41H21V42H20V43Z"/>\
<path d="M10 21H8V19H7V21H8V22H10V21H11V18H10V21ZM16 21H14V18H15V16H14V18H13V21H14V22H16V21H17V19H18V18H19V19H20V21H21V26H20V28H19V31H20V36H21V41H20V42H19V40H18V42H17V40H16V42H15V41H14V31H15V25H14V28H10V25H9V31H10V41H9V42H8V40H7V42H6V40H5V42H4V41H3V36H4V31H5V28H4V26H3V21H4V19H5V18H6V19H7V17H6V14H7V13H8V11H16V13H17V14H18V17H17V19H16V21ZM9 18H10V16H9V18ZM23 5H22V7H19V8H18V9H16V10H8V9H6V8H5V7H2V5H1V0H23V5Z" class="base-color"/>\
<path d="M5 32H9V31H10V33H9V34H5V33H4V31H5V32ZM15 32H19V31H20V33H19V34H15V33H14V31H15V32ZM14 28H10V27H14V28ZM10 23H8V22H10V23ZM16 23H14V22H16V23ZM8 22H7V21H8V22ZM11 22H10V21H11V22ZM14 22H13V21H14V22ZM17 22H16V21H17V22ZM7 21H6V19H7V21ZM18 21H17V19H18V21ZM16 12H15V13H9V12H8V11H16V12Z" class="shadow"/>\
</g>';
bytes constant EARS_PREFIX = '\
\
<g id="ears" transform="translate(11,9)">';
bytes constant FAIRY_EARS = '\
<path d="M28 12H2V11H28V12ZM3 4H10V5H12V4H18V5H20V4H27V3H29V4H30V9H29V11H28V9H2V11H1V9H0V4H1V3H3V4Z"/>\
<path d="M27 12H3V11H2V9H1V4H3V5H10V6H12V5H18V6H20V5H27V4H29V9H28V11H27V12Z" class="base-color"/>';
bytes constant OWL_EARS = '\
<path d="M27 12H3V11H27V12ZM4 1H5V2H6V3H7V4H9V5H12V4H18V5H21V4H23V3H24V2H25V1H26V0H27V1H28V6H29V9H28V11H27V9H3V11H2V9H1V6H2V1H3V0H4V1Z"/>\
<path d="M26 12H4V11H3V9H2V6H3V7H4V6H3V1H4V2H5V3H6V4H7V5H9V6H12V5H18V6H21V5H23V4H24V3H25V2H26V1H27V6H26V7H25V8H26V7H27V6H28V9H27V11H26V12ZM4 8H5V7H4V8Z" class="base-color"/>';
bytes constant BAT_EARS = '\
<path d="M4 0H5V1H6V2H7V4H8V6H9V5H12V4H18V5H21V6H22V4H23V2H24V1H25V0H26V1H27V2V4V6V7V12H26V7H4V12H3V7V6V4V2V1H4V0Z"/>\
<path d="M5 2H6V4H7V6H8V7H9V6H12V5H18V6H21V7H22V6H23V4H24V2H25V1H26V12H4V1H5V2Z" class="base-color"/>';
bytes constant PUP_EARS = '\
<path d="M4 3H7V4H9V5H12V4H18V5H21V4H23V3H26V4H28V5H29V6H30V9H28V11H27V9H3V11H2V9H0V6H1V5H2V4H4V3ZM3 11H4V12H3V11ZM27 11V12H26V11H27Z"/>\
<path d="M26 12H4V11H3V8H1V6H2V5H4V4H7V5H9V6H12V5H18V6H21V5H23V4H26V5H28V6H29V8H27V11H26V12Z" class="base-color"/>';
bytes constant CAT_EARS = '\
<path d="M6 3H8V4H9V5H12V4H18V5H21V4H22V3H24V2H27V3H28V11H27V12H3V11H2V3H3V2H6V3Z"/>\
<path d="M6 4H8V5H9V6H12V5H18V6H21V5H22V4H24V3H27V11H26V12H4V11H3V3H6V4Z" class="base-color"/>';
bytes constant IMP_EARS = '\
<path d="M5 2H6V4H8V5H12V4H18V5H22V4H24V2H25V1H26V2H27V4H28V11H27V12H3V11H2V4H3V2H4V1H5V2Z"/>\
<path d="M26 12H4V11H3V4H4V2H5V4H6V5H8V6H12V5H18V6H22V5H24V4H25V2H26V4H27V11H26V12Z" class="base-color"/>';
bytes constant PANTHEON_EARS = '\
<path d="M7 3H8V6H9V5H12V4H18V5H21V6H22V3H23V2H26V3H27V12H3V3H4V2H7V3Z"/>\
<path d="M7 6H8V7H9V6H12V5H18V6H21V7H22V6H23V3H26V12H4V3H7V6Z" class="base-color"/>';
bytes constant MORPH_EARS = '\
<path d="M10 1H11V3H10V5H12V4H18V5H20V3H19V1H20V0H23V1H25V2H26V3H27V5H28V11H27V12H3V11H2V5H3V3H4V2H5V1H7V0H10V1Z"/>\
<path d="M10 3H9V5H10V6H12V5H18V6H20V5H21V3H20V1H23V2H25V3H26V5H27V11H26V12H4V11H3V5H4V3H5V2H7V1H10V3Z" class="base-color"/>';
bytes constant EYES = '\
\
<g id="eyes" transform="translate(17,20)">\
<path d="M6 2H7V6H6V7H2V6H1V5H0V1H1V0H5V1H6V2ZM17 1H18V5H17V6H16V7H12V6H11V2H12V1H13V0H17V1Z"/>\
<path d="M5 2H6V6H2V5H1V1H5V2ZM17 5H16V6H12V2H13V1H17V5Z" class="eye-color"/>\
<path d="M2 3H1V2H2V3ZM17 3H16V2H17V3ZM3 2H2V1H3V2ZM16 2H15V1H16V2Z" class="shadow"/>\
<path d="M2 2H1V1H2V2ZM17 1V2H16V1H17Z" fill="#FFF"/>\
</g>';
bytes constant GEM = '\
\
<g id="gem" transform="translate(24,16)">\
<path d="M3 1H4V3H3V4H1V3H0V1H1V0H3V1Z"/>\
<rect x="1" y="1" width="2" height="2" class="gem-color"/>\
<path d="M3 3H2V2H3V3ZM2 2H1V1H2V2Z" class="shadow"/>\
<rect x="2" y="1" width="1" height="1" fill="#FFF"/>\
</g>';
bytes constant TAIL_PATTERN_PREFIX = '\
\
<g id="tail-pattern" transform="translate(7,40)">';
bytes constant TAIL_2_PATTERN_PREFIX = '\
\
<g id="tail-2-pattern" transform="translate(45,40) scale(-1,1)">';
bytes constant COLLAR_FOX_TAIL_PATTERN = '\
<path d="M5 5H4V6H5V5ZM6 6H5V7H6V6ZM7 7H6V8H7V7ZM8 8H7V9H8V8ZM9 9H8V10H9V9ZM2 11H1V12H2V11ZM4 12H2V13H4V12ZM6 13H4V14H6V13Z"/>\
<path d="M2 13H4V14H6V16H5V17H2V15H1V12H2V13ZM10 10H9V9H8V8H7V7H6V6H5V5H6V4H8V3H9V7H10V10Z" class="pattern-color"/>\
<path d="M9 7H10V10H9V8H8V3H9V7Z" class="shadow"/>';
bytes constant COLLAR_TWIST_TAIL_PATTERN = '\
<rect x="4" y="7" width="1" height="1"/>\
<path d="M6 12H4V11H3V7H4V8H5V11H6V12Z" class="pattern-color"/>';
bytes constant COLLAR_MONKEY_TAIL_PATTERN = '\
<path d="M7 9H6V10H7V9ZM4 14H3V20H4V14Z"/>\
<path d="M9 6H8V7H7V8H6V9H7V10H8V9H9V8H10V7H9V6ZM3 19H4V15H3V14H2V20H3V19Z" class="pattern-color"/>\
<path d="M10 8H8V6H9V7H10V8Z" class="shadow"/>';
bytes constant FACE_PATTERN_PREFIX = '\
\
<g id="face-pattern" transform="translate(14,14)">';
bytes constant MIME_FACE_PATTERN = '\
<path d="M22 5H23V6H24V7H0V6H1V5H2V4H3V3H21V4H22V5Z"/>\
<path d="M22 6H23V12H22V14H19V15H18V16H16V17H8V16H6V15H5V14H2V12H1V6H2V5H3V4H21V5H22V6Z" class="pattern-color"/>';
bytes constant RACER_FACE_PATTERN = '\
<path d="M17 16H7V1H17V16Z"/>\
<path d="M16 17H8V1H9V0H15V1H16V17Z" class="pattern-color"/>';
bytes constant BANDIT_FACE_PATTERN = '\
<path d="M22 12H2V11H22V12ZM14 10H10V9H14V10Z"/>\
<path d="M14 12H20V11H21V12H22V14H19V15H18V16H16V17H8V16H6V15H5V14H2V12H3V11H4V12H10V10H14V12Z" class="pattern-color"/>';
bytes constant CHEETAH_FACE_PATTERN = '\
<path d="M9 17H8V13H9V17ZM16 13V17H15V13H16ZM3 8H1V7H3V8ZM23 8H21V7H23V8Z"/>\
<path d="M3 11H4V12H5V13H8V16H6V15H5V14H2V12H1V8H3V11ZM16 13H19V12H20V11H21V8H23V12H22V14H19V15H18V16H16V13Z" class="pattern-color"/>';
bytes constant VITILIGO_FACE_PATTERN = '\
<path d="M12 17H11V14H12V17ZM11 12V14H10V12H11ZM3 9H1V8H3V9Z"/>\
<path d="M8 16H6V15H5V14H2V12H1V9H10V14H11V17H8V16Z" class="pattern-color"/>';
bytes constant COLLAR_NECK_PATTERN = '\
\
<g id="neck-pattern" transform="translate(22,32)">\
<path d="M6 4H2V3H0V2H8V3H6V4Z"/>\
<path d="M6 3H2V2H0V0H8V2H6V3Z" class="pattern-color"/>\
<path d="M6 2H2V1H1V0H7V1H6V2Z" class="shadow"/>\
</g>';
bytes constant STOMACH_PATTERN_PREFIX = '\
\
<g id="stomach-pattern" transform="translate(22,32)">';
bytes constant STOMACH_PATTERN = '\
<path d="M6 4H2V5H6V4ZM8 11H0V14H8V11Z"/>\
<path d="M2 5H6V7H5V10H6V11H7V14H6V17H2V14H1V11H2V10H3V7H2V5Z" class="pattern-color"/>\
<path d="M6 17H2V16H6V17ZM2 12H1V11H2V12ZM7 12H6V11H7V12ZM3 11H2V10H3V11ZM6 11H5V10H6V11Z" class="shadow"/>';
bytes constant SEPARATOR_STOMACH_PATTERN = '\
<path d="M6 14H2V12H1V11H7V12H6V14ZM6 1H7V3H6V5H2V3H1V1H2V0H6V1Z"/>\
<path d="M6 3H5V5H6V7H5V10H6V12H5V14H6V17H2V14H3V12H2V10H3V7H2V5H3V3H2V1H3V0H5V1H6V3Z" class="pattern-color"/>\
<path d="M6 17H2V16H6V17ZM3 11H2V10H3V11ZM6 11H5V10H6V11ZM5 1H6V2H2V1H3V0H5V1Z" class="shadow"/>';
bytes constant FOOT_PATTERN_PREFIX = '\
\
<g id="foot-pattern" transform="translate(17,56)">';
bytes constant SOCKS_FOOT_PATTERN = '\
<path d="M6 3H7V4H0V3H1V2H6V3ZM18 4H11V3H12V2H17V3H18V4Z"/>\
<path d="M6 4H7V6H6V7H5V5H4V7H3V5H2V7H1V6H0V4H1V3H6V4ZM17 7H16V5H15V7H14V5H13V7H12V6H11V4H12V3H17V4H18V6H17V7Z" class="pattern-color"/>';
bytes constant VITILIGO_FOOT_PATTERN = '\
<path d="M7 3H6V2H7V3ZM6 2H4V1H6V2ZM4 1H1V0H4V1Z"/>\
<path d="M6 7H5V5H4V7H3V5H2V7H1V6H0V1H4V2H6V3H7V6H6V7Z" class="pattern-color"/>';
bytes constant EAR_PATTERN_PREFIX = '\
\
<g id="ear-pattern" transform="translate(30,10)">';
bytes constant FAIRY_EAR_PATTERN = '\
<path d="M0 5H1V8H0V5ZM1 8H2V10H1V8ZM6 15H5V16H6V15Z"/>\
<path d="M10 3H8V4H1V8H2V10H4V11H5V15H6V16H7V15V11H8V10H9V8H10V4V3Z" class="pattern-color" id="YWR2YW5jZSB8IHJlcGVhdCB8IGZhdm9yaXRl"/>';
bytes constant OWL_EAR_PATTERN = '\
<path d="M6 16H5V15H6V16ZM2 10H1V8H2V10ZM1 8H0V5H1V8Z"/>\
<path d="M7 16H6V15H5V11H4V10H2V8H1V5H2V4H4V3H5V2H6V1H7V0H8V5H7V6H6V7H7V6H8V5H9V8H8V10H7V16Z" class="pattern-color" id="Y2Vuc3VzIHwgZXRlcm5hbCB8IHZlbnVl"/>';
bytes constant BAT_EAR_PATTERN = '\
<path d="M0 5H1V8H0V5ZM1 8H2V10H1V8ZM6 15H5V16H6V15Z"/>\
<path d="M6 0H7V1V3V5V6V8V10V11V15V16H6V15H5V11H4V10H2V8H1V6V5H2V6H3V5H4V3H5V1H6V0Z" class="pattern-color" id="bXl0aCB8IGluY2x1ZGUgfCBzdWJtaXQ="/>';
bytes constant PUP_EAR_PATTERN = '\
<path d="M0 5H1V8H0V5ZM1 8H2V10H1V8ZM6 15H5V16H6V15Z"/>\
<path d="M7 3H4V4H2V5H1V7V8H2V10H4V11H5V15H6V16H7V15V11V10H8V8V7H10V5H9V4H7V3Z" class="pattern-color" id="Zmxhdm9yIHwgamFndWFyIHwgcmF3"/>';
bytes constant MORPH_EAR_PATTERN = '\
<path d="M6 16H5V15H6V16ZM2 10H1V8H2V10ZM1 8H0V5H1V8Z"/>\
<path d="M6 2H7V4H8V10H7V16H6V15H5V11H4V10H2V8H1V4H2V2H1V0H4V1H6V2Z" class="pattern-color"/>';
// GRID COORDINATES
bytes16 constant GRID_X_COORDS = hex"06060606040200000000020404040202";
bytes16 constant GRID_Y_COORDS = hex"06040200000000020406060604020204";
// STRUCTS
struct DNA {
uint256 coloring;
uint256 ears;
uint256 tail;
uint256 pattern;
uint256 eyes;
uint256 gem;
uint256 parentA;
uint256 parentB;
}
// FUNCTIONS
function getDNA(uint256 tokenId) external pure returns (DNA memory) {
DNA memory dna;
// PARENTS
dna.parentA = tokenId / 15;
dna.parentB = tokenId % 15;
dna.parentB = dna.parentA > dna.parentB ? dna.parentB : (((dna.parentB + 1) % 15 == 0) ? 15 : dna.parentB + 1);
// COLORING, PATTERN, EYES, GEM
(dna.coloring, dna.pattern, dna.eyes, dna.gem) =
(dna.parentA == 0) ? (0, 0, 1, 0) : // Milk
(dna.parentA == 1) ? (1, 0, 0, 0) : // Stone
(dna.parentA == 2) ? (2, 0, 0, 1) : // Coyote
(dna.parentA == 3) ? (3, 0, 0, 1) : // Ink
(dna.parentA == 4) ? (4, 1, 2, 2) : // Sky
(dna.parentA == 5) ? (5, 1, 0, 2) : // Blush
(dna.parentA == 6) ? (6, 1, 0, 2) : // Potion
(dna.parentA == 7) ? (7, 1, 0, 1) : // Bear
(dna.parentA == 8) ? (8, 2, 0, 2) : // Martian
(dna.parentA == 9) ? (9, 2, 0, 0) : // Navy
(dna.parentA == 10) ? (10, 2, 3, 0) : // Moon
(dna.parentA == 11) ? (7, 2, 0, 1) : // Bear
(dna.parentA == 12) ? (11, 3, 0, 0) : // Star
(dna.parentA == 13) ? (12, 4, 0, 1) : // Army
(dna.parentA == 14) ? (13, 5, 3, 0) : // Silver
(14, 6, 4, 2); // Cow
// EARS
dna.ears =
(dna.parentB % 4 == 0) ? 0 : // Fairy
(dna.parentB % 4 == 1) ? 1 : // Owl
(dna.parentB % 4 == 2) ? 2 : // Bat
3; // Pup
// TAIL
dna.tail =
(dna.parentB % 16 < 4) ? 0 : // None
(dna.parentB % 16 < 8) ? 1 : // Fox
(dna.parentB % 16 < 12) ? 2 : // Twist
(dna.parentB % 16 == 12) ? 4 : // Twin
3; // Monkey
// MUTATE COLORING
dna.coloring =
(tokenId == 12 || tokenId == 39 || tokenId == 118 || tokenId == 150 || tokenId == 206) ? 15 : // Coral
(tokenId == 19 || tokenId == 59 || tokenId == 85 || tokenId == 215) ? 16 : // Jade
(tokenId == 3 || tokenId == 78 || tokenId == 188) ? 17 : // Fox
(tokenId == 45 || tokenId == 228) ? 18 : // Energy
dna.coloring;
// MUTATE EARS
dna.ears =
(tokenId == 19 || tokenId == 65 || tokenId == 86 || tokenId == 113 || tokenId == 174 || tokenId == 226) ? 4 : // Cat
(tokenId == 13 || tokenId == 121 || tokenId == 164 || tokenId == 198 || tokenId == 221) ? 5 : // Imp
(tokenId == 35 || tokenId == 148 || tokenId == 194) ? 6 : // Pantheon
(tokenId == 45 || tokenId == 98) ? 7 : // Morph
dna.ears;
// MUTATE PATTERN
dna.pattern =
(tokenId == 60 || tokenId == 108 || tokenId == 122 || tokenId == 226) ? 7 : // Pure
(tokenId == 21 || tokenId == 33 || tokenId == 140 || tokenId == 216) ? 8 : // Core
(tokenId == 84 || tokenId == 97 || tokenId == 191) ? 9 : // Mime
(tokenId == 38) ? 10 : // Mime-Pure
(tokenId == 26) ? 11 : // Racer
(tokenId == 177) ? 12 : // Socks
(tokenId == 163) ? 13 : // Collar
(tokenId == 44) ? 14 : // Blot
(tokenId == 67 || tokenId == 82 || tokenId == 91 || tokenId == 113) ? 15 : // Flipped
(tokenId == 164) ? 16 : // Flipped-Mime
(tokenId == 195) ? 17 : // Flipped-Socks
(tokenId == 3 || tokenId == 65 || tokenId == 79 || tokenId == 92 || tokenId == 119 || tokenId == 131 || tokenId == 135 || tokenId == 156 || tokenId == 178 || tokenId == 190 || tokenId == 235) ? 18 : // Bandit
(tokenId == 23 || tokenId == 37) ? 19 : // Bandit-Pure
(tokenId == 196) ? 20 : // Bandit-Socks
(tokenId == 217) ? 21 : // Bandit-Collar
(tokenId == 228) ? 22 : // Bandit-Separator
(tokenId == 12 || tokenId == 70 || tokenId == 83 || tokenId == 101 || tokenId == 105 || tokenId == 123 || tokenId == 148 || tokenId == 151 || tokenId == 172 || tokenId == 192 || tokenId == 238) ? 23 : // Cheetah
(tokenId == 28 || tokenId == 32) ? 24 : // Cheetah-Pure
(tokenId == 203) ? 25 : // Cheetah-Socks
(tokenId == 220) ? 26 : // Cheetah-Collar
(tokenId == 45 || tokenId == 61 || tokenId == 89 || tokenId == 96 || tokenId == 114 || tokenId == 145 || tokenId == 161 || tokenId == 173 || tokenId == 180 || tokenId == 201 || tokenId == 229) ? 27 : // Vitiligo
(tokenId == 16 || tokenId == 34 || tokenId == 52) ? 28 : // Vitiligo-Pure
(tokenId == 223) ? 29 : // Vitiligo-Collar
(tokenId == 128) ? 30 : // Vitiligo-Separator
(tokenId == 18 || tokenId == 43 || tokenId == 59 || tokenId == 71 || tokenId == 77 || tokenId == 98 || tokenId == 110) ? 31 : // Separator
(tokenId == 126 || tokenId == 142 || tokenId == 160 || tokenId == 166) ? 32 : // Separator-Mime
(tokenId == 187) ? 33 : // Separator-Racer
(tokenId == 209) ? 34 : // Separator-Socks
(tokenId == 224) ? 35 : // Separator-Collar
dna.pattern;
// MUTATE EYES
dna.eyes =
(tokenId == 1 || tokenId == 3 || tokenId == 12 || tokenId == 150 || tokenId == 158 || tokenId == 160 || tokenId == 210 || tokenId == 215 || tokenId == 219 || tokenId == 228 || tokenId == 230 || tokenId == 236) ? 5 : // Yellow
(tokenId == 14 || tokenId == 46 || tokenId == 136 || tokenId == 227 || tokenId == 232) ? 6 : // Blue
(tokenId == 17 || tokenId == 20 || tokenId == 49 || tokenId == 58 || tokenId == 87 || tokenId == 99 || tokenId == 101 || tokenId == 137 || tokenId == 147 ||tokenId == 181 || tokenId == 186 || tokenId == 197 || tokenId == 207) ? 7 : // Gray
(tokenId == 7 || tokenId == 55 || tokenId == 134 || tokenId == 198) ? 8 : // Green
dna.eyes;
// SWITCH GEM
dna.gem =
(tokenId == 78) ? 0 : // Sapphire
(tokenId == 12 || tokenId == 19 || tokenId == 85 || tokenId == 150 || tokenId == 215) ? 1 : // Emerald
(tokenId == 45 || tokenId == 228) ? 2 : // Ruby
dna.gem;
return dna;
}
function generateSVG(DNA memory dna) external pure returns (string memory) {
bytes memory grid = _generateGrid(1, dna.parentA);
bytes memory grid2 = _generateGrid(2, dna.parentB);
(string memory baseColor, string memory patternColor) = _getCoatColors(dna.coloring, dna.pattern);
string memory eyeColor = _getEyeColor(dna.eyes);
string memory gemColor = _getGemColor(dna.gem);
bytes memory tail = _getTail(dna.tail);
bytes memory pattern = _getPattern(dna.tail, dna.ears, dna.pattern);
bytes memory ears = _getEars(dna.ears);
bytes memory classes = abi.encodePacked(
'\
<defs>',
'\
<style>',
'\
.shadow { fill: #000; fill-opacity: 0.25; }',
'\
.base-color { fill: ', baseColor, '; }',
'\
.pattern-color { fill: ', patternColor, '; }',
'\
.eye-color { fill: ', eyeColor, '; }',
'\
.gem-color { fill: ', gemColor, '; }',
'\
</style>',
'\
</defs>'
);
return string(abi.encodePacked(
HEADER,
classes,
SCENE,
grid,
grid2,
tail,
BODY,
ears,
pattern,
EYES,
GEM,
'\
</svg>'
));
}
function _generateGrid(uint256 parent, uint256 parentTokenId) internal pure returns (bytes memory) {
bytes memory result = abi.encodePacked('\
\
<g id="grid', bytes(parent.toString()), '" transform="translate(', ((parent == 1) ? '4' : '41'), ',4)" fill="', INK, '">');
for (uint256 i = 0; i < 16; i++) {
if (i != parentTokenId) {
uint256 x;
uint256 y;
assembly {
x := byte(i, GRID_X_COORDS)
y := byte(i, GRID_Y_COORDS)
}
result = abi.encodePacked(
result,
'\
<rect x="', x.toString(), '" y="', y.toString(), '" width="1" height="1"/>'
);
}
}
result = abi.encodePacked(result, '\
</g>');
return result;
}
function _getTail(uint256 tailDNA) internal pure returns (bytes memory) {
bytes memory tail =
(tailDNA == 0) ? bytes("") :
(tailDNA == 1) ? FOX_TAIL :
(tailDNA == 2) ? TWIST_TAIL :
MONKEY_TAIL;
tail = tail.length != 0 ? abi.encodePacked(TAIL_PREFIX,tail, '\
</g>') : tail;
bytes memory tail2 = (tailDNA == 4) ? abi.encodePacked(TAIL_2_PREFIX, MONKEY_TAIL, '\
</g>') : bytes("");
return abi.encodePacked(
tail,
tail2
);
}
function _getEars(uint256 earDNA) internal pure returns (bytes memory) {
bytes memory ears =
(earDNA == 0) ? FAIRY_EARS :
(earDNA == 1) ? OWL_EARS :
(earDNA == 2) ? BAT_EARS :
(earDNA == 3) ? PUP_EARS :
(earDNA == 4) ? CAT_EARS :
(earDNA == 5) ? IMP_EARS :
(earDNA == 6) ? PANTHEON_EARS :
MORPH_EARS;
ears = abi.encodePacked(EARS_PREFIX, ears, '\
</g>');
return ears;
}
function _getPattern(uint256 tailDNA, uint256 earDNA, uint256 patternDNA) internal pure returns (bytes memory) {
bytes memory tail =
(patternDNA == 5 || patternDNA == 13 || patternDNA == 21 || patternDNA == 26 || patternDNA == 29 || patternDNA == 35) ?
((tailDNA == 0) ? bytes("") :
(tailDNA == 1) ? COLLAR_FOX_TAIL_PATTERN :
(tailDNA == 2) ? COLLAR_TWIST_TAIL_PATTERN :
COLLAR_MONKEY_TAIL_PATTERN) :
bytes("");
tail = tail.length != 0 ? abi.encodePacked(TAIL_PATTERN_PREFIX, tail, '\
</g>') : bytes("");
bytes memory tail2 = (tailDNA == 4 && patternDNA == 5) ? abi.encodePacked(TAIL_2_PATTERN_PREFIX, COLLAR_MONKEY_TAIL_PATTERN, '\
</g>') : bytes("");
bytes memory ear =
(patternDNA == 6 || patternDNA == 14 ||patternDNA == 27 || patternDNA == 28 || patternDNA == 29 || patternDNA == 30) ?
(earDNA == 0) ? FAIRY_EAR_PATTERN :
(earDNA == 1) ? OWL_EAR_PATTERN :
(earDNA == 2) ? BAT_EAR_PATTERN :
(earDNA == 3) ? PUP_EAR_PATTERN :
MORPH_EAR_PATTERN :
bytes("");
ear = ear.length != 0 ? abi.encodePacked(EAR_PATTERN_PREFIX, ear, '\
</g>') : bytes("");
bytes memory face =
(patternDNA == 2 || patternDNA == 9 || patternDNA == 10 || patternDNA == 16 || patternDNA == 32) ? MIME_FACE_PATTERN :
(patternDNA == 3 || patternDNA == 11 || patternDNA == 33) ? RACER_FACE_PATTERN :
(patternDNA == 18 || patternDNA == 19 || patternDNA == 20 || patternDNA == 21 || patternDNA == 22) ? BANDIT_FACE_PATTERN :
(patternDNA == 23 || patternDNA == 24 || patternDNA == 25 || patternDNA == 26) ? CHEETAH_FACE_PATTERN :
(patternDNA == 27 || patternDNA == 28 || patternDNA == 29 || patternDNA == 30) ? VITILIGO_FACE_PATTERN :
bytes("");
face = face.length != 0 ? abi.encodePacked(FACE_PATTERN_PREFIX, face, '\
</g>') : bytes("");
bytes memory neck = (patternDNA == 5 || patternDNA == 13 || patternDNA == 21 || patternDNA == 26 || patternDNA == 29) ? COLLAR_NECK_PATTERN : bytes("");
bytes memory stomach =
(patternDNA == 1 || patternDNA == 2 || patternDNA == 3 || patternDNA == 4 || patternDNA == 6 || patternDNA == 8 || patternDNA == 9 || patternDNA == 11 || patternDNA == 12 || patternDNA == 14 || patternDNA == 15 || patternDNA == 16 || patternDNA == 17 || patternDNA == 18 || patternDNA == 20 || patternDNA == 23 || patternDNA == 25 || patternDNA == 27) ? STOMACH_PATTERN :
(patternDNA == 22 || patternDNA == 30 || patternDNA == 31 || patternDNA == 32 || patternDNA == 33 || patternDNA == 34 || patternDNA == 35) ? SEPARATOR_STOMACH_PATTERN :
bytes("");
stomach = stomach.length != 0 ? abi.encodePacked(STOMACH_PATTERN_PREFIX, stomach, '\
</g>') : bytes("");
bytes memory foot =
(patternDNA == 4 || patternDNA == 12 || patternDNA == 17 || patternDNA == 20 || patternDNA == 25 || patternDNA == 34) ? SOCKS_FOOT_PATTERN :
(patternDNA == 27 || patternDNA == 28 || patternDNA == 29 || patternDNA == 30) ? VITILIGO_FOOT_PATTERN :
bytes("");
foot = foot.length != 0 ? abi.encodePacked(FOOT_PATTERN_PREFIX, foot, '\
</g>') : bytes("");
return abi.encodePacked(
tail,
tail2,
ear,
face,
neck,
stomach,
foot
);
}
function _getCoatColors(uint256 dnaColoring, uint256 dnaPattern) internal pure returns (string memory base, string memory pattern) {
(string memory baseColor, string memory patternColor) =
(dnaColoring == 0) ? ("#FFF", "#FFF") : // Milk
(dnaColoring == 1 || dnaColoring == 13) ? (STONE, GRAY) : // Stone & Silver
(dnaColoring == 2) ? (COYOTE, (dnaPattern == 14 ? COYOTE_DARK : COYOTE_LIGHT)) : // Coyote
(dnaColoring == 3) ? (INK, "#FFF") : // Ink
(dnaColoring == 4) ? (SKY, "#FFF") : // Sky
(dnaColoring == 5) ? (BLUSH, "#FFF") : // Blush
(dnaColoring == 6) ? (POTION, POTION_LIGHT) : // Potion
(dnaColoring == 7) ? (BEAR, COYOTE) : // Bear
(dnaColoring == 8) ? (MARTIAN, MARTIAN_LIGHT) : // Martian
(dnaColoring == 9) ? (NAVY, NAVY_LIGHT) : // Navy
(dnaColoring == 10) ? (INK, STONE) : // Moon
(dnaColoring == 11) ? (STAR, "#FFF") : // Star
(dnaColoring == 12) ? (ARMY, ARMY_LIGHT) : // Army
(dnaColoring == 14) ? ("#FFF", INK) : // Cow
(dnaColoring == 15) ? (CORAL, CORAL_LIGHT) : // Coral
(dnaColoring == 16) ? (JADE, STAR) : // Jade
(dnaColoring == 17) ? (FOX, "#FFF") : // Fox
(ENERGY, "#FFF"); // Energy
return ((dnaPattern == 15 || dnaPattern == 16 || dnaPattern == 17) ? (patternColor, baseColor) : (baseColor, patternColor)); // If flipped, return colors in reverse
}
function _getEyeColor(uint256 eyeDNA) internal pure returns (string memory) {
return (eyeDNA == 0 || eyeDNA == 5) ? YELLOW :
(eyeDNA == 1) ? SKY :
(eyeDNA == 2 || eyeDNA == 6) ? BLUE :
(eyeDNA == 3 || eyeDNA == 7) ? GRAY :
GREEN;
}
function _getGemColor(uint256 gemDNA) internal pure returns (string memory) {
return (gemDNA == 0) ? SAPPHIRE :
(gemDNA == 1) ? EMERALD :
RUBY;
}
}"
},
"lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "./IERC721.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {ERC721Utils} from "./utils/ERC721Utils.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC-721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
mapping(uint256 tokenId => address) private _owners;
mapping(address owner => uint256) private _balances;
mapping(uint256 tokenId => address) private _tokenApprovals;
mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/// @inheritdoc IERC721
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
/// @inheritdoc IERC721
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/// @inheritdoc IERC721Metadata
function name() public view virtual returns (string memory) {
return _name;
}
/// @inheritdoc IERC721Metadata
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/// @inheritdoc IERC721Metadata
function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/// @inheritdoc IERC721
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/// @inheritdoc IERC721
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/// @inheritdoc IERC721
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/// @inheritdoc IERC721
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
/// @inheritdoc IERC721
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/// @inheritdoc IERC721
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/// @inheritdoc IERC721
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
* core ERC-721 logic MUST be matched with the use of {_increaseBalance} to keep balances
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
*/
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
/**
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
* particular (ignoring whether it is owned by `owner`).
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
}
/**
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
* Reverts if:
* - `spender` does not have approval from `owner` for `tokenId`.
* - `spender` does not have approval to manage all of `owner`'s assets.
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
*
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
* remain consistent with one another.
*/
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
/**
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that
* `auth` is either the owner of the token, or approved to operate on the token (by the owner).
*
* Emits a {Transfer} event.
*
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_approve(address(0), tokenId, address(0), false);
unchecked {
_balances[from] -= 1;
}
}
if (to != address(0)) {
unchecked {
_balances[to] += 1;
}
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
return from;
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
ERC721Utils.checkOnERC721Received(_msgSender(), address(0), to, tokenId, data);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
* are aware of the ERC-721 standard to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is like {safeTransferFrom} in the sense that it invokes
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `tokenId` token must exist and be owned by `from`.
* - `to` cannot be the zero address.
* - `from` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
* either the owner of the token, or approved to operate on all tokens held by this owner.
*
* Emits an {Approval} event.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
/**
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
* emitted in the context of transfers.
*/
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Requirements:
* - operator can't be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
* Returns the owner.
*
* Overrides to ownership logic should be done to {_ownerOf}.
*/
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
}
"
},
"lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract
Submitted on: 2025-10-18 19:31:41
Comments
Log in to comment.
No comments yet.