文件 1 的 13:address-utils.sol
pragma solidity ^0.8.4;
library AddressUtils
{
function isContract(
address _addr
)
internal
view
returns (bool addressCheck)
{
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
assembly { codehash := extcodehash(_addr) }
addressCheck = (codehash != 0x0 && codehash != accountHash);
}
}
文件 2 的 13:cmk-utils.sol
pragma solidity ^0.8.4;
contract CmkUtils {
function getMin(uint256 a,uint256 b) internal pure returns(uint256){
if(a>b) return b;
else return a;
}
function uint2str(uint256 _i) internal pure returns (string memory str) {
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length;
j = _i;
while (j != 0) {
bstr[--k] = bytes1(uint8(48 + (j % 10)));
j /= 10;
}
str = string(bstr);
}
function strConcat(string memory _a, string memory _b)
internal
pure
returns (string memory _concatenatedString)
{
return strConcat(_a, _b, "", "", "");
}
function strConcat(
string memory _a,
string memory _b,
string memory _c
) internal pure returns (string memory _concatenatedString) {
return strConcat(_a, _b, _c, "", "");
}
function strConcat(
string memory _a,
string memory _b,
string memory _c,
string memory _d
) internal pure returns (string memory _concatenatedString) {
return strConcat(_a, _b, _c, _d, "");
}
function strConcat(
string memory _a,
string memory _b,
string memory _c,
string memory _d,
string memory _e
) internal pure returns (string memory _concatenatedString) {
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
bytes memory _bc = bytes(_c);
bytes memory _bd = bytes(_d);
bytes memory _be = bytes(_e);
string memory abcde = new string(
_ba.length + _bb.length + _bc.length + _bd.length + _be.length
);
bytes memory babcde = bytes(abcde);
uint256 k = 0;
uint256 i = 0;
for (i = 0; i < _ba.length; i++) {
babcde[k++] = _ba[i];
}
for (i = 0; i < _bb.length; i++) {
babcde[k++] = _bb[i];
}
for (i = 0; i < _bc.length; i++) {
babcde[k++] = _bc[i];
}
for (i = 0; i < _bd.length; i++) {
babcde[k++] = _bd[i];
}
for (i = 0; i < _be.length; i++) {
babcde[k++] = _be[i];
}
return string(babcde);
}
}
文件 3 的 13:crypto-monkey-king.sol
pragma solidity ^0.8.4;
pragma experimental ABIEncoderV2;
import "./erc721-metadata.sol";
import "./nf-token-enumerable.sol";
import "./ownable.sol";
import "./address-utils.sol";
import "./cmk-utils.sol";
import "./datetime-library.sol";
contract CryptoMonkeyKing is
NFTokenEnumerable,
Ownable,
CmkUtils,
ERC721Metadata
{
using AddressUtils for address;
struct NFT {
uint256 id;
bool isSelling;
uint256 priceInWei;
address owner;
}
struct MintedInfo {
uint256 lastYear;
uint256 lastMonth;
uint256 lastDay;
uint256 lastCount;
uint256 minted;
}
string constant NOT_VALID_NFT_ID = "004001";
string constant NOT_RESERVE = "004002";
string constant NOT_NORMAL = "004003";
string constant IN_SELLING = "004004";
string constant NO_SELLING = "004005";
string constant NFT_HAS_NO_OWNER = "004006";
string constant NFT_HAS_OWNER = "004007";
string constant IS_NFT_OWNER = "004008";
string constant NOT_NFT_OWNER = "004009";
string constant NOT_AIRDROP_ACCOUNT = "004010";
string constant NOT_ENOUGH_BALANCE = "004011";
string constant OUT_OF_MAX_PAGE_SIZE = "004012";
string constant OUT_OF_MINT_MAX_PER_TIME = "004013";
string constant OUT_OF_MINT_MAX_PER_DAY = "004014";
string constant SENDER_MINTED = "004015";
string private baseURI;
address private airdropAccount;
uint256 private maxSupply;
uint256 private maxPageSize;
uint256 private maxMintPerTime;
uint256 private mintMaxPerDay;
uint256 private mintPrice;
MintedInfo mintedInfo;
mapping(address => bool) private addressToMinted;
modifier onlyAirdrop() {
require(msg.sender == airdropAccount, NOT_AIRDROP_ACCOUNT);
_;
}
modifier validNFTId(uint256 _id) {
require(_id >= 0 && _id <= maxSupply - 1, NOT_VALID_NFT_ID);
_;
}
function isReserve(uint256 _id) internal pure returns (bool) {
return _id >= 7000 && _id < 10000;
}
function isNormal(uint256 _id) internal pure returns (bool) {
return _id >= 0 && _id < 7000;
}
constructor() {
baseURI = "https://cryptomonkeyking.com/token";
airdropAccount = 0x251d42b900973eD14D7Ea82Ce9310D34534f75EC;
maxSupply = 10000;
maxPageSize = 1000;
maxMintPerTime = 5;
mintMaxPerDay = 200;
}
function name() external pure override returns (string memory _name) {
return "Crypto Monkey King";
}
function symbol() external pure override returns (string memory _symbol) {
return "CMK";
}
function tokenURI(uint256 _id)
external
view
override
returns (string memory)
{
return strConcat(baseURI, "/", uint2str(_id));
}
function setBaseURI(string memory _baseURI) public onlyOwner {
baseURI = _baseURI;
}
function getBaseURI() public view onlyOwner returns (string memory) {
return baseURI;
}
function setAirdropAccount(address _airdropAccount) public onlyOwner {
airdropAccount = _airdropAccount;
}
function getAirdropAccount() public view onlyOwner returns (address) {
return airdropAccount;
}
function setMintPrice(uint256 _mintPrice) public onlyOwner {
mintPrice = _mintPrice;
}
function getMintPrice() public view returns (uint256) {
return mintPrice;
}
function setMaxMintPerTime(uint256 _maxMintPerTime) public onlyOwner {
maxMintPerTime = _maxMintPerTime;
}
function getMaxMintPerTime() public view returns (uint256) {
return maxMintPerTime;
}
function setMintMaxPerDay(uint256 _mintMaxPerDay) public onlyOwner {
mintMaxPerDay = _mintMaxPerDay;
}
function getMintMaxPerDay() public view returns (uint256) {
return mintMaxPerDay;
}
function getMaxSupply() public view returns (uint256) {
return maxSupply;
}
function getMaxPageSize() public view returns (uint256) {
return maxPageSize;
}
function getMintableRemaining() public view returns (uint256) {
return 7000 - mintedInfo.minted;
}
function getPagedNFTs(uint256 pageSize, uint256 pageIndex)
public
view
returns (NFT[] memory)
{
require(pageSize <= maxPageSize, OUT_OF_MAX_PAGE_SIZE);
uint256 start = pageSize * pageIndex;
uint256 end = start + pageSize - 1;
if (start > maxSupply - 1) {
start = maxSupply - 1;
}
if (end > maxSupply - 1) {
end = maxSupply - 1;
}
NFT[] memory NFTs = new NFT[](pageSize);
for (uint256 i = start; i <= end; i++) {
address owner = idToOwner[i];
NFT memory nft = NFT(i, false, 0, owner);
NFTs[i - start] = nft;
}
return NFTs;
}
function getMintedCountToday() public view returns(uint256){
(uint256 year, uint256 month, uint256 day) = BokkyPooBahsDateTimeLibrary
.timestampToDate(block.timestamp);
uint count=0;
if (
mintedInfo.lastYear == year &&
mintedInfo.lastMonth == month &&
mintedInfo.lastDay == day
) {
count = mintedInfo.lastCount;
} else {
count = 0;
}
return count;
}
function getMintableCountToday() public view returns (uint256) {
(uint256 year, uint256 month, uint256 day) = BokkyPooBahsDateTimeLibrary
.timestampToDate(block.timestamp);
uint256 count = 0;
uint256 remainToday = CmkUtils.getMin(7000 - mintedInfo.minted, mintMaxPerDay);
if (
mintedInfo.lastYear == year &&
mintedInfo.lastMonth == month &&
mintedInfo.lastDay == day
) {
count = remainToday - mintedInfo.lastCount;
} else {
count = remainToday;
}
if (count < 0) {
count = 0;
}
return count;
}
function isMinted(address _addr) public view returns (bool) {
return addressToMinted[_addr];
}
function mintNFTs(uint256[] memory _ids) public payable {
require(!addressToMinted[msg.sender], SENDER_MINTED);
require(
_ids.length <= getMintableCountToday(),
OUT_OF_MINT_MAX_PER_DAY
);
require(_ids.length <= maxMintPerTime, OUT_OF_MINT_MAX_PER_TIME);
uint256 total = _ids.length * mintPrice;
require(total <= msg.value, NOT_ENOUGH_BALANCE);
for (uint256 i = 0; i < _ids.length; i++) {
uint256 _id = _ids[i];
require(isNormal(_id), NOT_NORMAL);
require(idToOwner[_id] == address(0), NFT_HAS_OWNER);
_mint(msg.sender, _id);
}
(uint256 year, uint256 month, uint256 day) = BokkyPooBahsDateTimeLibrary
.timestampToDate(block.timestamp);
if (
mintedInfo.lastYear == year &&
mintedInfo.lastMonth == month &&
mintedInfo.lastDay == day
) {
mintedInfo.lastCount += _ids.length;
} else {
mintedInfo.lastCount = _ids.length;
}
mintedInfo.lastYear = year;
mintedInfo.lastMonth = month;
mintedInfo.lastDay = day;
mintedInfo.minted += _ids.length;
addressToMinted[msg.sender] = true;
if (mintPrice > 0) {
payable(owner).transfer(msg.value);
}
}
function airdropNFT(address _to, uint256 _id)
public
onlyAirdrop
validNFTId(_id)
{
require(isReserve(_id), NOT_RESERVE);
require(idToOwner[_id] == address(0), NFT_HAS_OWNER);
_mint(_to, _id);
}
function airdropNFTs(address _to, uint256[] memory _ids) public {
for (uint256 i = 0; i < _ids.length; i++) {
uint256 id = _ids[i];
airdropNFT(_to, id);
}
}
function giveNFT(address _to, uint256 _id) public validNFTId(_id) {
require(this.ownerOf(_id) == msg.sender, NOT_NFT_OWNER);
_transfer(_to, _id);
}
function giveNFTs(address _to, uint256[] memory _ids) public {
for (uint256 i = 0; i < _ids.length; i++) {
uint256 id = _ids[i];
giveNFT(_to, id);
}
}
}
文件 4 的 13:datetime-library.sol
pragma solidity ^0.8.4;
library BokkyPooBahsDateTimeLibrary {
uint constant SECONDS_PER_DAY = 24 * 60 * 60;
uint constant SECONDS_PER_HOUR = 60 * 60;
uint constant SECONDS_PER_MINUTE = 60;
int constant OFFSET19700101 = 2440588;
uint constant DOW_MON = 1;
uint constant DOW_TUE = 2;
uint constant DOW_WED = 3;
uint constant DOW_THU = 4;
uint constant DOW_FRI = 5;
uint constant DOW_SAT = 6;
uint constant DOW_SUN = 7;
function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) {
require(year >= 1970);
int _year = int(year);
int _month = int(month);
int _day = int(day);
int __days = _day
- 32075
+ 1461 * (_year + 4800 + (_month - 14) / 12) / 4
+ 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12
- 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4
- OFFSET19700101;
_days = uint(__days);
}
function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) {
int __days = int(_days);
int L = __days + 68569 + OFFSET19700101;
int N = 4 * L / 146097;
L = L - (146097 * N + 3) / 4;
int _year = 4000 * (L + 1) / 1461001;
L = L - 1461 * _year / 4 + 31;
int _month = 80 * L / 2447;
int _day = L - 2447 * _month / 80;
L = _month / 11;
_month = _month + 2 - 12 * L;
_year = 100 * (N - 49) + _year + L;
year = uint(_year);
month = uint(_month);
day = uint(_day);
}
function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) {
timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
}
function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) {
timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second;
}
function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) {
(year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {
(year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
uint secs = timestamp % SECONDS_PER_DAY;
hour = secs / SECONDS_PER_HOUR;
secs = secs % SECONDS_PER_HOUR;
minute = secs / SECONDS_PER_MINUTE;
second = secs % SECONDS_PER_MINUTE;
}
function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) {
if (year >= 1970 && month > 0 && month <= 12) {
uint daysInMonth = _getDaysInMonth(year, month);
if (day > 0 && day <= daysInMonth) {
valid = true;
}
}
}
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) {
if (isValidDate(year, month, day)) {
if (hour < 24 && minute < 60 && second < 60) {
valid = true;
}
}
}
function isLeapYear(uint timestamp) internal pure returns (bool leapYear) {
(uint year,,) = _daysToDate(timestamp / SECONDS_PER_DAY);
leapYear = _isLeapYear(year);
}
function _isLeapYear(uint year) internal pure returns (bool leapYear) {
leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}
function isWeekDay(uint timestamp) internal pure returns (bool weekDay) {
weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
}
function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) {
weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
}
function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) {
(uint year, uint month,) = _daysToDate(timestamp / SECONDS_PER_DAY);
daysInMonth = _getDaysInMonth(year, month);
}
function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) {
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
daysInMonth = 31;
} else if (month != 2) {
daysInMonth = 30;
} else {
daysInMonth = _isLeapYear(year) ? 29 : 28;
}
}
function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) {
uint _days = timestamp / SECONDS_PER_DAY;
dayOfWeek = (_days + 3) % 7 + 1;
}
function getYear(uint timestamp) internal pure returns (uint year) {
(year,,) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function getMonth(uint timestamp) internal pure returns (uint month) {
(,month,) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function getDay(uint timestamp) internal pure returns (uint day) {
(,,day) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function getHour(uint timestamp) internal pure returns (uint hour) {
uint secs = timestamp % SECONDS_PER_DAY;
hour = secs / SECONDS_PER_HOUR;
}
function getMinute(uint timestamp) internal pure returns (uint minute) {
uint secs = timestamp % SECONDS_PER_HOUR;
minute = secs / SECONDS_PER_MINUTE;
}
function getSecond(uint timestamp) internal pure returns (uint second) {
second = timestamp % SECONDS_PER_MINUTE;
}
function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
year += _years;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp >= timestamp);
}
function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
month += _months;
year += (month - 1) / 12;
month = (month - 1) % 12 + 1;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp >= timestamp);
}
function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _days * SECONDS_PER_DAY;
require(newTimestamp >= timestamp);
}
function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
require(newTimestamp >= timestamp);
}
function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
require(newTimestamp >= timestamp);
}
function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _seconds;
require(newTimestamp >= timestamp);
}
function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
year -= _years;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp <= timestamp);
}
function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
uint yearMonth = year * 12 + (month - 1) - _months;
year = yearMonth / 12;
month = yearMonth % 12 + 1;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp <= timestamp);
}
function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _days * SECONDS_PER_DAY;
require(newTimestamp <= timestamp);
}
function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
require(newTimestamp <= timestamp);
}
function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
require(newTimestamp <= timestamp);
}
function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _seconds;
require(newTimestamp <= timestamp);
}
function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) {
require(fromTimestamp <= toTimestamp);
(uint fromYear,,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
(uint toYear,,) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
_years = toYear - fromYear;
}
function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) {
require(fromTimestamp <= toTimestamp);
(uint fromYear, uint fromMonth,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
(uint toYear, uint toMonth,) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
_months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
}
function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) {
require(fromTimestamp <= toTimestamp);
_days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
}
function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) {
require(fromTimestamp <= toTimestamp);
_hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
}
function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) {
require(fromTimestamp <= toTimestamp);
_minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
}
function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) {
require(fromTimestamp <= toTimestamp);
_seconds = toTimestamp - fromTimestamp;
}
}
文件 5 的 13:erc165.sol
pragma solidity ^0.8.4;
interface ERC165
{
function supportsInterface(
bytes4 _interfaceID
)
external
view
returns (bool);
}
文件 6 的 13:erc721-enumerable.sol
pragma solidity ^0.8.4;
interface ERC721Enumerable
{
function totalSupply()
external
view
returns (uint256);
function tokenByIndex(
uint256 _index
)
external
view
returns (uint256);
function tokenOfOwnerByIndex(
address _owner,
uint256 _index
)
external
view
returns (uint256);
}
文件 7 的 13:erc721-metadata.sol
pragma solidity ^0.8.4;
interface ERC721Metadata
{
function name()
external
view
returns (string memory _name);
function symbol()
external
view
returns (string memory _symbol);
function tokenURI(uint256 _tokenId)
external
view
returns (string memory);
}
文件 8 的 13:erc721-token-receiver.sol
pragma solidity ^0.8.4;
interface ERC721TokenReceiver
{
function onERC721Received(
address _operator,
address _from,
uint256 _tokenId,
bytes calldata _data
)
external
returns(bytes4);
}
文件 9 的 13:erc721.sol
pragma solidity ^0.8.4;
interface ERC721
{
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
);
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
)
external;
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
external;
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
external;
function approve(
address _approved,
uint256 _tokenId
)
external;
function setApprovalForAll(
address _operator,
bool _approved
)
external;
function balanceOf(
address _owner
)
external
view
returns (uint256);
function ownerOf(
uint256 _tokenId
)
external
view
returns (address);
function getApproved(
uint256 _tokenId
)
external
view
returns (address);
function isApprovedForAll(
address _owner,
address _operator
)
external
view
returns (bool);
}
文件 10 的 13:nf-token-enumerable.sol
pragma solidity ^0.8.4;
import "./nf-token.sol";
import "./erc721-enumerable.sol";
contract NFTokenEnumerable is
NFToken,
ERC721Enumerable
{
string constant INVALID_INDEX = "005007";
uint256[] internal tokens;
mapping(uint256 => uint256) internal idToIndex;
mapping(address => uint256[]) internal ownerToIds;
mapping(uint256 => uint256) internal idToOwnerIndex;
constructor()
{
supportedInterfaces[0x780e9d63] = true;
}
function totalSupply()
external
override
view
returns (uint256)
{
return tokens.length;
}
function tokenByIndex(
uint256 _index
)
external
override
view
returns (uint256)
{
require(_index < tokens.length, INVALID_INDEX);
return tokens[_index];
}
function tokenOfOwnerByIndex(
address _owner,
uint256 _index
)
external
override
view
returns (uint256)
{
require(_index < ownerToIds[_owner].length, INVALID_INDEX);
return ownerToIds[_owner][_index];
}
function _mint(
address _to,
uint256 _tokenId
)
internal
override
virtual
{
super._mint(_to, _tokenId);
tokens.push(_tokenId);
idToIndex[_tokenId] = tokens.length - 1;
}
function _burn(
uint256 _tokenId
)
internal
override
virtual
{
super._burn(_tokenId);
uint256 tokenIndex = idToIndex[_tokenId];
uint256 lastTokenIndex = tokens.length - 1;
uint256 lastToken = tokens[lastTokenIndex];
tokens[tokenIndex] = lastToken;
tokens.pop();
idToIndex[lastToken] = tokenIndex;
idToIndex[_tokenId] = 0;
}
function _removeNFToken(
address _from,
uint256 _tokenId
)
internal
override
virtual
{
require(idToOwner[_tokenId] == _from, NOT_OWNER);
delete idToOwner[_tokenId];
uint256 tokenToRemoveIndex = idToOwnerIndex[_tokenId];
uint256 lastTokenIndex = ownerToIds[_from].length - 1;
if (lastTokenIndex != tokenToRemoveIndex)
{
uint256 lastToken = ownerToIds[_from][lastTokenIndex];
ownerToIds[_from][tokenToRemoveIndex] = lastToken;
idToOwnerIndex[lastToken] = tokenToRemoveIndex;
}
ownerToIds[_from].pop();
}
function _addNFToken(
address _to,
uint256 _tokenId
)
internal
override
virtual
{
require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS);
idToOwner[_tokenId] = _to;
ownerToIds[_to].push(_tokenId);
idToOwnerIndex[_tokenId] = ownerToIds[_to].length - 1;
}
function _getOwnerNFTCount(
address _owner
)
internal
override
virtual
view
returns (uint256)
{
return ownerToIds[_owner].length;
}
}
文件 11 的 13:nf-token.sol
pragma solidity ^0.8.4;
import "./erc721.sol";
import "./erc721-token-receiver.sol";
import "./supports-interface.sol";
import "./address-utils.sol";
contract NFToken is
ERC721,
SupportsInterface
{
using AddressUtils for address;
string constant ZERO_ADDRESS = "003001";
string constant NOT_VALID_NFT = "003002";
string constant NOT_OWNER_OR_OPERATOR = "003003";
string constant NOT_OWNER_APPROVED_OR_OPERATOR = "003004";
string constant NOT_ABLE_TO_RECEIVE_NFT = "003005";
string constant NFT_ALREADY_EXISTS = "003006";
string constant NOT_OWNER = "003007";
string constant IS_OWNER = "003008";
bytes4 internal constant MAGIC_ON_ERC721_RECEIVED = 0x150b7a02;
mapping (uint256 => address) internal idToOwner;
mapping (uint256 => address) internal idToApproval;
mapping (address => uint256) private ownerToNFTokenCount;
mapping (address => mapping (address => bool)) internal ownerToOperators;
modifier canOperate(
uint256 _tokenId
)
{
address tokenOwner = idToOwner[_tokenId];
require(
tokenOwner == msg.sender || ownerToOperators[tokenOwner][msg.sender],
NOT_OWNER_OR_OPERATOR
);
_;
}
modifier canTransfer(
uint256 _tokenId
)
{
address tokenOwner = idToOwner[_tokenId];
require(
tokenOwner == msg.sender
|| idToApproval[_tokenId] == msg.sender
|| ownerToOperators[tokenOwner][msg.sender],
NOT_OWNER_APPROVED_OR_OPERATOR
);
_;
}
modifier validNFToken(
uint256 _tokenId
)
{
require(idToOwner[_tokenId] != address(0), NOT_VALID_NFT);
_;
}
constructor()
{
supportedInterfaces[0x80ac58cd] = true;
}
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
)
external
override
{
_safeTransferFrom(_from, _to, _tokenId, _data);
}
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
external
override
{
_safeTransferFrom(_from, _to, _tokenId, "");
}
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
external
override
canTransfer(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from, NOT_OWNER);
require(_to != address(0), ZERO_ADDRESS);
_transfer(_to, _tokenId);
}
function approve(
address _approved,
uint256 _tokenId
)
external
override
canOperate(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(_approved != tokenOwner, IS_OWNER);
idToApproval[_tokenId] = _approved;
emit Approval(tokenOwner, _approved, _tokenId);
}
function setApprovalForAll(
address _operator,
bool _approved
)
external
override
{
ownerToOperators[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function balanceOf(
address _owner
)
external
override
view
returns (uint256)
{
require(_owner != address(0), ZERO_ADDRESS);
return _getOwnerNFTCount(_owner);
}
function ownerOf(
uint256 _tokenId
)
external
override
view
returns (address _owner)
{
_owner = idToOwner[_tokenId];
require(_owner != address(0), NOT_VALID_NFT);
}
function getApproved(
uint256 _tokenId
)
external
override
view
validNFToken(_tokenId)
returns (address)
{
return idToApproval[_tokenId];
}
function isApprovedForAll(
address _owner,
address _operator
)
external
override
view
returns (bool)
{
return ownerToOperators[_owner][_operator];
}
function _transfer(
address _to,
uint256 _tokenId
)
internal
{
address from = idToOwner[_tokenId];
_clearApproval(_tokenId);
_removeNFToken(from, _tokenId);
_addNFToken(_to, _tokenId);
emit Transfer(from, _to, _tokenId);
}
function _mint(
address _to,
uint256 _tokenId
)
internal
virtual
{
require(_to != address(0), ZERO_ADDRESS);
require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS);
_addNFToken(_to, _tokenId);
emit Transfer(address(0), _to, _tokenId);
}
function _burn(
uint256 _tokenId
)
internal
virtual
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
_clearApproval(_tokenId);
_removeNFToken(tokenOwner, _tokenId);
emit Transfer(tokenOwner, address(0), _tokenId);
}
function _removeNFToken(
address _from,
uint256 _tokenId
)
internal
virtual
{
require(idToOwner[_tokenId] == _from, NOT_OWNER);
ownerToNFTokenCount[_from] = ownerToNFTokenCount[_from] - 1;
delete idToOwner[_tokenId];
}
function _addNFToken(
address _to,
uint256 _tokenId
)
internal
virtual
{
require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS);
idToOwner[_tokenId] = _to;
ownerToNFTokenCount[_to] = ownerToNFTokenCount[_to] + 1;
}
function _getOwnerNFTCount(
address _owner
)
internal
virtual
view
returns (uint256)
{
return ownerToNFTokenCount[_owner];
}
function _safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes memory _data
)
private
canTransfer(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from, NOT_OWNER);
require(_to != address(0), ZERO_ADDRESS);
_transfer(_to, _tokenId);
if (_to.isContract())
{
bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
require(retval == MAGIC_ON_ERC721_RECEIVED, NOT_ABLE_TO_RECEIVE_NFT);
}
}
function _clearApproval(
uint256 _tokenId
)
private
{
if (idToApproval[_tokenId] != address(0))
{
delete idToApproval[_tokenId];
}
}
}
文件 12 的 13:ownable.sol
pragma solidity ^0.8.4;
contract Ownable
{
string public constant NOT_CURRENT_OWNER = "018001";
string public constant CANNOT_TRANSFER_TO_ZERO_ADDRESS = "018002";
address public owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
constructor()
{
owner = msg.sender;
}
modifier onlyOwner()
{
require(msg.sender == owner, NOT_CURRENT_OWNER);
_;
}
function transferOwnership(
address _newOwner
)
public
onlyOwner
{
require(_newOwner != address(0), CANNOT_TRANSFER_TO_ZERO_ADDRESS);
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
文件 13 的 13:supports-interface.sol
pragma solidity ^0.8.4;
import "./erc165.sol";
contract SupportsInterface is
ERC165
{
mapping(bytes4 => bool) internal supportedInterfaces;
constructor()
{
supportedInterfaces[0x01ffc9a7] = true;
}
function supportsInterface(
bytes4 _interfaceID
)
external
override
view
returns (bool)
{
return supportedInterfaces[_interfaceID];
}
}
{
"compilationTarget": {
"crypto-monkey-king.sol": "CryptoMonkeyKing"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":false,"internalType":"bool","name":"_approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CANNOT_TRANSFER_TO_ZERO_ADDRESS","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NOT_CURRENT_OWNER","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"airdropNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"airdropNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAirdropAccount","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxMintPerTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxPageSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintMaxPerDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintableCountToday","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintableRemaining","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintedCountToday","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pageSize","type":"uint256"},{"internalType":"uint256","name":"pageIndex","type":"uint256"}],"name":"getPagedNFTs","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bool","name":"isSelling","type":"bool"},{"internalType":"uint256","name":"priceInWei","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"internalType":"struct CryptoMonkeyKing.NFT[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"giveNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"giveNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"isMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"mintNFTs","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"_name","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_airdropAccount","type":"address"}],"name":"setAirdropAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxMintPerTime","type":"uint256"}],"name":"setMaxMintPerTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mintMaxPerDay","type":"uint256"}],"name":"setMintMaxPerDay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mintPrice","type":"uint256"}],"name":"setMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]