编译器
0.8.12+commit.f00d7308
文件 1 的 27:AccessControl.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 27:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 3 的 27:Base64.sol
pragma solidity >=0.6.0;
library Base64 {
string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
bytes internal constant TABLE_DECODE =
hex'0000000000000000000000000000000000000000000000000000000000000000'
hex'00000000000000000000003e0000003f3435363738393a3b3c3d000000000000'
hex'00000102030405060708090a0b0c0d0e0f101112131415161718190000000000'
hex'001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000';
function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return '';
string memory table = TABLE_ENCODE;
uint256 encodedLen = 4 * ((data.length + 2) / 3);
string memory result = new string(encodedLen + 32);
assembly {
mstore(result, encodedLen)
let tablePtr := add(table, 1)
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
let resultPtr := add(result, 32)
for {
} lt(dataPtr, endPtr) {
} {
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1)
}
switch mod(mload(data), 3)
case 1 {
mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
}
case 2 {
mstore(sub(resultPtr, 1), shl(248, 0x3d))
}
}
return result;
}
function decode(string memory _data) internal pure returns (bytes memory) {
bytes memory data = bytes(_data);
if (data.length == 0) return new bytes(0);
require(data.length % 4 == 0, 'invalid base64 decoder input');
bytes memory table = TABLE_DECODE;
uint256 decodedLen = (data.length / 4) * 3;
bytes memory result = new bytes(decodedLen + 32);
assembly {
let lastBytes := mload(add(data, mload(data)))
if eq(and(lastBytes, 0xFF), 0x3d) {
decodedLen := sub(decodedLen, 1)
if eq(and(lastBytes, 0xFFFF), 0x3d3d) {
decodedLen := sub(decodedLen, 1)
}
}
mstore(result, decodedLen)
let tablePtr := add(table, 1)
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
let resultPtr := add(result, 32)
for {
} lt(dataPtr, endPtr) {
} {
dataPtr := add(dataPtr, 4)
let input := mload(dataPtr)
let output := add(
add(
shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)),
shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))
),
add(
shl(6, and(mload(add(tablePtr, and(shr(8, input), 0xFF))), 0xFF)),
and(mload(add(tablePtr, and(input, 0xFF))), 0xFF)
)
)
mstore(resultPtr, shl(232, output))
resultPtr := add(resultPtr, 3)
}
}
return result;
}
}
文件 4 的 27:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 5 的 27:ECDSA.sol
pragma solidity ^0.8.0;
import "../Strings.sol";
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
文件 6 的 27:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 7 的 27:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, _allowances[owner][spender] + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = _allowances[owner][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
文件 8 的 27:ERC721M.sol
pragma solidity ^0.8.0;
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
import './ERC721MLibrary.sol';
error IncorrectOwner();
error NonexistentToken();
error QueryForZeroAddress();
error TokenIdUnstaked();
error ExceedsStakingLimit();
error MintToZeroAddress();
error MintZeroQuantity();
error MintMaxSupplyReached();
error MintMaxWalletReached();
error CallerNotOwnerNorApproved();
error ApprovalToCaller();
error ApproveToCurrentOwner();
error TransferFromIncorrectOwner();
error TransferToNonERC721ReceiverImplementer();
error TransferToZeroAddress();
abstract contract ERC721M {
using Address for address;
using Strings for uint256;
using UserDataOps for uint256;
using TokenDataOps for uint256;
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
string public name;
string public symbol;
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
uint256 public totalSupply;
uint256 immutable startingIndex;
uint256 immutable collectionSize;
uint256 immutable maxPerWallet;
uint256 constant stakingLimit = 100;
mapping(uint256 => uint256) internal _tokenData;
mapping(address => uint256) internal _userData;
constructor(
string memory name_,
string memory symbol_,
uint256 startingIndex_,
uint256 collectionSize_,
uint256 maxPerWallet_
) {
name = name_;
symbol = symbol_;
collectionSize = collectionSize_;
maxPerWallet = maxPerWallet_;
startingIndex = startingIndex_;
}
function stake(uint256[] calldata tokenIds) external payable {
uint256 userData = _claimReward();
for (uint256 i; i < tokenIds.length; ++i) userData = _stake(msg.sender, tokenIds[i], userData);
_userData[msg.sender] = userData;
}
function unstake(uint256[] calldata tokenIds) external payable {
uint256 userData = _claimReward();
for (uint256 i; i < tokenIds.length; ++i) userData = _unstake(msg.sender, tokenIds[i], userData);
_userData[msg.sender] = userData;
}
function claimReward() external payable {
_userData[msg.sender] = _claimReward();
}
function _stake(
address from,
uint256 tokenId,
uint256 userData
) private returns (uint256) {
uint256 _numStaked = userData.numStaked();
uint256 tokenData = _tokenDataOf(tokenId);
address owner = tokenData.owner();
if (_numStaked >= stakingLimit) revert ExceedsStakingLimit();
if (owner != from) revert IncorrectOwner();
delete getApproved[tokenId];
(uint256 userDataX, uint256 tokenDataX) = _beforeStakeDataTransform(tokenId, userData, tokenData);
(userData, tokenData) = applySafeDataTransform(userData, tokenData, userDataX, tokenDataX);
tokenData = tokenData.setstaked();
userData = userData.decreaseBalance(1).increaseNumStaked(1);
if (_numStaked == 0) userData = userData.setStakeStart(block.timestamp);
_tokenData[tokenId] = tokenData;
emit Transfer(from, address(this), tokenId);
return userData;
}
function _unstake(
address to,
uint256 tokenId,
uint256 userData
) private returns (uint256) {
uint256 tokenData = _tokenDataOf(tokenId);
address owner = tokenData.trueOwner();
bool isStaked = tokenData.staked();
if (owner != to) revert IncorrectOwner();
if (!isStaked) revert TokenIdUnstaked();
(uint256 userDataX, uint256 tokenDataX) = _beforeUnstakeDataTransform(tokenId, userData, tokenData);
(userData, tokenData) = applySafeDataTransform(userData, tokenData, userDataX, tokenDataX);
if (tokenData.mintAndStake()) {
unchecked {
tokenData = _ensureTokenDataSet(tokenId + 1, tokenData).unsetMintAndStake();
}
}
tokenData = tokenData.unsetstaked();
userData = userData.increaseBalance(1).decreaseNumStaked(1).setStakeStart(block.timestamp);
_tokenData[tokenId] = tokenData;
emit Transfer(address(this), to, tokenId);
return userData;
}
function _mintAndStake(
address to,
uint256 quantity,
bool stake_
) internal {
unchecked {
uint256 totalSupply_ = totalSupply;
uint256 startTokenId = startingIndex + totalSupply_;
uint256 userData = _userData[to];
uint256 numMinted_ = userData.numMinted();
if (to == address(0)) revert MintToZeroAddress();
if (quantity == 0) revert MintZeroQuantity();
if (totalSupply_ + quantity > collectionSize) revert MintMaxSupplyReached();
if (numMinted_ + quantity > maxPerWallet && address(this).code.length != 0) revert MintMaxWalletReached();
userData = userData.increaseNumMinted(quantity);
uint256 tokenData = TokenDataOps.newTokenData(to, block.timestamp, stake_);
if (quantity == 1) tokenData = tokenData.flagNextTokenDataSet();
if (stake_) {
uint256 _numStaked = userData.numStaked();
userData = claimReward(userData);
userData = userData.increaseNumStaked(quantity);
if (_numStaked + quantity > stakingLimit) revert ExceedsStakingLimit();
if (_numStaked == 0) userData = userData.setStakeStart(block.timestamp);
uint256 tokenId;
for (uint256 i; i < quantity; ++i) {
tokenId = startTokenId + i;
(userData, tokenData) = _beforeStakeDataTransform(tokenId, userData, tokenData);
emit Transfer(address(0), to, tokenId);
emit Transfer(to, address(this), tokenId);
}
} else {
userData = userData.increaseBalance(quantity);
for (uint256 i; i < quantity; ++i) emit Transfer(address(0), to, startTokenId + i);
}
_userData[to] = userData;
_tokenData[startTokenId] = tokenData;
totalSupply += quantity;
}
}
function _claimReward() internal returns (uint256) {
uint256 userData = _userData[msg.sender];
return claimReward(userData);
}
function claimReward(uint256 userData) private returns (uint256) {
uint256 reward = _pendingReward(msg.sender, userData);
userData = userData.setLastClaimed(block.timestamp);
_payoutReward(msg.sender, reward);
return userData;
}
function _tokenDataOf(uint256 tokenId) public view returns (uint256) {
if (!_exists(tokenId)) revert NonexistentToken();
for (uint256 curr = tokenId; ; curr--) {
uint256 tokenData = _tokenData[curr];
if (tokenData != 0) return (curr == tokenId) ? tokenData : tokenData.copy();
}
return 0;
}
function _exists(uint256 tokenId) internal view returns (bool) {
return startingIndex <= tokenId && tokenId < startingIndex + totalSupply;
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public {
if (to == address(this)) {
uint256 userData = _claimReward();
_userData[msg.sender] = _stake(msg.sender, tokenId, userData);
} else {
uint256 tokenData = _tokenDataOf(tokenId);
address owner = tokenData.owner();
bool isApprovedOrOwner = (msg.sender == owner ||
isApprovedForAll[owner][msg.sender] ||
getApproved[tokenId] == msg.sender);
if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved();
if (to == address(0)) revert TransferToZeroAddress();
if (owner != from) revert TransferFromIncorrectOwner();
delete getApproved[tokenId];
unchecked {
_tokenData[tokenId] = _ensureTokenDataSet(tokenId + 1, tokenData)
.setOwner(to)
.setLastTransfer(block.timestamp)
.incrementOwnerCount();
}
_userData[from] = _userData[from].decreaseBalance(1);
_userData[to] = _userData[to].increaseBalance(1);
emit Transfer(from, to, tokenId);
}
}
function _ensureTokenDataSet(uint256 tokenId, uint256 tokenData) private returns (uint256) {
if (!tokenData.nextTokenDataSet() && _tokenData[tokenId] == 0 && _exists(tokenId))
_tokenData[tokenId] = tokenData.copy();
return tokenData.flagNextTokenDataSet();
}
function _beforeStakeDataTransform(
uint256,
uint256 userData,
uint256 tokenData
) internal view virtual returns (uint256, uint256) {
return (userData, tokenData);
}
function _beforeUnstakeDataTransform(
uint256,
uint256 userData,
uint256 tokenData
) internal view virtual returns (uint256, uint256) {
return (userData, tokenData);
}
function _pendingReward(address, uint256 userData) internal view virtual returns (uint256);
function _payoutReward(address user, uint256 reward) internal virtual;
function ownerOf(uint256 tokenId) external view returns (address) {
return _tokenDataOf(tokenId).owner();
}
function trueOwnerOf(uint256 tokenId) external view returns (address) {
return _tokenDataOf(tokenId).trueOwner();
}
function balanceOf(address owner) external view returns (uint256) {
if (owner == address(0)) revert QueryForZeroAddress();
return _userData[owner].balance();
}
function numStaked(address user) external view returns (uint256) {
return _userData[user].numStaked();
}
function numOwned(address user) external view returns (uint256) {
uint256 userData = _userData[user];
return userData.balance() + userData.numStaked();
}
function numMinted(address user) external view returns (uint256) {
return _userData[user].numMinted();
}
function pendingReward(address user) external view returns (uint256) {
return _pendingReward(user, _userData[user]);
}
function tokenIdsOf(address user, uint256 type_) external view returns (uint256[] memory) {
unchecked {
uint256 numTotal = type_ == 0 ? this.balanceOf(user) : type_ == 1
? this.numStaked(user)
: this.numOwned(user);
uint256[] memory ids = new uint256[](numTotal);
if (numTotal == 0) return ids;
uint256 count;
for (uint256 i = startingIndex; i < totalSupply + startingIndex; ++i) {
uint256 tokenData = _tokenDataOf(i);
if (user == tokenData.trueOwner()) {
bool staked = tokenData.staked();
if ((type_ == 0 && !staked) || (type_ == 1 && staked) || type_ == 2) {
ids[count++] = i;
if (numTotal == count) return ids;
}
}
}
return ids;
}
}
function totalNumStaked() external view returns (uint256) {
unchecked {
uint256 count;
for (uint256 i = startingIndex; i < startingIndex + totalSupply; ++i) {
if (_tokenDataOf(i).staked()) ++count;
}
return count;
}
}
function tokenURI(uint256 id) public view virtual returns (string memory);
function supportsInterface(bytes4 interfaceId) external view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function approve(address spender, uint256 tokenId) external {
address owner = _tokenDataOf(tokenId).owner();
if (msg.sender != owner && !isApprovedForAll[owner][msg.sender]) revert CallerNotOwnerNorApproved();
getApproved[tokenId] = spender;
emit Approval(owner, spender, tokenId);
}
function setApprovalForAll(address operator, bool approved) external {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external {
safeTransferFrom(from, to, tokenId, '');
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public {
transferFrom(from, to, tokenId);
if (
to.code.length != 0 &&
IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) !=
IERC721Receiver(to).onERC721Received.selector
) revert TransferToNonERC721ReceiverImplementer();
}
}
文件 9 的 27:ERC721MLibrary.sol
pragma solidity ^0.8.0;
uint256 constant RESTRICTED_TOKEN_DATA = 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
struct TokenData {
address owner;
uint256 lastTransfer;
uint256 ownerCount;
bool staked;
bool mintAndStake;
bool nextTokenDataSet;
uint256 level;
uint256 role;
uint256 rarity;
}
uint256 constant RESTRICTED_USER_DATA = 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
struct UserData {
uint256 balance;
uint256 stakeStart;
uint256 lastClaimed;
uint256 numStaked;
uint256[5] roleBalances;
uint256 uniqueRoleCount;
uint256[3] levelBalances;
uint256 specialGuestIndex;
uint256 rarityPoints;
uint256 OGCount;
uint256 boostStart;
}
function applySafeDataTransform(
uint256 userData,
uint256 tokenData,
uint256 userDataTransformed,
uint256 tokenDataTransformed
) pure returns (uint256, uint256) {
userData = (userData & RESTRICTED_USER_DATA) | (userDataTransformed & ~RESTRICTED_USER_DATA);
tokenData = (tokenData & RESTRICTED_TOKEN_DATA) | (tokenDataTransformed & ~RESTRICTED_TOKEN_DATA);
return (userData, tokenData);
}
library UserDataOps {
function getUserData(uint256 userData) internal pure returns (UserData memory) {
return
UserData({
balance: UserDataOps.balance(userData),
stakeStart: UserDataOps.stakeStart(userData),
lastClaimed: UserDataOps.lastClaimed(userData),
numStaked: UserDataOps.numStaked(userData),
roleBalances: UserDataOps.roleBalances(userData),
uniqueRoleCount: UserDataOps.uniqueRoleCount(userData),
levelBalances: UserDataOps.levelBalances(userData),
specialGuestIndex: UserDataOps.specialGuestIndex(userData),
rarityPoints: UserDataOps.rarityPoints(userData),
OGCount: UserDataOps.OGCount(userData),
boostStart: UserDataOps.boostStart(userData)
});
}
function balance(uint256 userData) internal pure returns (uint256) {
return userData & 0xFFFFF;
}
function increaseBalance(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData + amount;
}
}
function decreaseBalance(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData - amount;
}
}
function numMinted(uint256 userData) internal pure returns (uint256) {
return (userData >> 20) & 0xFFFFF;
}
function increaseNumMinted(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData + (amount << 20);
}
}
function stakeStart(uint256 userData) internal pure returns (uint256) {
return (userData >> 40) & 0xFFFFFFFFFF;
}
function setStakeStart(uint256 userData, uint256 timestamp) internal pure returns (uint256) {
return (userData & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000FFFFFFFFFF) | (timestamp << 40);
}
function lastClaimed(uint256 userData) internal pure returns (uint256) {
return (userData >> 80) & 0xFFFFFFFFFF;
}
function setLastClaimed(uint256 userData, uint256 timestamp) internal pure returns (uint256) {
return (userData & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000FFFFFFFFFFFFFFFFFFFF) | (timestamp << 80);
}
function numStaked(uint256 userData) internal pure returns (uint256) {
return (userData >> 120) & 0xFF;
}
function increaseNumStaked(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData + (amount << 120);
}
}
function decreaseNumStaked(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData - (amount << 120);
}
}
function roleBalances(uint256 userData) internal pure returns (uint256[5] memory balances) {
balances = [
(userData >> (128 + 0)) & 0xFF,
(userData >> (128 + 8)) & 0xFF,
(userData >> (128 + 16)) & 0xFF,
(userData >> (128 + 24)) & 0xFF,
(userData >> (128 + 32)) & 0xFF
];
}
function uniqueRoleCount(uint256 userData) internal pure returns (uint256) {
unchecked {
return (toUInt256((userData >> (128)) & 0xFF > 0) +
toUInt256((userData >> (128 + 8)) & 0xFF > 0) +
toUInt256((userData >> (128 + 16)) & 0xFF > 0) +
toUInt256((userData >> (128 + 24)) & 0xFF > 0) +
toUInt256((userData >> (128 + 32)) & 0xFF > 0));
}
}
function levelBalances(uint256 userData) internal pure returns (uint256[3] memory balances) {
balances = [(userData >> (168 + 0)) & 0xFF, (userData >> (168 + 8)) & 0xFF, (userData >> (168 + 16)) & 0xFF];
}
function baseReward(uint256 userData) internal pure returns (uint256) {
unchecked {
return (((userData >> (168)) & 0xFF) +
(((userData >> (168 + 8)) & 0xFF) << 1) +
(((userData >> (168 + 16)) & 0xFF) << 2));
}
}
function rarityPoints(uint256 userData) internal pure returns (uint256) {
return (userData >> 200) & 0x3FF;
}
function specialGuestIndex(uint256 userData) internal pure returns (uint256) {
return (userData >> 192) & 0xFF;
}
function setSpecialGuestIndex(uint256 userData, uint256 index) internal pure returns (uint256) {
return (userData & ~uint256(0xFF << 192)) | (index << 192);
}
function boostStart(uint256 userData) internal pure returns (uint256) {
return (userData >> 218) & 0xFFFFFFFFFF;
}
function setBoostStart(uint256 userData, uint256 timestamp) internal pure returns (uint256) {
return (userData & ~(uint256(0xFFFFFFFFFF) << 218)) | (timestamp << 218);
}
function OGCount(uint256 userData) internal pure returns (uint256) {
return (userData >> 210) & 0xFF;
}
function updateUserDataStake(uint256 userData, uint256 tokenData) internal pure returns (uint256) {
unchecked {
uint256 role = TokenDataOps.role(tokenData);
if (role > 0) {
userData += uint256(1) << (120 + (role << 3));
userData += TokenDataOps.rarity(tokenData) << 200;
}
if (TokenDataOps.mintAndStake(tokenData)) userData += uint256(1) << 210;
userData += uint256(1) << (160 + (TokenDataOps.level(tokenData) << 3));
return userData;
}
}
function updateUserDataUnstake(uint256 userData, uint256 tokenData) internal pure returns (uint256) {
unchecked {
uint256 role = TokenDataOps.role(tokenData);
if (role > 0) {
userData -= uint256(1) << (120 + (role << 3));
userData -= TokenDataOps.rarity(tokenData) << 200;
}
if (TokenDataOps.mintAndStake(tokenData)) userData -= uint256(1) << 210;
userData -= uint256(1) << (160 + (TokenDataOps.level(tokenData) << 3));
return userData;
}
}
function increaseLevelBalances(uint256 userData, uint256 tokenData) internal pure returns (uint256) {
unchecked {
return userData + (uint256(1) << (160 + (TokenDataOps.level(tokenData) << 3)));
}
}
function decreaseLevelBalances(uint256 userData, uint256 tokenData) internal pure returns (uint256) {
unchecked {
return userData - (uint256(1) << (160 + (TokenDataOps.level(tokenData) << 3)));
}
}
}
library TokenDataOps {
function getTokenData(uint256 tokenData) internal view returns (TokenData memory) {
return
TokenData({
owner: TokenDataOps.owner(tokenData),
lastTransfer: TokenDataOps.lastTransfer(tokenData),
ownerCount: TokenDataOps.ownerCount(tokenData),
staked: TokenDataOps.staked(tokenData),
mintAndStake: TokenDataOps.mintAndStake(tokenData),
nextTokenDataSet: TokenDataOps.nextTokenDataSet(tokenData),
level: TokenDataOps.level(tokenData),
role: TokenDataOps.role(tokenData),
rarity: TokenDataOps.rarity(tokenData)
});
}
function newTokenData(
address owner_,
uint256 lastTransfer_,
bool stake_
) internal pure returns (uint256) {
uint256 tokenData = (uint256(uint160(owner_)) | (lastTransfer_ << 160) | (uint256(1) << 200));
return stake_ ? setstaked(setMintAndStake(tokenData)) : tokenData;
}
function copy(uint256 tokenData) internal pure returns (uint256) {
return tokenData & (RESTRICTED_TOKEN_DATA >> (mintAndStake(tokenData) ? 2 : 4));
}
function owner(uint256 tokenData) internal view returns (address) {
if (staked(tokenData)) return address(this);
return trueOwner(tokenData);
}
function setOwner(uint256 tokenData, address owner_) internal pure returns (uint256) {
return (tokenData & 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000) | uint160(owner_);
}
function staked(uint256 tokenData) internal pure returns (bool) {
return ((tokenData >> 220) & uint256(1)) > 0;
}
function setstaked(uint256 tokenData) internal pure returns (uint256) {
return tokenData | (uint256(1) << 220);
}
function unsetstaked(uint256 tokenData) internal pure returns (uint256) {
return tokenData & ~(uint256(1) << 220);
}
function mintAndStake(uint256 tokenData) internal pure returns (bool) {
return ((tokenData >> 221) & uint256(1)) > 0;
}
function setMintAndStake(uint256 tokenData) internal pure returns (uint256) {
return tokenData | (uint256(1) << 221);
}
function unsetMintAndStake(uint256 tokenData) internal pure returns (uint256) {
return tokenData & ~(uint256(1) << 221);
}
function nextTokenDataSet(uint256 tokenData) internal pure returns (bool) {
return ((tokenData >> 222) & uint256(1)) > 0;
}
function flagNextTokenDataSet(uint256 tokenData) internal pure returns (uint256) {
return tokenData | (uint256(1) << 222);
}
function trueOwner(uint256 tokenData) internal pure returns (address) {
return address(uint160(tokenData));
}
function ownerCount(uint256 tokenData) internal pure returns (uint256) {
return (tokenData >> 200) & 0xFFFFF;
}
function incrementOwnerCount(uint256 tokenData) internal pure returns (uint256) {
uint256 newOwnerCount = min(ownerCount(tokenData) + 1, 0xFFFFF);
return (tokenData & ~(uint256(0xFFFFF) << 200)) | (newOwnerCount << 200);
}
function resetOwnerCount(uint256 tokenData) internal pure returns (uint256) {
uint256 count = min(ownerCount(tokenData), 2);
return (tokenData & ~(uint256(0xFFFFF) << 200)) | (count << 200);
}
function lastTransfer(uint256 tokenData) internal pure returns (uint256) {
return (tokenData >> 160) & 0xFFFFFFFFFF;
}
function setLastTransfer(uint256 tokenData, uint256 timestamp) internal pure returns (uint256) {
return (tokenData & 0xFFFFFFFFFFFFFF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | (timestamp << 160);
}
function level(uint256 tokenData) internal pure returns (uint256) {
unchecked {
return 1 + (tokenData >> 252);
}
}
function increaseLevel(uint256 tokenData) internal pure returns (uint256) {
unchecked {
return tokenData + (uint256(1) << 252);
}
}
function role(uint256 tokenData) internal pure returns (uint256) {
return (tokenData >> 248) & 0xF;
}
function rarity(uint256 tokenData) internal pure returns (uint256) {
return (tokenData >> 244) & 0xF;
}
function setRoleAndRarity(uint256 tokenData, uint256 dna) internal pure returns (uint256) {
return ((tokenData & ~(uint256(0xFF) << 244)) | (DNAOps.toRole(dna) << 248) | (DNAOps.toRarity(dna) << 244));
}
}
library DNAOps {
function toRole(uint256 dna) internal pure returns (uint256) {
unchecked {
return 1 + ((dna & 0xFF) % 5);
}
}
function toRarity(uint256 dna) internal pure returns (uint256) {
uint256 dnaFur = (dna >> 8) & 0xFF;
if (dnaFur > 108) return 0;
if (dnaFur > 73) return 1;
if (dnaFur > 17) return 2;
return 3;
}
}
function toUInt256(bool x) pure returns (uint256 r) {
assembly {
r := x
}
}
function min(uint256 a, uint256 b) pure returns (uint256) {
return a < b ? a : b;
}
function isValidString(string calldata str, uint256 maxLen) pure returns (bool) {
bytes memory b = bytes(str);
if (b.length < 1 || b.length > maxLen || b[0] == 0x20 || b[b.length - 1] == 0x20) return false;
bytes1 lastChar = b[0];
bytes1 char;
for (uint256 i; i < b.length; ++i) {
char = b[i];
if (
(char > 0x60 && char < 0x7B) ||
(char > 0x40 && char < 0x5B) ||
(char == 0x20) ||
(char > 0x2F && char < 0x3A)
) {
lastChar = char;
} else {
return false;
}
}
return true;
}
文件 10 的 27:Gouda.sol
pragma solidity ^0.8.0;
import '@openzeppelin/contracts/access/AccessControl.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
contract Gouda is ERC20, AccessControl {
bytes32 constant MINT_AUTHORITY = keccak256('MINT_AUTHORITY');
bytes32 constant BURN_AUTHORITY = keccak256('BURN_AUTHORITY');
bytes32 constant TREASURY = keccak256('TREASURY');
address public multiSigTreasury = 0xFB79a928C5d6c5932Ba83Aa8C7145cBDCDb9fd2E;
constructor(address madmouse) ERC20('Gouda', 'GOUDA') {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(MINT_AUTHORITY, madmouse);
_setupRole(BURN_AUTHORITY, madmouse);
_setupRole(TREASURY, multiSigTreasury);
_mint(multiSigTreasury, 200_000 * 1e18);
}
function mint(address user, uint256 amount) external onlyRole(MINT_AUTHORITY) {
_mint(user, amount);
}
function burnFrom(address account, uint256 amount) public {
if (!hasRole(BURN_AUTHORITY, msg.sender)) _spendAllowance(account, msg.sender, amount);
_burn(account, amount);
}
}
文件 11 的 27:IAccessControl.sol
pragma solidity ^0.8.0;
interface IAccessControl {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
文件 12 的 27:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 13 的 27:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 14 的 27:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 15 的 27:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
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 balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
文件 16 的 27:IERC721Metadata.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 17 的 27:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 18 的 27:LinkTokenInterface.sol
pragma solidity ^0.8.0;
interface LinkTokenInterface {
function allowance(address owner, address spender) external view returns (uint256 remaining);
function approve(address spender, uint256 value) external returns (bool success);
function balanceOf(address owner) external view returns (uint256 balance);
function decimals() external view returns (uint8 decimalPlaces);
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
function increaseApproval(address spender, uint256 subtractedValue) external;
function name() external view returns (string memory tokenName);
function symbol() external view returns (string memory tokenSymbol);
function totalSupply() external view returns (uint256 totalTokensIssued);
function transfer(address to, uint256 value) external returns (bool success);
function transferAndCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool success);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool success);
}
文件 19 的 27:MadMouse.sol
pragma solidity ^0.8.12;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import './lib/Ownable.sol';
import {VRFBaseMainnet as VRFBase} from './lib/VRFBase.sol';
import './Gouda.sol';
import './MadMouseMetadata.sol';
import './MadMouseStaking.sol';
error PublicSaleNotActive();
error WhitelistNotActive();
error InvalidAmount();
error ExceedsLimit();
error SignatureExceedsLimit();
error IncorrectValue();
error InvalidSignature();
error ContractCallNotAllowed();
error InvalidString();
error MaxLevelReached();
error MaxNumberReached();
error MinHoldDurationRequired();
error IncorrectHash();
error CollectionAlreadyRevealed();
error CollectionNotRevealed();
error TokenDataAlreadySet();
error MintAndStakeMinHoldDurationNotReached();
contract MadMouse is Ownable, MadMouseStaking, VRFBase {
using ECDSA for bytes32;
using UserDataOps for uint256;
using TokenDataOps for uint256;
using DNAOps for uint256;
bool public publicSaleActive;
uint256 constant MAX_SUPPLY = 5555;
uint256 constant MAX_PER_WALLET = 20;
uint256 constant price = 0.085 ether;
uint256 constant PURCHASE_LIMIT = 5;
uint256 constant whitelistPrice = 0.075 ether;
uint256 constant WHITELIST_PURCHASE_LIMIT = 5;
MadMouseMetadata public metadata;
address public multiSigTreasury = 0xFB79a928C5d6c5932Ba83Aa8C7145cBDCDb9fd2E;
address signerAddress = 0x3ADE0c5e35cbF136245F4e4bBf4563BD151d39D1;
uint256 public totalLevel2Reached;
uint256 public totalLevel3Reached;
uint256 constant LEVEL_2_COST = 120 * 1e18;
uint256 constant LEVEL_3_COST = 350 * 1e18;
uint256 constant MAX_NUM_LEVEL_2 = 3477;
uint256 constant MAX_NUM_LEVEL_3 = 1399;
uint256 constant NAME_CHANGE_COST = 50 * 1e18;
uint256 constant BIO_CHANGE_COST = 25 * 1e18;
uint256 constant MAX_LEN_NAME = 20;
uint256 constant MAX_LEN_BIO = 35;
uint256 constant MINT_AND_STAKE_MIN_HOLD_DURATION = 2 days;
uint256 profileUpdateMinHoldDuration = 30 days;
mapping(uint256 => string) public mouseName;
mapping(uint256 => string) public mouseBio;
string public description;
string public imagesBaseURI;
string constant unrevealedURI = 'ipfs://QmW9NKUGYesTiYx5iSP1o82tn4Chq9i1yQV6DBnzznrHTH';
bool private revealed;
bytes32 immutable secretHash;
constructor(bytes32 secretHash_) MadMouseStaking(MAX_SUPPLY, MAX_PER_WALLET) {
secretHash = secretHash_;
}
function mint(
uint256 amount,
bytes calldata signature,
bool stake
) external payable noContract {
if (!publicSaleActive) revert PublicSaleNotActive();
if (PURCHASE_LIMIT < amount) revert ExceedsLimit();
if (msg.value != price * amount) revert IncorrectValue();
if (!validSignature(signature, 0)) revert InvalidSignature();
_mintAndStake(msg.sender, amount, stake);
}
function whitelistMint(
uint256 amount,
uint256 limit,
bytes calldata signature,
bool stake
) external payable noContract {
if (publicSaleActive) revert WhitelistNotActive();
if (WHITELIST_PURCHASE_LIMIT < limit) revert SignatureExceedsLimit();
if (msg.value != whitelistPrice * amount) revert IncorrectValue();
if (!validSignature(signature, limit)) revert InvalidSignature();
uint256 numMinted = _userData[msg.sender].numMinted();
if (numMinted + amount > limit) revert ExceedsLimit();
_mintAndStake(msg.sender, amount, stake);
}
function levelUp(uint256 tokenId) external payable {
uint256 tokenData = _tokenDataOf(tokenId);
address owner = tokenData.trueOwner();
if (owner != msg.sender) revert IncorrectOwner();
uint256 level = tokenData.level();
if (level > 2) revert MaxLevelReached();
if (level == 1) {
if (totalLevel2Reached >= MAX_NUM_LEVEL_2) revert MaxNumberReached();
gouda.burnFrom(msg.sender, LEVEL_2_COST);
++totalLevel2Reached;
} else {
if (totalLevel3Reached >= MAX_NUM_LEVEL_3) revert MaxNumberReached();
gouda.burnFrom(msg.sender, LEVEL_3_COST);
++totalLevel3Reached;
}
uint256 newTokenData = tokenData.increaseLevel().resetOwnerCount();
if (tokenData.staked() && revealed) {
uint256 userData = _claimReward();
(userData, newTokenData) = updateDataWhileStaked(userData, tokenId, tokenData, newTokenData);
_userData[msg.sender] = userData;
}
_tokenData[tokenId] = newTokenData;
}
function setName(uint256 tokenId, string calldata name) external payable onlyLongtermHolder(tokenId) {
if (!isValidString(name, MAX_LEN_NAME)) revert InvalidString();
gouda.burnFrom(msg.sender, NAME_CHANGE_COST);
mouseName[tokenId] = name;
}
function setBio(uint256 tokenId, string calldata bio) external payable onlyLongtermHolder(tokenId) {
if (!isValidString(bio, MAX_LEN_BIO)) revert InvalidString();
gouda.burnFrom(msg.sender, BIO_CHANGE_COST);
mouseBio[tokenId] = bio;
}
function resetName(uint256 tokenId) external payable {
address _owner = _tokenDataOf(tokenId).trueOwner();
if (_owner != msg.sender && owner() != msg.sender) revert IncorrectOwner();
delete mouseName[tokenId];
}
function resetBio(uint256 tokenId) external payable {
address _owner = _tokenDataOf(tokenId).trueOwner();
if (_owner != msg.sender && owner() != msg.sender) revert IncorrectOwner();
delete mouseBio[tokenId];
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
if (!_exists(tokenId)) revert NonexistentToken();
if (!revealed || address(metadata) == address(0)) return unrevealedURI;
return metadata.buildMouseMetadata(tokenId, this.getLevel(tokenId));
}
function previewTokenURI(uint256 tokenId, uint256 level) external view returns (string memory) {
if (!_exists(tokenId)) revert NonexistentToken();
if (!revealed || address(metadata) == address(0)) return unrevealedURI;
return metadata.buildMouseMetadata(tokenId, level);
}
function getDNA(uint256 tokenId) external view onceRevealed returns (uint256) {
if (!_exists(tokenId)) revert NonexistentToken();
return computeDNA(tokenId);
}
function getLevel(uint256 tokenId) external view returns (uint256) {
return _tokenDataOf(tokenId).level();
}
function validSignature(bytes calldata signature, uint256 limit) private view returns (bool) {
bytes32 msgHash = keccak256(abi.encode(address(this), msg.sender, limit));
return msgHash.toEthSignedMessageHash().recover(signature) == signerAddress;
}
function computeDNA(uint256 tokenId) private view returns (uint256) {
return uint256(keccak256(abi.encodePacked(randomSeed, tokenId)));
}
function setPublicSaleActive(bool active) external payable onlyOwner {
publicSaleActive = active;
}
function setProfileUpdateMinHoldDuration(uint256 duration) external payable onlyOwner {
profileUpdateMinHoldDuration = duration;
}
function giveAway(address[] calldata to) external payable onlyOwner {
for (uint256 i; i < to.length; ++i) _mintAndStake(to[i], 1, false);
}
function setSignerAddress(address address_) external payable onlyOwner {
signerAddress = address_;
}
function setMetadataAddress(MadMouseMetadata metadata_) external payable onlyOwner {
metadata = metadata_;
}
function withdraw() external payable onlyOwner {
uint256 balance = address(this).balance;
multiSigTreasury.call{value: balance}('');
}
function recoverToken(IERC20 token) external payable onlyOwner {
uint256 balance = token.balanceOf(address(this));
token.transfer(msg.sender, balance);
}
function setDescription(string memory description_) external payable onlyOwner {
description = description_;
}
function setImagesBaseURI(string memory uri) external payable onlyOwner onceRevealed {
imagesBaseURI = uri;
}
function reveal(string memory _imagesBaseURI, bytes32 secretSeed_) external payable onlyOwner whenRandomSeedSet {
if (revealed) revert CollectionAlreadyRevealed();
if (secretHash != keccak256(abi.encode(secretSeed_))) revert IncorrectHash();
revealed = true;
imagesBaseURI = _imagesBaseURI;
_shiftRandomSeed(uint256(secretSeed_));
}
function _beforeStakeDataTransform(
uint256 tokenId,
uint256 userData,
uint256 tokenData
) internal view override returns (uint256, uint256) {
if (!tokenData.mintAndStake() && tokenData.role() == 0 && revealed)
tokenData = tokenData.setRoleAndRarity(computeDNA(tokenId));
userData = userData.updateUserDataStake(tokenData);
return (userData, tokenData);
}
function _beforeUnstakeDataTransform(
uint256,
uint256 userData,
uint256 tokenData
) internal view override returns (uint256, uint256) {
userData = userData.updateUserDataUnstake(tokenData);
if (tokenData.mintAndStake() && block.timestamp - tokenData.lastTransfer() < MINT_AND_STAKE_MIN_HOLD_DURATION)
revert MintAndStakeMinHoldDurationNotReached();
return (userData, tokenData);
}
function updateStakedTokenData(uint256[] calldata tokenIds) external payable onceRevealed {
uint256 userData = _claimReward();
uint256 tokenId;
uint256 tokenData;
for (uint256 i; i < tokenIds.length; ++i) {
tokenId = tokenIds[i];
tokenData = _tokenDataOf(tokenId);
if (tokenData.trueOwner() != msg.sender) revert IncorrectOwner();
if (!tokenData.staked()) revert TokenIdUnstaked();
if (tokenData.role() != 0) revert TokenDataAlreadySet();
(userData, tokenData) = updateDataWhileStaked(userData, tokenId, tokenData, tokenData);
_tokenData[tokenId] = tokenData;
}
_userData[msg.sender] = userData;
}
function updateDataWhileStaked(
uint256 userData,
uint256 tokenId,
uint256 oldTokenData,
uint256 newTokenData
) private view returns (uint256, uint256) {
uint256 userDataX;
uint256 tokenDataX = newTokenData.role() != 0
? newTokenData
: newTokenData.setRoleAndRarity(computeDNA(tokenId));
userDataX = userData.updateUserDataUnstake(oldTokenData).updateUserDataStake(tokenDataX);
return applySafeDataTransform(userData, newTokenData, userDataX, tokenDataX);
}
function shouldUpdateStakedIds(address user) external view returns (uint256[] memory) {
if (!revealed) return new uint256[](0);
uint256[] memory stakedIds = this.tokenIdsOf(user, 1);
uint256 userData = _userData[user];
uint256 oldTotalBonus = totalBonus(user, userData);
uint256 tokenData;
for (uint256 i; i < stakedIds.length; ++i) {
tokenData = _tokenDataOf(stakedIds[i]);
if (tokenData.role() == 0)
(userData, ) = updateDataWhileStaked(userData, stakedIds[i], tokenData, tokenData);
else stakedIds[i] = 0;
}
uint256 newTotalBonus = totalBonus(user, userData);
return (newTotalBonus > oldTotalBonus) ? stakedIds : new uint256[](0);
}
modifier onceRevealed() {
if (!revealed) revert CollectionNotRevealed();
_;
}
modifier noContract() {
if (tx.origin != msg.sender) revert ContractCallNotAllowed();
_;
}
modifier onlyLongtermHolder(uint256 tokenId) {
uint256 tokenData = _tokenDataOf(tokenId);
uint256 timeHeld = block.timestamp - tokenData.lastTransfer();
if (tokenData.trueOwner() != msg.sender) revert IncorrectOwner();
if (timeHeld < profileUpdateMinHoldDuration) revert MinHoldDurationRequired();
_;
}
}
文件 20 的 27:MadMouseMetadata.sol
pragma solidity ^0.8.12;
import './Gouda.sol';
import './MadMouse.sol';
import './MadMouseStaking.sol';
import './lib/Base64.sol';
import './lib/MetadataEncode.sol';
import './lib/Ownable.sol';
contract MadMouseMetadata is Ownable {
using Strings for uint256;
using MetadataEncode for bytes;
using TokenDataOps for uint256;
using DNAOps for uint256;
struct Mouse {
string role;
string rarity;
string fur;
string expression;
string glasses;
string hat;
string body;
string background;
string makeup;
string scene;
}
uint256 constant CLOWN = 0;
uint256 constant MAGICIAN = 1;
uint256 constant JUGGLER = 2;
uint256 constant TRAINER = 3;
uint256 constant PERFORMER = 4;
bytes constant ROLE = hex'436c6f776e004d6167696369616e004a7567676c657200547261696e657200506572666f726d6572';
bytes constant RARITIES = hex'436f6d6d6f6e005261726500537570657200556c747261';
bytes constant FUR =
hex'47686f737400476f6c64004c6176610050616e746865720043616d6f00426c75652d50696e6b0057686974650050696e6b0042726f776e004461726b2042726f776e00477265656e00477265790049636500507572706c6500526564';
bytes constant EXPRESSION =
hex'41776b7761726400416e67727900426f72656400436f6e6675736564004772696d61636564004c6f76696e67004c61756768696e670053616400536879005374757069640057686973746c696e67';
bytes constant BACKGROUND =
hex'507572706c6500477265656e00426c7565004c6967687420426c7565005265640059656c6c6f77004c6967687420477265656e0050696e6b004f72616e6765005468726f6e6500537461722052696e6700436f6c6f737365756d';
bytes constant BODY =
hex'426c61636b00576869746500426c7565004b696d6f6e6f00546f676500436f77626f79004775617264004661726d6572004e61706f6c656f6e004b696e67004a756d706572005361696c6f7200506972617465004d65786963616e00526f62696e20486f6f6400456c660056696b696e670056616d706972650043726f636f64696c6500417369616e0053686f776d616e0047656e6965004261726f6e67';
bytes constant HAT =
hex'4e6f6e6500426c61636b005768697465004a65737465720053616b6174004c617572656c00436f77626f79004775617264004661726d6572004e61706f6c656f6e0043726f776e0048656c69636f70746572005361696c6f7200506972617465004d65786963616e00526f62696e20486f6f6400456c660056696b696e670048616c6f0043726f636f64696c6500417369616e0053686f776d616e0047656e69650053616c616b6f74';
bytes constant GLASSES =
hex'4e6f6e650052656400477265656e0059656c6c6f77005679706572005468696566004379626572005261696e626f77003344005468756700476f6c64004d6f6e6f636c65004d616e676100426c61636b0052617920426c61636b00426c756500507572706c6500436174204d61736b';
bytes constant CLOWN_MAKEUP =
hex'436c6f776e203100436c6f776e203200436c6f776e203300436c6f776e203400436c6f776e203500436c6f776e203600436c6f776e203700436c6f776e203800436c6f776e2039';
bytes constant CLOWN_BODY_LEVEL_2 =
hex'5069650042616c6c6f6f6e7300547769737465642042616c6c6f6f6e73004a61636b20696e2074686520426f78005761746572205370726179005075707065747300537472696e67004c6974746c6520506965727265004c6974746c65204d7572706879';
bytes constant CLOWN_WIG_LEVEL_2 =
hex'507572706c6520576967005261696e626f772057696700426c756520576967005465616c20576967005265642057696700477265656e20576967004861726c657175696e00426c61636b20426572657400426c7565204265726574';
bytes constant CLOWN_BODY_LEVEL_3 =
hex'506965204d61737465720042616c6c6f6f6e204d617374657200547769737465642042616c6c6f6f6e204d6173746572004a61636b20696e2074686520426f78204d6173746572005261696e626f7720466c6f7700507570706574204d617374657200537472696e67204d617374657200506965727265004d7572706879';
bytes constant CLOWN_BACKGROUND_LEVEL_3 =
hex'426c756520506f73746572005570210052656420506f737465720044616e636520466c6f6f7200526564204375727461696e730050757070657473005374616469756d005061726973004e657720596f726b';
bytes constant MAGICIAN_HAT =
hex'48617474657200436172640057697a6172640050656e746f00466f7274756e650046616e74617379004d616a657374696300507269736f6e6572';
bytes constant MAGICIAN_BODY_LEVEL_2 =
hex'446f7665730043617264730057616e6400526162626974004372797374616c004c657669746174696f6e005361770048616e64637566666564';
bytes constant MAGICIAN_BODY_LEVEL_3 =
hex'50686f656e69780043617264204d61737465720057616e64204d6173746572005261696e626f7700536e6f7720476c6f6265004c657669746174696f6e204d6173746572004772617465720043656d656e74';
bytes constant MAGICIAN_BACKGROUND_LEVEL_3 =
hex'426c7565204375727461696e730050696e6b20506f7374657200536b790048696c6c7300536e6f7700537061636500466163746f727900436f6e737472756374696f6e2053697465';
bytes constant MAGICIAN_SCENE_LEVEL_3 =
hex'4e6f6e65004661697279204d6f75736500466c79696e67204b6579004f776c00466c79696e6720506f74004c6974746c65204d6f757365004c69676874204175726100426f6f6b';
bytes constant JUGGLER_BODY =
hex'4a7567676c696e672042616c6c7300436c756273004b6e6976657300486f6f7073005370696e6e696e6720506c61746500426f6c617300446961626f6c6f00536c696e6b79';
bytes constant JUGGLER_BODY_LEVEL_2 =
hex'4a7567676c696e672042616c6c205363686f6c617200436c7562205363686f6c6172004b6e696665205363686f6c617200486f6f70205363686f6c6172005370696e6e696e6720506c617465205363686f6c617200426f6c6173205363686f6c617200446961626f6c6f205363686f6c617200536c696e6b79205363686f6c6172';
bytes constant JUGGLER_BODY_LEVEL_3 =
hex'4a7567676c696e672042616c6c204d617374657200436c7562204d6173746572004b6e696665204d617374657200486f6f70204d6173746572005370696e6e696e6720506c617465204d617374657200426f6c6173204d617374657200446961626f6c6f204d617374657200536c696e6b79204d6173746572';
bytes constant JUGGLER_BACKGROUND_LEVEL_3 =
hex'53706f746c6967687420546f70004d616e6f720054617267657400466f726573740053706f746c6967687420477265656e2d50696e6b004465736572740046697265776f726b7300536861646f77';
bytes constant TRAINER_PET =
hex'4765636b6f00436174004368696d7000547572746c6500446f6e6b65790054656464792042656172005365616c004261627920446f646f';
bytes constant TRAINER_BODY_LEVEL_2 =
hex'426c756520576869700052656420486f6f700042616e616e6100477265656e205768697000486f727365205768697000486f6e6579004669736800426c756520486f6f70';
bytes constant TRAINER_PET_LEVEL_2 =
hex'43726f636f64696c65005469676572004d6f6e6b6579004b6f6d6f646f00486f7273650042656172004f746172696100446f646f';
bytes constant TRAINER_BODY_LEVEL_3 =
hex'4c696768742057686970004669726520486f6f700042616e616e6173005261696e626f77205768697000556e69776869700042616d626f6f0046697368204665617374005261696e626f7720486f6f70';
bytes constant TRAINER_PET_LEVEL_3 =
hex'542d526578004c696f6e00476f72696c6c6100447261676f6e00556e69636f726e0050616e64610057616c72757300506561636f636b';
bytes constant TRAINER_BACKGROUND_LEVEL_3 =
hex'54656e74005265642052696e6700477265656e2052696e6700436173746c65004c616b650042616d626f6f00477265792052696e6700506561636f636b';
bytes constant PERFORMER_SCENE =
hex'48756c6120486f6f7000526f6c6c6120426f6c6c61004d6f6e6f6379636c650041657269616c20486f6f70004e6f6e65005377696e670044756d6262656c6c73005472616d706f6c696e65';
bytes constant PERFORMER_BODY_LEVEL_2 =
hex'547269636b7374657200526f6c6c6120426f6c6c610046756e616d62756c6973740054726170657a6973740050656e64756c756d204d61737465720044616e636572004d72204d7573636c65005472616d706f6c696e65';
bytes constant PERFORMER_SCENE_LEVEL_2 =
hex'48756c6120486f6f7020547269636b7374657200546f7765720046756e616d62756c6973740054726170657a697374205477696e73004e6f6e65005461702044616e63650044756d6262656c6c205374616e6400436f6479';
bytes constant PERFORMER_BODY_LEVEL_3 =
hex'43686169726d616e00536b617465720044617265646576696c004d65676170686f6e65004e6f6e650056697274756f736f0048657263756c65730043616e6f6e697374';
bytes constant PERFORMER_SCENE_LEVEL_3 =
hex'507972616d696400536b617465720044617265646576696c004669726573746172004e6f6e650053616c73610042617262656c6c0043616e6f6e6d616e';
bytes constant PERFORMER_BACKGROUND_LEVEL_3 =
hex'52656420506f6469756d0048616c662d5069706500426c756520506f6469756d004461726b2054656e74004d65736d6572697a65640053746167650048657263756c657320506f7374657200507572706c652052696e67';
uint256 constant WEIGHTS_FUR = 0x00000000000000000000000000000000001515151515151512110e0e0e0e0909;
uint256 constant WEIGHTS_GLASSES = 0x0000000000000000000000000000050e0e0e0e050e0e0e0e05050e050e0e0e3f;
uint256 constant WEIGHTS_BODY = 0x0000000000000000000f0f030f0303030f0f0f0f0f0f02080f0f0f0f0f080808;
uint256 constant WEIGHTS_BACKGROUND = 0x00000000000000000000000000000000000000000a0b0a191919191919191919;
uint256 constant WEIGHTS_HAT = 0x00000000000000000e0e030e0303030e0e0e0e0e0e03080e0e0e0e0e0808080d;
MadMouse public madmouse;
function setMadMouseAddress(MadMouse madmouse_) external onlyOwner {
madmouse = madmouse_;
}
function balanceOf(address user) external view returns (uint256) {
return madmouse.numOwned(user);
}
function buildMouseMetadata(uint256 tokenId, uint256 level) external view returns (string memory) {
return string.concat('data:application/json;base64,', Base64.encode(bytes(mouseMetadataJSON(tokenId, level))));
}
function getMouse(uint256 dna, uint256 level) private pure returns (Mouse memory mouse) {
uint256 dnaRole = dna & 0xFF;
uint256 dnaFur = (dna >> 8) & 0xFF;
uint256 dnaClass = (dna >> 16) & 0xFF;
uint256 dnaExpression = (dna >> 24) & 0xFF;
uint256 dnaGlasses = (dna >> 32) & 0xFF;
uint256 dnaBody = (dna >> 40) & 0xFF;
uint256 dnaBackground = (dna >> 48) & 0xFF;
uint256 dnaHat = (dna >> 56) & 0xFF;
uint256 dnaSpecial = (dna >> 64) & 0xFF;
uint256 role = dnaRole % 5;
mouse.role = ROLE.decode(role);
mouse.rarity = RARITIES.decode(dna.toRarity());
mouse.fur = FUR.selectWeighted(dnaFur, WEIGHTS_FUR);
mouse.expression = EXPRESSION.decode(dnaExpression % 11);
mouse.glasses = GLASSES.selectWeighted(dnaGlasses, WEIGHTS_GLASSES);
mouse.body = BODY.selectWeighted(dnaBody, WEIGHTS_BODY);
mouse.background = BACKGROUND.selectWeighted(dnaBackground, WEIGHTS_BACKGROUND);
mouse.hat = HAT.selectWeighted(dnaHat, WEIGHTS_HAT);
uint256 class;
if (role == CLOWN) {
class = dnaClass % 9;
mouse.makeup = CLOWN_MAKEUP.decode(class);
if (level == 2) mouse.body = CLOWN_BODY_LEVEL_2.decode(class);
if (level >= 2) {
uint256 hat = dnaHat % 9;
if (hat == 6) hat = class;
mouse.hat = CLOWN_WIG_LEVEL_2.decode(hat);
}
if (level == 3) mouse.body = CLOWN_BODY_LEVEL_3.decode(class);
if (level == 3) {
uint256 backgroundType = dnaBackground % 9;
if (backgroundType == 6) mouse.background = CLOWN_BACKGROUND_LEVEL_3.decode(class);
else mouse.background = CLOWN_BACKGROUND_LEVEL_3.decode(backgroundType);
}
}
if (role == MAGICIAN) {
class = dnaClass % 8;
mouse.hat = MAGICIAN_HAT.decode(class);
if (level == 2) mouse.body = MAGICIAN_BODY_LEVEL_2.decode(class);
if (level == 3) mouse.body = MAGICIAN_BODY_LEVEL_3.decode(class);
if (level == 3) {
uint256 sceneType = dnaSpecial % 8;
if (class == 1 && (sceneType < 4 || 6 < sceneType)) sceneType = 4;
mouse.scene = MAGICIAN_SCENE_LEVEL_3.decode(sceneType);
}
if (level == 3) {
uint256 backgroundType = dnaBackground % 8;
if (backgroundType == 6) mouse.background = MAGICIAN_BACKGROUND_LEVEL_3.decode(class);
else mouse.background = MAGICIAN_BACKGROUND_LEVEL_3.decode(backgroundType);
}
}
if (role == JUGGLER) {
class = dnaClass % 8;
if (level == 1) mouse.body = JUGGLER_BODY.decode(class);
if (level == 2) mouse.body = JUGGLER_BODY_LEVEL_2.decode(class);
if (level == 3) mouse.body = JUGGLER_BODY_LEVEL_3.decode(class);
if (level == 3) {
uint256 backgroundType = dnaBackground % 8;
if (backgroundType == 2) mouse.background = JUGGLER_BACKGROUND_LEVEL_3.decode(class);
else mouse.background = JUGGLER_BACKGROUND_LEVEL_3.decode(backgroundType);
}
}
if (role == TRAINER) {
class = dnaClass % 8;
if (level == 1) mouse.scene = TRAINER_PET.decode(class);
if (level == 2) mouse.body = TRAINER_BODY_LEVEL_2.decode(class);
if (level == 2) mouse.scene = TRAINER_PET_LEVEL_2.decode(class);
if (level == 3) mouse.body = TRAINER_BODY_LEVEL_3.decode(class);
if (level == 3) mouse.scene = TRAINER_PET_LEVEL_3.decode(class);
if (level == 3) {
uint256 backgroundType = dnaBackground % 8;
if (backgroundType == 7) mouse.background = TRAINER_BACKGROUND_LEVEL_3.decode(class);
else mouse.background = TRAINER_BACKGROUND_LEVEL_3.decode(backgroundType);
}
}
if (role == PERFORMER) {
class = dnaClass % 8;
if (level >= 1) {
if (class == 4) mouse.body = 'Hypnotist';
else mouse.scene = PERFORMER_SCENE.decode(class);
}
if (level >= 2) mouse.body = PERFORMER_BODY_LEVEL_2.decode(class);
if (level >= 2 && class != 4) mouse.scene = PERFORMER_SCENE_LEVEL_2.decode(class);
if (level == 3) {
if (class != 4) mouse.body = PERFORMER_BODY_LEVEL_3.decode(class);
if (class != 4) mouse.scene = PERFORMER_SCENE_LEVEL_3.decode(class);
if (class == 4) mouse.glasses = 'Hypnotist';
}
if (level == 3) {
uint256 backgroundType = dnaBackground % 8;
if (backgroundType == 1 || backgroundType == 4 || backgroundType == 5 || backgroundType == 6)
mouse.background = PERFORMER_BACKGROUND_LEVEL_3.decode(class);
else mouse.background = PERFORMER_BACKGROUND_LEVEL_3.decode(backgroundType);
}
}
}
function mouseMetadataJSON(uint256 tokenId, uint256 level) private view returns (string memory) {
uint256 dna = madmouse.getDNA(tokenId);
bool mintAndStake = madmouse._tokenDataOf(tokenId).mintAndStake();
string memory name = madmouse.mouseName(tokenId);
string memory bio = madmouse.mouseBio(tokenId);
if (bytes(name).length == 0) name = string.concat('Mad Mouse #', tokenId.toString());
if (bytes(bio).length == 0) bio = madmouse.description();
string memory imageURI = string.concat(
madmouse.imagesBaseURI(),
((level - 1) * 10_000 + tokenId).toString(),
'.png'
);
string memory baseData = string.concat(
MetadataEncode.keyValueString('name', name),
MetadataEncode.keyValueString('description', bio),
MetadataEncode.keyValueString('image', imageURI),
MetadataEncode.keyValue('id', tokenId.toString()),
MetadataEncode.keyValueString('dna', dna.toHexString())
);
string memory result = string.concat(
'{',
baseData,
MetadataEncode.keyValueString('OG', mintAndStake ? 'Staker' : ''),
MetadataEncode.attributes(getAttributesList(dna, level)),
'}'
);
return result;
}
function getAttributesList(uint256 dna, uint256 level) private pure returns (string memory) {
Mouse memory mouse = getMouse(dna, level);
string memory attributes = string.concat(
MetadataEncode.attribute('Level', level.toString()),
MetadataEncode.attributeString('Role', mouse.role),
MetadataEncode.attributeString('Rarity', mouse.rarity),
MetadataEncode.attributeString('Background', mouse.background),
MetadataEncode.attributeString('Scene', mouse.scene),
MetadataEncode.attributeString('Fur', mouse.fur)
);
attributes = string.concat(
attributes,
MetadataEncode.attributeString('Expression', mouse.expression),
MetadataEncode.attributeString('Glasses', mouse.glasses),
MetadataEncode.attributeString('Hat', mouse.hat),
MetadataEncode.attributeString('Makeup', mouse.makeup),
MetadataEncode.attributeString('Body', mouse.body, false)
);
return attributes;
}
}
文件 21 的 27:MadMouseStaking.sol
pragma solidity ^0.8.0;
import './Gouda.sol';
import './lib/ERC721M.sol';
import './lib/Ownable.sol';
error InvalidBoostToken();
error TransferFailed();
error BoostInEffect();
error NotSpecialGuestOwner();
error SpecialGuestIndexMustDiffer();
abstract contract MadMouseStaking is ERC721M, Ownable {
using UserDataOps for uint256;
event BoostActivation(address token);
Gouda public gouda;
uint256 constant dailyReward = 1e18;
uint256 constant ROLE_BONUS_3 = 2000;
uint256 constant ROLE_BONUS_5 = 3500;
uint256 constant TOKEN_BONUS = 1000;
uint256 constant TIME_BONUS = 1000;
uint256 constant RARITY_BONUS = 1000;
uint256 constant OG_BONUS = 2000;
uint256 constant SPECIAL_GUEST_BONUS = 1000;
uint256 immutable OG_BONUS_END;
uint256 immutable LAST_GOUDA_EMISSION_DATE;
uint256 constant TOKEN_BOOST_DURATION = 9 days;
uint256 constant TOKEN_BOOST_COOLDOWN = 9 days;
uint256 constant TIME_BONUS_STAKE_DURATION = 30 days;
mapping(IERC20 => uint256) tokenBoostCosts;
mapping(uint256 => IERC721) specialGuests;
mapping(IERC721 => bytes4) specialGuestsNumStakedSelector;
address constant burnAddress = 0x000000000000000000000000000000000000dEaD;
constructor(uint256 maxSupply_, uint256 maxPerWallet_)
ERC721M('MadMouseCircus', 'MMC', 1, maxSupply_, maxPerWallet_)
{
OG_BONUS_END = block.timestamp + 60 days;
LAST_GOUDA_EMISSION_DATE = block.timestamp + 5 * 365 days;
}
function burnForBoost(IERC20 token) external payable {
uint256 userData = _claimReward();
uint256 boostCost = tokenBoostCosts[token];
if (boostCost == 0) revert InvalidBoostToken();
bool success = token.transferFrom(msg.sender, burnAddress, boostCost);
if (!success) revert TransferFailed();
uint256 boostStart = userData.boostStart();
if (boostStart + TOKEN_BOOST_DURATION + TOKEN_BOOST_COOLDOWN > block.timestamp) revert BoostInEffect();
_userData[msg.sender] = userData.setBoostStart(block.timestamp);
emit BoostActivation(address(token));
}
function claimSpecialGuest(uint256 collectionIndex) external payable {
uint256 userData = _claimReward();
uint256 specialGuestIndexOld = userData.specialGuestIndex();
if (collectionIndex == specialGuestIndexOld) revert SpecialGuestIndexMustDiffer();
if (collectionIndex != 0 && !hasSpecialGuest(msg.sender, collectionIndex)) revert NotSpecialGuestOwner();
_userData[msg.sender] = userData.setSpecialGuestIndex(collectionIndex);
}
function clearSpecialGuestData() external payable {
_userData[msg.sender] = _userData[msg.sender].setSpecialGuestIndex(0);
}
function tokenBonus(uint256 userData) private view returns (uint256) {
unchecked {
uint256 lastClaimed = userData.lastClaimed();
uint256 boostEnd = userData.boostStart() + TOKEN_BOOST_DURATION;
if (lastClaimed > boostEnd) return 0;
if (block.timestamp <= boostEnd) return TOKEN_BONUS;
return (TOKEN_BONUS * (boostEnd - lastClaimed)) / (block.timestamp - lastClaimed);
}
}
function roleBonus(uint256 userData) private pure returns (uint256) {
uint256 numRoles = userData.uniqueRoleCount();
return numRoles < 3 ? 0 : numRoles < 5 ? ROLE_BONUS_3 : ROLE_BONUS_5;
}
function rarityBonus(uint256 userData) private pure returns (uint256) {
unchecked {
uint256 numStaked = userData.numStaked();
return numStaked == 0 ? 0 : (userData.rarityPoints() * RARITY_BONUS) / numStaked;
}
}
function OGBonus(uint256 userData) private view returns (uint256) {
unchecked {
uint256 count = userData.OGCount();
uint256 lastClaimed = userData.lastClaimed();
if (count == 0 || lastClaimed > OG_BONUS_END) return 0;
uint256 bonus = (count * OG_BONUS) / userData.numStaked();
if (block.timestamp <= OG_BONUS_END) return bonus;
return (bonus * (OG_BONUS_END - lastClaimed)) / (block.timestamp - lastClaimed);
}
}
function timeBonus(uint256 userData) private view returns (uint256) {
unchecked {
uint256 stakeStart = userData.stakeStart();
uint256 stakeBonusStart = stakeStart + TIME_BONUS_STAKE_DURATION;
if (block.timestamp < stakeBonusStart) return 0;
uint256 lastClaimed = userData.lastClaimed();
if (lastClaimed >= stakeBonusStart) return TIME_BONUS;
return (TIME_BONUS * (block.timestamp - stakeBonusStart)) / (block.timestamp - lastClaimed);
}
}
function hasSpecialGuest(address user, uint256 index) public view returns (bool) {
if (index == 0) return false;
if (index < 19) {
address[19] memory guests = [
0x0000000000000000000000000000000000000000,
0x4BB33f6E69fd62cf3abbcC6F1F43b94A5D572C2B,
0xbEA8123277142dE42571f1fAc045225a1D347977,
0x12d2D1beD91c24f878F37E66bd829Ce7197e4d14,
0x0c2E57EFddbA8c768147D1fdF9176a0A6EBd5d83,
0x6E5a65B5f9Dd7b1b08Ff212E210DCd642DE0db8B,
0x17eD38f5F519C6ED563BE6486e629041Bed3dfbC,
0xdd67892E722bE69909d7c285dB572852d5F8897C,
0x8a90CAb2b38dba80c64b7734e58Ee1dB38B8992e,
0x6F44Db5ed6b86d9cC6046D0C78B82caD9E600F6a,
0x219B8aB790dECC32444a6600971c7C3718252539,
0xC4a0b1E7AA137ADA8b2F911A501638088DFdD508,
0x9712228cEeDA1E2dDdE52Cd5100B88986d1Cb49c,
0x56b391339615fd0e88E0D370f451fA91478Bb20F,
0x648E8428e0104Ec7D08667866a3568a72Fe3898F,
0xd2F668a8461D6761115dAF8Aeb3cDf5F40C532C6,
0xbad6186E92002E312078b5a1dAfd5ddf63d3f731,
0xcB4307F1c3B5556256748DDF5B86E81258990B3C,
0x5c211B8E4f93F00E2BD68e82F4E00FbB3302b35c
];
if (IERC721(guests[index]).balanceOf(user) != 0) return true;
if (index == 10) return ISVSGraveyard(guests[index]).getBuriedCount(user) != 0;
else if (index == 12) return AWOO(guests[index]).getStakedAmount(user) != 0;
else if (index == 16) return CheethV2(guests[index]).stakedMiceQuantity(user) != 0;
} else {
IERC721 collection = specialGuests[index];
if (address(collection) != address(0)) {
if (collection.balanceOf(user) != 0) return true;
bytes4 selector = specialGuestsNumStakedSelector[collection];
if (selector != bytes4(0)) {
(bool success, bytes memory data) = address(collection).staticcall(
abi.encodeWithSelector(selector, user)
);
return success && abi.decode(data, (uint256)) != 0;
}
}
}
return false;
}
function specialGuestBonus(address user, uint256 userData) private view returns (uint256) {
uint256 index = userData.specialGuestIndex();
if (!hasSpecialGuest(user, index)) return 0;
return SPECIAL_GUEST_BONUS;
}
function _pendingReward(address user, uint256 userData) internal view override returns (uint256) {
uint256 lastClaimed = userData.lastClaimed();
if (lastClaimed == 0) return 0;
uint256 timestamp = min(LAST_GOUDA_EMISSION_DATE, block.timestamp);
unchecked {
uint256 delta = timestamp < lastClaimed ? 0 : timestamp - lastClaimed;
uint256 reward = (userData.baseReward() * delta * dailyReward) / (1 days);
if (reward == 0) return 0;
uint256 bonus = totalBonus(user, userData);
return (reward * (10000 + bonus)) / 10000;
}
}
function totalBonus(address user, uint256 userData) internal view returns (uint256) {
unchecked {
return
roleBonus(userData) +
specialGuestBonus(user, userData) +
rarityBonus(userData) +
OGBonus(userData) +
timeBonus(userData) +
tokenBonus(userData);
}
}
function _payoutReward(address user, uint256 reward) internal override {
if (reward > 0.0001 ether) gouda.mint(user, reward);
}
struct StakeInfo {
uint256 numStaked;
uint256 roleCount;
uint256 roleBonus;
uint256 specialGuestBonus;
uint256 tokenBoost;
uint256 stakeStart;
uint256 timeBonus;
uint256 rarityPoints;
uint256 rarityBonus;
uint256 OGCount;
uint256 OGBonus;
uint256 totalBonus;
uint256 multiplierBase;
uint256 dailyRewardBase;
uint256 dailyReward;
uint256 pendingReward;
int256 tokenBoostDelta;
uint256[3] levelBalances;
}
function getUserStakeInfo(address user) external view returns (StakeInfo memory info) {
unchecked {
uint256 userData = _userData[user];
info.numStaked = userData.numStaked();
info.roleCount = userData.uniqueRoleCount();
info.roleBonus = roleBonus(userData) / 100;
info.specialGuestBonus = specialGuestBonus(user, userData) / 100;
info.tokenBoost = (block.timestamp < userData.boostStart() + TOKEN_BOOST_DURATION) ? TOKEN_BONUS / 100 : 0;
info.stakeStart = userData.stakeStart();
info.timeBonus = (info.stakeStart > 0 &&
block.timestamp > userData.stakeStart() + TIME_BONUS_STAKE_DURATION)
? TIME_BONUS / 100
: 0;
info.OGCount = userData.OGCount();
info.OGBonus = (block.timestamp > OG_BONUS_END || userData.numStaked() == 0)
? 0
: (userData.OGCount() * OG_BONUS) / userData.numStaked() / 100;
info.rarityPoints = userData.rarityPoints();
info.rarityBonus = rarityBonus(userData) / 100;
info.totalBonus =
info.roleBonus +
info.specialGuestBonus +
info.tokenBoost +
info.timeBonus +
info.rarityBonus +
info.OGBonus;
info.multiplierBase = userData.baseReward();
info.dailyRewardBase = info.multiplierBase * dailyReward;
info.dailyReward = (info.dailyRewardBase * (100 + info.totalBonus)) / 100;
info.pendingReward = _pendingReward(user, userData);
info.tokenBoostDelta = int256(TOKEN_BOOST_DURATION) - int256(block.timestamp - userData.boostStart());
info.levelBalances = userData.levelBalances();
}
}
function setGoudaToken(Gouda gouda_) external payable onlyOwner {
gouda = gouda_;
}
function setSpecialGuests(IERC721[] calldata collections, uint256[] calldata indices) external payable onlyOwner {
for (uint256 i; i < indices.length; ++i) {
uint256 index = indices[i];
require(index != 0);
specialGuests[index] = collections[i];
}
}
function setSpecialGuestStakingSelector(IERC721 collection, bytes4 selector) external payable onlyOwner {
specialGuestsNumStakedSelector[collection] = selector;
}
function setBoostTokens(IERC20[] calldata _boostTokens, uint256[] calldata _boostCosts) external payable onlyOwner {
for (uint256 i; i < _boostTokens.length; ++i) tokenBoostCosts[_boostTokens[i]] = _boostCosts[i];
}
}
interface ISVSGraveyard {
function getBuriedCount(address burier) external view returns (uint256);
}
interface AWOO {
function getStakedAmount(address staker) external view returns (uint256);
}
interface CheethV2 {
function stakedMiceQuantity(address _address) external view returns (uint256);
}
文件 22 的 27:MetadataEncode.sol
pragma solidity ^0.8.12;
library MetadataEncode {
function encode(string[] memory strs) internal pure returns (bytes memory) {
bytes memory a;
for (uint256 i; i < strs.length; i++) {
if (i < strs.length - 1) a = abi.encodePacked(a, strs[i], bytes1(0));
else a = abi.encodePacked(a, strs[i]);
}
return a;
}
function decode(bytes memory input, uint256 index) internal pure returns (string memory) {
uint256 counter;
uint256 start;
uint256 end;
for (; end < input.length; end++) {
if (input[end] == 0x00) {
if (counter == index) return getSlice(input, start, end);
start = end + 1;
counter++;
}
}
return getSlice(input, start, end);
}
function getSlice(
bytes memory input,
uint256 start,
uint256 end
) internal pure returns (string memory) {
bytes memory out = new bytes(end - start);
for (uint256 i = 0; i < end - start; i++) out[i] = input[i + start];
return string(out);
}
function selectWeighted(
bytes memory traits,
uint256 r,
uint256 weights
) internal pure returns (string memory) {
uint256 index = selectWeighted(r, weights);
return decode(traits, index);
}
function selectWeighted(uint256 r, uint256 weights) private pure returns (uint256) {
unchecked {
for (uint256 i; i < 32; ++i) {
r -= (weights >> (i << 3)) & 0xFF;
if (r > 0xFF) return i;
}
}
return 666666;
}
function encode(uint256[] memory weights) internal pure returns (bytes32) {
uint256 r;
uint256 sum;
for (uint256 i; i < weights.length; i++) {
r |= weights[i] << (i << 3);
sum += weights[i];
}
require(sum == 256, 'Should sum to 256');
return bytes32(r);
}
function decode(bytes32 code, uint256 length) internal pure returns (uint256[] memory) {
uint256[] memory r = new uint256[](length);
for (uint256 i; i < length; i++) r[i] = uint256(code >> (i << 3)) & 0xFF;
return r;
}
function keyValue(string memory key, string memory value) internal pure returns (string memory) {
return bytes(value).length > 0 ? string.concat('"', key, '": ', value, ', ') : '';
}
function keyValueString(string memory key, string memory value) internal pure returns (string memory) {
return bytes(value).length > 0 ? string.concat('"', key, '": ', '"', value, '", ') : '';
}
function attributeString(string memory traitType, string memory value) internal pure returns (string memory) {
return attributeString(traitType, value, true);
}
function attributeString(
string memory traitType,
string memory value,
bool comma
) internal pure returns (string memory) {
return bytes(value).length > 0 ? attribute(traitType, string.concat('"', value, '"'), comma) : '';
}
function attribute(string memory traitType, string memory value) internal pure returns (string memory) {
return attribute(traitType, value, true);
}
function attribute(
string memory traitType,
string memory value,
bool comma
) internal pure returns (string memory) {
return
bytes(value).length > 0
? string.concat('{"trait_type": "', traitType, '", "value": ', value, '}', comma ? ', ' : '')
: '';
}
function attributes(string memory attr) internal pure returns (string memory) {
return string.concat('"attributes": [', attr, ']');
}
}
文件 23 的 27:Ownable.sol
pragma solidity ^0.8.0;
error CallerIsNotTheOwner();
abstract contract Ownable {
address _owner;
constructor() {
_owner = msg.sender;
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
if (msg.sender != _owner) revert CallerIsNotTheOwner();
_;
}
function transferOwnership(address newOwner) external onlyOwner {
_owner = newOwner;
}
}
文件 24 的 27:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
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);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
文件 25 的 27:VRFBase.sol
pragma solidity ^0.8.0;
import '@chainlink/contracts/src/v0.8/VRFConsumerBase.sol';
import './Ownable.sol';
error RandomSeedNotSet();
error RandomSeedAlreadySet();
contract VRFBase is VRFConsumerBase, Ownable {
bytes32 private immutable keyHash;
uint256 private immutable fee;
uint256 public randomSeed;
constructor(
bytes32 keyHash_,
uint256 fee_,
address vrfCoordinator_,
address link_
) VRFConsumerBase(vrfCoordinator_, link_) {
keyHash = keyHash_;
fee = fee_;
}
function requestRandomSeed() external payable virtual onlyOwner whenRandomSeedUnset {
requestRandomness(keyHash, fee);
}
function forceFulfillRandomness() external payable virtual onlyOwner whenRandomSeedUnset {
randomSeed = uint256(blockhash(block.number - 1));
}
function fulfillRandomness(bytes32, uint256 randomNumber) internal virtual override {
randomSeed = randomNumber;
}
function _shiftRandomSeed(uint256 randomNumber) internal {
randomSeed = uint256(keccak256(abi.encode(randomSeed, randomNumber)));
}
function randomSeedSet() public view returns (bool) {
return randomSeed > 0;
}
modifier whenRandomSeedSet() {
if (!randomSeedSet()) revert RandomSeedNotSet();
_;
}
modifier whenRandomSeedUnset() {
if (randomSeedSet()) revert RandomSeedAlreadySet();
_;
}
}
contract VRFBaseMainnet is
VRFBase(
0xAA77729D3466CA35AE8D28B3BBAC7CC36A5031EFDC430821C02BC31A238AF445,
2 * 1e18,
0xf0d54349aDdcf704F77AE15b96510dEA15cb7952,
0x514910771AF9Ca656af840dff83E8264EcF986CA
)
{
}
contract VRFBaseRinkeby is
VRFBase(
0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311,
0.1 * 1e18,
0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B,
0x01BE23585060835E02B77ef475b0Cc51aA1e0709
)
{}
contract VRFBaseMumbai is
VRFBase(
0x6e75b569a01ef56d18cab6a8e71e6600d6ce853834d4a5748b720d06f878b3a4,
0.0001 * 1e18,
0x8C7382F9D8f56b33781fE506E897a4F1e2d17255,
0x326C977E6efc84E512bB9C30f76E30c160eD06FB
)
{}
文件 26 的 27:VRFConsumerBase.sol
pragma solidity ^0.8.0;
import "./interfaces/LinkTokenInterface.sol";
import "./VRFRequestIDBase.sol";
abstract contract VRFConsumerBase is VRFRequestIDBase {
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal virtual;
uint256 private constant USER_SEED_PLACEHOLDER = 0;
function requestRandomness(bytes32 _keyHash, uint256 _fee) internal returns (bytes32 requestId) {
LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER));
uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]);
nonces[_keyHash] = nonces[_keyHash] + 1;
return makeRequestId(_keyHash, vRFSeed);
}
LinkTokenInterface internal immutable LINK;
address private immutable vrfCoordinator;
mapping(bytes32 => uint256)
private nonces;
constructor(address _vrfCoordinator, address _link) {
vrfCoordinator = _vrfCoordinator;
LINK = LinkTokenInterface(_link);
}
function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external {
require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill");
fulfillRandomness(requestId, randomness);
}
}
文件 27 的 27:VRFRequestIDBase.sol
pragma solidity ^0.8.0;
contract VRFRequestIDBase {
function makeVRFInputSeed(
bytes32 _keyHash,
uint256 _userSeed,
address _requester,
uint256 _nonce
) internal pure returns (uint256) {
return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce)));
}
function makeRequestId(bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed));
}
}
{
"compilationTarget": {
"contracts/MadMouse.sol": "MadMouse"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1337
},
"remappings": []
}
[{"inputs":[{"internalType":"bytes32","name":"secretHash_","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BoostInEffect","type":"error"},{"inputs":[],"name":"CallerIsNotTheOwner","type":"error"},{"inputs":[],"name":"CallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"CollectionAlreadyRevealed","type":"error"},{"inputs":[],"name":"CollectionNotRevealed","type":"error"},{"inputs":[],"name":"ContractCallNotAllowed","type":"error"},{"inputs":[],"name":"ExceedsLimit","type":"error"},{"inputs":[],"name":"ExceedsStakingLimit","type":"error"},{"inputs":[],"name":"IncorrectHash","type":"error"},{"inputs":[],"name":"IncorrectOwner","type":"error"},{"inputs":[],"name":"IncorrectValue","type":"error"},{"inputs":[],"name":"InvalidBoostToken","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidString","type":"error"},{"inputs":[],"name":"MaxLevelReached","type":"error"},{"inputs":[],"name":"MaxNumberReached","type":"error"},{"inputs":[],"name":"MinHoldDurationRequired","type":"error"},{"inputs":[],"name":"MintAndStakeMinHoldDurationNotReached","type":"error"},{"inputs":[],"name":"MintMaxSupplyReached","type":"error"},{"inputs":[],"name":"MintMaxWalletReached","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NonexistentToken","type":"error"},{"inputs":[],"name":"NotSpecialGuestOwner","type":"error"},{"inputs":[],"name":"PublicSaleNotActive","type":"error"},{"inputs":[],"name":"QueryForZeroAddress","type":"error"},{"inputs":[],"name":"RandomSeedAlreadySet","type":"error"},{"inputs":[],"name":"RandomSeedNotSet","type":"error"},{"inputs":[],"name":"SignatureExceedsLimit","type":"error"},{"inputs":[],"name":"SpecialGuestIndexMustDiffer","type":"error"},{"inputs":[],"name":"TokenDataAlreadySet","type":"error"},{"inputs":[],"name":"TokenIdUnstaked","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"WhitelistNotActive","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","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":false,"internalType":"address","name":"token","type":"address"}],"name":"BoostActivation","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":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"_tokenDataOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","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":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"burnForBoost","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"claimReward","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"collectionIndex","type":"uint256"}],"name":"claimSpecialGuest","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"clearSpecialGuestData","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forceFulfillRandomness","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getDNA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getLevel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserStakeInfo","outputs":[{"components":[{"internalType":"uint256","name":"numStaked","type":"uint256"},{"internalType":"uint256","name":"roleCount","type":"uint256"},{"internalType":"uint256","name":"roleBonus","type":"uint256"},{"internalType":"uint256","name":"specialGuestBonus","type":"uint256"},{"internalType":"uint256","name":"tokenBoost","type":"uint256"},{"internalType":"uint256","name":"stakeStart","type":"uint256"},{"internalType":"uint256","name":"timeBonus","type":"uint256"},{"internalType":"uint256","name":"rarityPoints","type":"uint256"},{"internalType":"uint256","name":"rarityBonus","type":"uint256"},{"internalType":"uint256","name":"OGCount","type":"uint256"},{"internalType":"uint256","name":"OGBonus","type":"uint256"},{"internalType":"uint256","name":"totalBonus","type":"uint256"},{"internalType":"uint256","name":"multiplierBase","type":"uint256"},{"internalType":"uint256","name":"dailyRewardBase","type":"uint256"},{"internalType":"uint256","name":"dailyReward","type":"uint256"},{"internalType":"uint256","name":"pendingReward","type":"uint256"},{"internalType":"int256","name":"tokenBoostDelta","type":"int256"},{"internalType":"uint256[3]","name":"levelBalances","type":"uint256[3]"}],"internalType":"struct MadMouseStaking.StakeInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"to","type":"address[]"}],"name":"giveAway","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"gouda","outputs":[{"internalType":"contract Gouda","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"hasSpecialGuest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imagesBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"levelUp","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"metadata","outputs":[{"internalType":"contract MadMouseMetadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bool","name":"stake","type":"bool"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"mouseBio","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"mouseName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multiSigTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"numMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"numOwned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"numStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"pendingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"level","type":"uint256"}],"name":"previewTokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicSaleActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomSeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomSeedSet","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"uint256","name":"randomness","type":"uint256"}],"name":"rawFulfillRandomness","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"recoverToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestRandomSeed","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"resetBio","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"resetName","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"_imagesBaseURI","type":"string"},{"internalType":"bytes32","name":"secretSeed_","type":"bytes32"}],"name":"reveal","outputs":[],"stateMutability":"payable","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":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"bio","type":"string"}],"name":"setBio","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_boostTokens","type":"address[]"},{"internalType":"uint256[]","name":"_boostCosts","type":"uint256[]"}],"name":"setBoostTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"description_","type":"string"}],"name":"setDescription","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract Gouda","name":"gouda_","type":"address"}],"name":"setGoudaToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setImagesBaseURI","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract MadMouseMetadata","name":"metadata_","type":"address"}],"name":"setMetadataAddress","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"name","type":"string"}],"name":"setName","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"setProfileUpdateMinHoldDuration","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"active","type":"bool"}],"name":"setPublicSaleActive","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setSignerAddress","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"collection","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"setSpecialGuestStakingSelector","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC721[]","name":"collections","type":"address[]"},{"internalType":"uint256[]","name":"indices","type":"uint256[]"}],"name":"setSpecialGuests","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"shouldUpdateStakedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"stake","outputs":[],"stateMutability":"payable","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":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"type_","type":"uint256"}],"name":"tokenIdsOf","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLevel2Reached","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLevel3Reached","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNumStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"trueOwnerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unstake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"updateStakedTokenData","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bool","name":"stake","type":"bool"}],"name":"whitelistMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"}]