编译器
0.8.13+commit.abaa5c0e
文件 1 的 24: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 的 24: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 的 24: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;
}
}
文件 4 的 24: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));
}
}
文件 5 的 24: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;
}
}
文件 6 的 24: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 {}
}
文件 7 的 24: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();
if (to == msg.sender) 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();
}
}
文件 8 的 24: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) {
unchecked {
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) {
unchecked {
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;
}
}
文件 9 的 24: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);
}
}
文件 10 的 24: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;
}
文件 11 的 24:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 12 的 24: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);
}
文件 13 的 24: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);
}
文件 14 的 24: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;
}
文件 15 的 24: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);
}
文件 16 的 24:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 17 的 24: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);
}
文件 18 的 24: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 = 0.3 * 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) 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);
}
文件 19 的 24:MadMouseTroupe.sol
pragma solidity ^0.8.13;
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 './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();
interface IMadMouseMetadata {
function buildMouseMetadata(uint256 tokenId, uint256 level) external view returns (string memory);
}
contract MadMouseTroupe 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 = 5000;
uint256 constant MAX_PER_WALLET = 50;
uint256 constant PURCHASE_LIMIT = 3;
uint256 constant whitelistPrice = 0.075 ether;
address 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 = 3300;
uint256 constant MAX_NUM_LEVEL_3 = 1200;
uint256 constant NAME_CHANGE_COST = 25 * 1e18;
uint256 constant BIO_CHANGE_COST = 15 * 1e18;
uint256 constant MAX_LEN_NAME = 20;
uint256 constant MAX_LEN_BIO = 35;
uint256 constant MINT_AND_STAKE_MIN_HOLD_DURATION = 3 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_;
_mintAndStake(multiSigTreasury, 30, false);
}
function mint(uint256 amount, bool stake) external payable noContract {
if (!publicSaleActive) revert PublicSaleNotActive();
if (PURCHASE_LIMIT < amount) revert ExceedsLimit();
uint256 price_ = price();
if (msg.value != price_ * amount) revert IncorrectValue();
if (price_ == 0) {
uint256 numMinted = _userData[msg.sender].numMinted();
if (numMinted + amount > PURCHASE_LIMIT) revert ExceedsLimit();
if (amount != 1) stake = true;
}
_mintAndStake(msg.sender, amount, stake);
}
function whitelistMint(
uint256 amount,
uint256 limit,
bytes calldata signature,
bool stake
) external payable noContract {
if (publicSaleActive) revert WhitelistNotActive();
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 price() public view returns (uint256) {
return totalSupply < 3500 ? 0 ether : .02 ether;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
if (!_exists(tokenId)) revert NonexistentToken();
if (!revealed || address(metadata) == address(0)) return unrevealedURI;
return IMadMouseMetadata(address(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 IMadMouseMetadata(address(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, uint256[] calldata amounts) external payable onlyOwner {
for (uint256 i; i < to.length; ++i) _mintAndStake(to[i], amounts[i], false);
}
function setSignerAddress(address address_) external payable onlyOwner {
signerAddress = address_;
}
function setMetadataAddress(address 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 的 24: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;
}
}
文件 21 的 24: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);
}
}
文件 22 的 24: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
)
{}
文件 23 的 24: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);
}
}
文件 24 的 24: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/MadMouseTroupe.sol": "MadMouseTroupe"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 932
},
"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":"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[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"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":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"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":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"address","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"}]