文件 1 的 1:Heroes.sol
pragma solidity 0.8.6;
interface IMirrorWriteRaceOracle {
function verify(
address account,
uint256 index,
bytes32[] calldata merkleProof
) external returns (bool);
}
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
contract Heroes {
string public constant name = "Heroes";
string public constant symbol = "HEROES";
address immutable oracle;
mapping(address => bool) public claimed;
uint256 nextTokenId = 1;
string[] private firstNames = [
"Orie",
"Guadalupe",
"Nyx",
"Gertrude",
"Queenie",
"Nathaniel",
"Joyce",
"Claudine",
"Olin",
"Aeneas",
"Elige",
"Jackson",
"Euclid",
"Myrtie",
"Turner",
"Neal",
"Wilmer",
"Nat",
"Euna",
"Aline",
"Iris",
"Sofia",
"Morpheus",
"Curtis",
"Claire",
"Apinya",
"Lefteris",
"Alice",
"Hector",
"Malee",
"Geo",
"Murry",
"Anastasia",
"Kahlil",
"Paris",
"Noble",
"Clara",
"Besse",
"Wilhelmina",
"Napoleon",
"Phillip",
"Isaiah",
"Alexander",
"Lea",
"Verner",
"Verla",
"Beatrice",
"Willie",
"William",
"Elvira",
"Mildred",
"Sula",
"Dido",
"Adaline",
"Jean",
"Inez",
"Reta",
"Isidore",
"Liza",
"Rollin",
"Beverly",
"Theron",
"Moses",
"Abbie",
"Emanuel",
"Buck",
"Alphonso",
"Everett",
"Ruth",
"Easter",
"Cecil",
"Ivy",
"Mariah",
"Lottie",
"Barney",
"Adeline",
"Hazel",
"Sterling",
"Kathrine",
"Mina",
"Eva",
"Francisco",
"Neva",
"Myrle",
"Hector",
"Velva",
"Dewey",
"Manda",
"Mathilda",
"Pallas",
"Zollie",
"Lella",
"Hiram",
"Orval",
"Marcia",
"Leda",
"Patricia",
"Ellie",
"Riley",
"Evie",
"Zelia",
"Leota",
"Camilla",
"Mat",
"Helen",
"Letha",
"Thomas",
"Osie",
"Stella",
"Bernice",
"Daisy",
"Hosea",
"Frederick",
"Reese",
"Adah",
"Nettie",
"Wade",
"Hugo",
"Sipho",
"Ollie",
"Zola",
"Arlie",
"Iyana",
"Webster",
"Rae",
"Alden",
"Juno",
"Luetta",
"Raphael",
"Eura",
"Cupid",
"Priam",
"Kame",
"Louis",
"Hana",
"Lyra",
"Kholo",
"Gunnar",
"Olafur",
"Anatolia",
"Lelia",
"Agatha",
"Helga",
"Rossie",
"Katsu",
"Toku",
"Verdie",
"Nandi",
"Anna",
"Maksim",
"Mihlali",
"Aloysius",
"Mittie",
"Olive",
"Virgie",
"Gregory",
"Leah",
"Maudie",
"Fanny",
"Andres",
"Mava",
"Ines",
"Clovis",
"Clint",
"Scarlett",
"Porter",
"Isabelle",
"Mahlon",
"Elsie",
"Seth",
"Irma",
"Annis",
"Pearle",
"Dumo",
"Lamar",
"Fay",
"Olga",
"Billie",
"Maybelle",
"Santiago",
"Ludie",
"Salvador",
"Adem",
"Emir",
"Hamza",
"Emre"
];
string[] private lastNames = [
"Galway",
"Wheeler",
"Hotty",
"Mae",
"Beale",
"Zabu",
"Robins",
"Farrell",
"Goslan",
"Garnier",
"Tow",
"Chai",
"Seong",
"Ross",
"Barbary",
"Burress",
"McLean",
"Kennedy",
"Murphy",
"Cortez",
"Aku",
"Middlemiss",
"Saxon",
"Dupont",
"Sullivan",
"Hunter",
"Gibb",
"Ali",
"Holmes",
"Griffin",
"Patel",
"Kabble",
"Brown",
"Guillan",
"Thompson",
"Doolan",
"Brownhill",
"de la Mancha",
"Crogan",
"Fitzgerald",
"Flaubert",
"Salander",
"Park",
"Singh",
"Hassan",
"Peri",
"Horgan",
"Tolin",
"Kim",
"Beckham",
"Shackley",
"Lobb",
"Yoon",
"Blanchet",
"Wang",
"Ames",
"Liu",
"Raghavan",
"Morgan",
"Xiao",
"Mills",
"Yang",
"Pabst",
"Duffey",
"Monaghan",
"Bu",
"Teague",
"Obi",
"Abberton",
"Corbin",
"Zhang",
"Kildare",
"Okoro",
"Eze",
"Rovelli",
"Garcia",
"Wareham",
"Sun",
"Langhorne",
"Liu",
"Popov",
"Howlett"
];
string[] private prefixes = [
"President",
"General",
"Captain",
"Dr",
"Professor",
"Chancellor",
"The Honourable",
"Venerable",
"Barrister",
"Prophet",
"Evangelist",
"Senpai",
"Senator",
"Speaker",
"Sama",
"Chief",
"Ambassador",
"Nari",
"Lion-hearted",
"Tireless",
"Poet",
"Beloved",
"Godlike",
"All-Powerful",
"Sweet-spoken",
"Wise Old",
"Peerless",
"Gentle",
"Swift-footed",
"Mysterious",
"Dear",
"Revered",
"Adored"
];
string[] private suffixes = [
"I",
"II",
"III",
"the Thoughtful",
"of the Sword",
"the Illustrious",
"from the North",
"from the South",
"the Younger",
"the Elder",
"the Wise",
"the Mighty",
"the Great",
"the Hero",
"the Adventurer",
"the Beautiful",
"the Conqueror",
"the Courageous",
"the Valiant",
"the Fair",
"the Magnificent",
"the Pious",
"the Just",
"the Peaceful",
"the Rich",
"the Learned",
"the Bold",
"the Giant",
"the Deep-minded",
"the Brilliant",
"the Joyful",
"the Famous",
"the Bard",
"the Knowing",
"the Sophisticated",
"the Enlightened"
];
mapping(uint256 => address) internal _owners;
mapping(address => uint256) internal _balances;
mapping(uint256 => address) internal _tokenApprovals;
mapping(address => mapping(address => bool)) internal _operatorApprovals;
event Transfer(
address indexed from,
address indexed to,
uint256 indexed tokenId
);
event Approval(
address indexed owner,
address indexed approved,
uint256 indexed tokenId
);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
constructor(address oracle_) {
oracle = oracle_;
}
function claim(
address account,
uint256 index,
bytes32[] calldata merkleProof
) public {
require(!claimed[account], "already claimed");
claimed[account] = true;
require(
IMirrorWriteRaceOracle(oracle).verify(account, index, merkleProof),
"must prove oracle"
);
_safeMint(account, nextTokenId);
nextTokenId += 1;
}
function tokenURI(uint256 tokenId) public view returns (string memory) {
require(_exists(tokenId), "nonexistent token");
string[3] memory parts;
parts[
0
] = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: white; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="base">';
parts[1] = getFullName(tokenId);
parts[2] = "</text></svg>";
string memory output = string(
abi.encodePacked(parts[0], parts[1], parts[2])
);
string memory json = Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "Hero #',
toString(tokenId),
'", "description": "Heroes", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(output)),
'"}'
)
)
)
);
output = string(
abi.encodePacked("data:application/json;base64,", json)
);
return output;
}
function getFullName(uint256 tokenId) public view returns (string memory) {
require(_exists(tokenId), "nonexistent token");
uint256 randFirst = random(
string(abi.encodePacked("f", toString(tokenId)))
);
uint256 randLast = random(
string(abi.encodePacked("l", toString(tokenId)))
);
uint256 randPrefix = random(
string(abi.encodePacked("p", toString(tokenId)))
);
uint256 randSuffix = random(
string(abi.encodePacked("s", toString(tokenId)))
);
bool hasPrefix = randPrefix % 21 > 13;
bool hasSuffix = randSuffix % 21 > 13;
string memory fullName = string(
abi.encodePacked(
firstNames[randFirst % firstNames.length],
" ",
lastNames[randLast % lastNames.length]
)
);
if (hasPrefix) {
fullName = string(
abi.encodePacked(
prefixes[randPrefix % prefixes.length],
" ",
fullName
)
);
}
if (hasSuffix) {
fullName = string(
abi.encodePacked(
fullName,
" ",
suffixes[randSuffix % suffixes.length]
)
);
}
return fullName;
}
function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
return interfaceId == 0x780e9d63;
}
function balanceOf(address owner_) public view returns (uint256) {
require(owner_ != address(0), "balance query for the zero address");
return _balances[owner_];
}
function ownerOf(uint256 tokenId) public view virtual returns (address) {
address _owner = _owners[tokenId];
require(_owner != address(0), "owner query for nonexistent token");
return _owner;
}
function burn(uint256 tokenId) public {
require(
_isApprovedOrOwner(msg.sender, tokenId),
"transfer caller is not owner nor approved"
);
_burn(tokenId);
}
function _exists(uint256 tokenId) internal view returns (bool) {
return _owners[tokenId] != address(0);
}
function _burn(uint256 tokenId) internal {
address owner_ = ownerOf(tokenId);
_approve(address(0), tokenId);
_balances[owner_] -= 1;
delete _owners[tokenId];
emit Transfer(owner_, address(0), tokenId);
}
function approve(address to, uint256 tokenId) public virtual {
address owner = ownerOf(tokenId);
require(to != owner, "approval to current owner");
require(
msg.sender == owner || isApprovedForAll(owner, msg.sender),
"approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
function getApproved(uint256 tokenId)
public
view
virtual
returns (address)
{
require(_exists(tokenId), "nonexistent token");
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address approver, bool approved) public virtual {
require(approver != msg.sender, "approve to caller");
_operatorApprovals[msg.sender][approver] = approved;
emit ApprovalForAll(msg.sender, approver, approved);
}
function isApprovedForAll(address owner, address operator)
public
view
returns (bool)
{
return _operatorApprovals[owner][operator];
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual {
require(
_isApprovedOrOwner(msg.sender, tokenId),
"transfer caller is not owner nor approved"
);
_transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public virtual {
require(
_isApprovedOrOwner(msg.sender, tokenId),
"transfer caller is not owner nor approved"
);
_safeTransfer(from, to, tokenId, _data);
}
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_transfer(from, to, tokenId);
require(
_checkOnERC721Received(from, to, tokenId, _data),
"transfer to non ERC721Receiver implementer"
);
}
function _isApprovedOrOwner(address spender, uint256 tokenId)
internal
view
virtual
returns (bool)
{
require(_exists(tokenId), "operator query for nonexistent token");
address owner = ownerOf(tokenId);
return (spender == owner ||
getApproved(tokenId) == spender ||
isApprovedForAll(owner, spender));
}
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
function _safeMint(
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, _data),
"transfer to non ERC721Receiver"
);
}
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "mint to the zero address");
require(!_exists(tokenId), "token already minted");
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
function _transfer(
address from,
address to,
uint256 tokenId
) internal virtual {
require(ownerOf(tokenId) == from, "transfer of token that is not own");
require(
to != address(0),
"transfer to the zero address (use burn instead)"
);
_approve(address(0), tokenId);
_balances[from] -= 1;
_owners[tokenId] = to;
_balances[to] += 1;
emit Transfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ownerOf(tokenId), to, tokenId);
}
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
if (isContract(to)) {
try
IERC721Receiver(to).onERC721Received(
msg.sender,
from,
tokenId,
_data
)
returns (bytes4 retval) {
return retval == IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("transfer to non ERC721Receiver implementer");
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function random(string memory input) internal pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(input)));
}
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
library Base64 {
bytes internal constant TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function encode(bytes memory data) internal pure returns (string memory) {
uint256 len = data.length;
if (len == 0) return "";
uint256 encodedLen = 4 * ((len + 2) / 3);
bytes memory result = new bytes(encodedLen + 32);
bytes memory table = TABLE;
assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)
for {
let i := 0
} lt(i, len) {
} {
i := add(i, 3)
let input := and(mload(add(data, i)), 0xffffff)
let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF)
)
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF)
)
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(input, 0x3F))), 0xFF)
)
out := shl(224, out)
mstore(resultPtr, out)
resultPtr := add(resultPtr, 4)
}
switch mod(len, 3)
case 1 {
mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
}
case 2 {
mstore(sub(resultPtr, 1), shl(248, 0x3d))
}
mstore(result, encodedLen)
}
return string(result);
}
}