编译器
0.8.24+commit.e11b9ed9
文件 1 的 52:AccessControl.sol
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
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 returns (bool) {
return _roles[role].hasRole[account];
}
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
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 returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
文件 2 的 52:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 3 的 52:Arrays.sol
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
library Arrays {
using StorageSlot for bytes32;
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getAddressSlot();
}
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getBytes32Slot();
}
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getUint256Slot();
}
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
}
文件 4 的 52:ConfirmedOwner.sol
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}
文件 5 的 52:ConfirmedOwnerWithProposal.sol
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
function acceptOwnership() external override {
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
function owner() public view override returns (address) {
return s_owner;
}
function _transferOwnership(address to) private {
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
function _validateOwnership() internal view {
require(msg.sender == s_owner, "Only callable by owner");
}
modifier onlyOwner() {
_validateOwnership();
_;
}
}
文件 6 的 52:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 7 的 52:ERC1155.sol
pragma solidity ^0.8.20;
import {IERC1155} from "./IERC1155.sol";
import {IERC1155Receiver} from "./IERC1155Receiver.sol";
import {IERC1155MetadataURI} from "./extensions/IERC1155MetadataURI.sol";
import {Context} from "../../utils/Context.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {Arrays} from "../../utils/Arrays.sol";
import {IERC1155Errors} from "../../interfaces/draft-IERC6093.sol";
abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors {
using Arrays for uint256[];
using Arrays for address[];
mapping(uint256 id => mapping(address account => uint256)) private _balances;
mapping(address account => mapping(address operator => bool)) private _operatorApprovals;
string private _uri;
constructor(string memory uri_) {
_setURI(uri_);
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
super.supportsInterface(interfaceId);
}
function uri(uint256 ) public view virtual returns (string memory) {
return _uri;
}
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
return _balances[id][account];
}
function balanceOfBatch(
address[] memory accounts,
uint256[] memory ids
) public view virtual returns (uint256[] memory) {
if (accounts.length != ids.length) {
revert ERC1155InvalidArrayLength(ids.length, accounts.length);
}
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
}
return batchBalances;
}
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address account, address operator) public view virtual returns (bool) {
return _operatorApprovals[account][operator];
}
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeTransferFrom(from, to, id, value, data);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeBatchTransferFrom(from, to, ids, values, data);
}
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
if (ids.length != values.length) {
revert ERC1155InvalidArrayLength(ids.length, values.length);
}
address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids.unsafeMemoryAccess(i);
uint256 value = values.unsafeMemoryAccess(i);
if (from != address(0)) {
uint256 fromBalance = _balances[id][from];
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
unchecked {
_balances[id][from] = fromBalance - value;
}
}
if (to != address(0)) {
_balances[id][to] += value;
}
}
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, value);
} else {
emit TransferBatch(operator, from, to, ids, values);
}
}
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal virtual {
_update(from, to, ids, values);
if (to != address(0)) {
address operator = _msgSender();
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
_doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
} else {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
}
}
}
function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
function _setURI(string memory newuri) internal virtual {
_uri = newuri;
}
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
function _burn(address from, uint256 id, uint256 value) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC1155InvalidOperator(address(0));
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC1155InvalidReceiver(to);
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC1155InvalidReceiver(to);
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
function _asSingletonArrays(
uint256 element1,
uint256 element2
) private pure returns (uint256[] memory array1, uint256[] memory array2) {
assembly {
array1 := mload(0x40)
mstore(array1, 1)
mstore(add(array1, 0x20), element1)
array2 := add(array1, 0x40)
mstore(array2, 1)
mstore(add(array2, 0x20), element2)
mstore(0x40, add(array2, 0x40))
}
}
}
文件 8 的 52:ERC1155Burnable.sol
pragma solidity ^0.8.20;
import {ERC1155} from "../ERC1155.sol";
abstract contract ERC1155Burnable is ERC1155 {
function burn(address account, uint256 id, uint256 value) public virtual {
if (account != _msgSender() && !isApprovedForAll(account, _msgSender())) {
revert ERC1155MissingApprovalForAll(_msgSender(), account);
}
_burn(account, id, value);
}
function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual {
if (account != _msgSender() && !isApprovedForAll(account, _msgSender())) {
revert ERC1155MissingApprovalForAll(_msgSender(), account);
}
_burnBatch(account, ids, values);
}
}
文件 9 的 52:ERC1155Holder.sol
pragma solidity ^0.8.20;
import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
import {IERC1155Receiver} from "../IERC1155Receiver.sol";
abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
文件 10 的 52:ERC1155Supply.sol
pragma solidity ^0.8.20;
import {ERC1155} from "../ERC1155.sol";
abstract contract ERC1155Supply is ERC1155 {
mapping(uint256 id => uint256) private _totalSupply;
uint256 private _totalSupplyAll;
function totalSupply(uint256 id) public view virtual returns (uint256) {
return _totalSupply[id];
}
function totalSupply() public view virtual returns (uint256) {
return _totalSupplyAll;
}
function exists(uint256 id) public view virtual returns (bool) {
return totalSupply(id) > 0;
}
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
) internal virtual override {
super._update(from, to, ids, values);
if (from == address(0)) {
uint256 totalMintValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 value = values[i];
_totalSupply[ids[i]] += value;
totalMintValue += value;
}
_totalSupplyAll += totalMintValue;
}
if (to == address(0)) {
uint256 totalBurnValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 value = values[i];
unchecked {
_totalSupply[ids[i]] -= value;
totalBurnValue += value;
}
}
unchecked {
_totalSupplyAll -= totalBurnValue;
}
}
}
}
文件 11 的 52:ERC165.sol
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 12 的 52:ERC2981.sol
pragma solidity ^0.8.20;
import {IERC2981} from "../../interfaces/IERC2981.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
abstract contract ERC2981 is IERC2981, ERC165 {
struct RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
RoyaltyInfo private _defaultRoyaltyInfo;
mapping(uint256 tokenId => RoyaltyInfo) private _tokenRoyaltyInfo;
error ERC2981InvalidDefaultRoyalty(uint256 numerator, uint256 denominator);
error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);
error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);
error ERC2981InvalidTokenRoyaltyReceiver(uint256 tokenId, address receiver);
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual returns (address, uint256) {
RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
if (royalty.receiver == address(0)) {
royalty = _defaultRoyaltyInfo;
}
uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
return (royalty.receiver, royaltyAmount);
}
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
uint256 denominator = _feeDenominator();
if (feeNumerator > denominator) {
revert ERC2981InvalidDefaultRoyalty(feeNumerator, denominator);
}
if (receiver == address(0)) {
revert ERC2981InvalidDefaultRoyaltyReceiver(address(0));
}
_defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
}
function _deleteDefaultRoyalty() internal virtual {
delete _defaultRoyaltyInfo;
}
function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
uint256 denominator = _feeDenominator();
if (feeNumerator > denominator) {
revert ERC2981InvalidTokenRoyalty(tokenId, feeNumerator, denominator);
}
if (receiver == address(0)) {
revert ERC2981InvalidTokenRoyaltyReceiver(tokenId, address(0));
}
_tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
}
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
delete _tokenRoyaltyInfo[tokenId];
}
}
文件 13 的 52:ERC721.sol
pragma solidity ^0.8.20;
import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
string private _name;
string private _symbol;
mapping(uint256 tokenId => address) private _owners;
mapping(address owner => uint256) private _balances;
mapping(uint256 tokenId => address) private _tokenApprovals;
mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
function name() public view virtual returns (string memory) {
return _name;
}
function symbol() public view virtual returns (string memory) {
return _symbol;
}
function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
}
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(tokenId);
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
if (from != address(0)) {
_approve(address(0), tokenId, address(0), false);
unchecked {
_balances[from] -= 1;
}
}
if (to != address(0)) {
unchecked {
_balances[to] += 1;
}
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
return from;
}
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
function _transfer(address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}
文件 14 的 52:ERC721Burnable.sol
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {Context} from "../../../utils/Context.sol";
abstract contract ERC721Burnable is Context, ERC721 {
function burn(uint256 tokenId) public virtual {
_update(address(0), tokenId, _msgSender());
}
}
文件 15 的 52:ERC721Enumerable.sol
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {IERC721Enumerable} from "./IERC721Enumerable.sol";
import {IERC165} from "../../../utils/introspection/ERC165.sol";
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens;
mapping(uint256 tokenId => uint256) private _ownedTokensIndex;
uint256[] private _allTokens;
mapping(uint256 tokenId => uint256) private _allTokensIndex;
error ERC721OutOfBoundsIndex(address owner, uint256 index);
error ERC721EnumerableForbiddenBatchMint();
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
}
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
if (index >= balanceOf(owner)) {
revert ERC721OutOfBoundsIndex(owner, index);
}
return _ownedTokens[owner][index];
}
function totalSupply() public view virtual returns (uint256) {
return _allTokens.length;
}
function tokenByIndex(uint256 index) public view virtual returns (uint256) {
if (index >= totalSupply()) {
revert ERC721OutOfBoundsIndex(address(0), index);
}
return _allTokens[index];
}
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
address previousOwner = super._update(to, tokenId, auth);
if (previousOwner == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_removeTokenFromOwnerEnumeration(previousOwner, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_addTokenToOwnerEnumeration(to, tokenId);
}
return previousOwner;
}
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = balanceOf(to) - 1;
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
uint256 lastTokenIndex = balanceOf(from);
uint256 tokenIndex = _ownedTokensIndex[tokenId];
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId;
_ownedTokensIndex[lastTokenId] = tokenIndex;
}
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId;
_allTokensIndex[lastTokenId] = tokenIndex;
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
function _increaseBalance(address account, uint128 amount) internal virtual override {
if (amount > 0) {
revert ERC721EnumerableForbiddenBatchMint();
}
super._increaseBalance(account, amount);
}
}
文件 16 的 52:ERC721Pausable.sol
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {Pausable} from "../../../utils/Pausable.sol";
abstract contract ERC721Pausable is ERC721, Pausable {
function _update(
address to,
uint256 tokenId,
address auth
) internal virtual override whenNotPaused returns (address) {
return super._update(to, tokenId, auth);
}
}
文件 17 的 52:IAccessControl.sol
pragma solidity ^0.8.20;
interface IAccessControl {
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
error AccessControlBadConfirmation();
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 callerConfirmation) external;
}
文件 18 的 52:IERC1155.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
}
文件 19 的 52:IERC1155MetadataURI.sol
pragma solidity ^0.8.20;
import {IERC1155} from "../IERC1155.sol";
interface IERC1155MetadataURI is IERC1155 {
function uri(uint256 id) external view returns (string memory);
}
文件 20 的 52:IERC1155Receiver.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC1155Receiver is IERC165 {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
文件 21 的 52:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 22 的 52:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 23 的 52:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 24 的 52:IERC2981.sol
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";
interface IERC2981 is IERC165 {
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}
文件 25 的 52:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../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, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 26 的 52:IERC721Enumerable.sol
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 27 的 52:IERC721Metadata.sol
pragma solidity ^0.8.20;
import {IERC721} from "../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);
}
文件 28 的 52:IERC721Receiver.sol
pragma solidity ^0.8.20;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 29 的 52:IOwnable.sol
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}
文件 30 的 52:IPermissionCallable.sol
pragma solidity ^0.8.18;
interface IPermissionCallable {
function permissionedCall(bytes calldata call) external payable returns (bytes memory res);
function supportsPermissionedCallSelector(bytes4 selector) external view returns (bool supported);
}
文件 31 的 52:IVRFCoordinatorV2Plus.sol
pragma solidity ^0.8.0;
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
import {IVRFSubscriptionV2Plus} from "./IVRFSubscriptionV2Plus.sol";
interface IVRFCoordinatorV2Plus is IVRFSubscriptionV2Plus {
function requestRandomWords(VRFV2PlusClient.RandomWordsRequest calldata req) external returns (uint256 requestId);
}
文件 32 的 52:IVRFMigratableConsumerV2Plus.sol
pragma solidity ^0.8.0;
interface IVRFMigratableConsumerV2Plus {
event CoordinatorSet(address vrfCoordinator);
function setCoordinator(address vrfCoordinator) external;
}
文件 33 的 52:IVRFSubscriptionV2Plus.sol
pragma solidity ^0.8.0;
interface IVRFSubscriptionV2Plus {
function addConsumer(uint256 subId, address consumer) external;
function removeConsumer(uint256 subId, address consumer) external;
function cancelSubscription(uint256 subId, address to) external;
function acceptSubscriptionOwnerTransfer(uint256 subId) external;
function requestSubscriptionOwnerTransfer(uint256 subId, address newOwner) external;
function createSubscription() external returns (uint256 subId);
function getSubscription(
uint256 subId
)
external
view
returns (uint96 balance, uint96 nativeBalance, uint64 reqCount, address owner, address[] memory consumers);
function pendingRequestExists(uint256 subId) external view returns (bool);
function getActiveSubscriptionIds(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory);
function fundSubscriptionWithNative(uint256 subId) external payable;
}
文件 34 的 52:ManaSystem.sol
pragma solidity 0.8.24;
import "@openzeppelin/contracts/access/AccessControl.sol";
contract ManaSystem is AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant SPENDER_ROLE = keccak256("SPENDER_ROLE");
mapping(address => uint256) public userMana;
mapping(address => uint256) public lifetimeMana;
event ManaAdded(address indexed user, uint256 amount, uint256 newTotal, uint256 newLifetime);
event ManaSpent(address indexed user, uint256 amount, uint256 newTotal);
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function addMana(address user, uint256 amount) external onlyRole(MINTER_ROLE) {
userMana[user] += amount;
lifetimeMana[user] += amount;
emit ManaAdded(user, amount, userMana[user], lifetimeMana[user]);
}
function spendMana(address user, uint256 amount) external onlyRole(SPENDER_ROLE) {
require(userMana[user] >= amount, "Insufficient mana");
userMana[user] -= amount;
emit ManaSpent(user, amount, userMana[user]);
}
function getMana(address user) external view returns (uint256) {
return userMana[user];
}
function getLifetimeMana(address user) external view returns (uint256) {
return lifetimeMana[user];
}
}
文件 35 的 52:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 36 的 52:Multicall.sol
pragma solidity ^0.8.20;
import {Address} from "./Address.sol";
import {Context} from "./Context.sol";
abstract contract Multicall is Context {
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
}
return results;
}
}
文件 37 的 52:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 38 的 52:Pausable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Pausable is Context {
bool private _paused;
event Paused(address account);
event Unpaused(address account);
error EnforcedPause();
error ExpectedPause();
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 39 的 52:PermissionCallable.sol
pragma solidity ^0.8.23;
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
import {Multicall} from "openzeppelin-contracts/contracts/utils/Multicall.sol";
import {IPermissionCallable} from "./IPermissionCallable.sol";
abstract contract PermissionCallable is IPermissionCallable {
error NotPermissionCallable(bytes4 selector);
function permissionedCall(bytes calldata call) external payable returns (bytes memory res) {
if (!supportsPermissionedCallSelector(bytes4(call))) revert NotPermissionCallable(bytes4(call));
return Address.functionDelegateCall(address(this), call);
}
function supportsPermissionedCallSelector(bytes4 selector) public view virtual returns (bool);
}
文件 40 的 52:RandomnessHandler.sol
pragma solidity 0.8.24;
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
contract RandomnessHandler is VRFConsumerBaseV2Plus, AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant REQUESTER_ROLE = keccak256("REQUESTER_ROLE");
uint256 public s_subscriptionId;
address public vrfCoordinator;
bytes32 public s_keyHash;
uint32 public callbackGasLimit;
uint16 public requestConfirmations;
uint32 public numWords;
mapping(uint256 => address) private s_requesters;
mapping(address => uint256) private s_results;
mapping(address => uint256) private s_lastRequestId;
event RandomnessRequested(uint256 indexed requestId, address indexed requester);
event RandomnessFulfilled(uint256 indexed requestId, uint256 indexed result);
event VariablesUpdated(uint256 subscriptionId, bytes32 keyHash, uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords);
constructor(uint256 subscriptionId, address _vrfCoordinator, bytes32 _keyHash) VRFConsumerBaseV2Plus(_vrfCoordinator) {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
s_subscriptionId = subscriptionId;
vrfCoordinator = _vrfCoordinator;
s_keyHash = _keyHash;
callbackGasLimit = 100000;
requestConfirmations = 0;
numWords = 1;
}
function getRandomResult(address requester) external view onlyRole(REQUESTER_ROLE) returns (uint256) {
require(s_results[requester] != 0, "Randomness not fulfilled yet");
return s_results[requester];
}
function getLastRequestId(address requester) external view returns (uint256) {
require(s_lastRequestId[requester] != 0, "No request made for this address");
return s_lastRequestId[requester];
}
function requestRandomness(address requester) external onlyRole(REQUESTER_ROLE) returns (uint256 requestId) {
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: s_keyHash,
subId: s_subscriptionId,
requestConfirmations: requestConfirmations,
callbackGasLimit: callbackGasLimit,
numWords: numWords,
extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false}))
})
);
s_requesters[requestId] = requester;
s_results[requester] = 0;
s_lastRequestId[requester] = requestId;
emit RandomnessRequested(requestId, requester);
return requestId;
}
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
uint256 randomResult = randomWords[0];
address requester = s_requesters[requestId];
s_results[requester] = randomResult;
emit RandomnessFulfilled(requestId, randomResult);
}
function addRequester(address requester) external onlyRole(ADMIN_ROLE) {
grantRole(REQUESTER_ROLE, requester);
}
function removeRequester(address requester) external onlyRole(ADMIN_ROLE) {
revokeRole(REQUESTER_ROLE, requester);
}
function manuallyFulfillRandomness(uint256 requestId, uint256 randomResult) external onlyRole(ADMIN_ROLE) {
address requester = s_requesters[requestId];
require(requester != address(0), "Invalid request ID");
require(s_results[requester] == 0, "Randomness already fulfilled");
s_results[requester] = randomResult;
emit RandomnessFulfilled(requestId, randomResult);
}
}
文件 41 的 52:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 42 的 52:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 43 的 52:SignedMath.sol
pragma solidity ^0.8.20;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 44 的 52:StorageSlot.sol
pragma solidity ^0.8.20;
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly {
r.slot := slot
}
}
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly {
r.slot := slot
}
}
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly {
r.slot := slot
}
}
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly {
r.slot := slot
}
}
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly {
r.slot := store.slot
}
}
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly {
r.slot := slot
}
}
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly {
r.slot := store.slot
}
}
}
文件 45 的 52:Strings.sol
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
error StringsInsufficientHexLength(uint256 value, uint256 length);
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
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_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 46 的 52:TokiemonCapturing.sol
pragma solidity 0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokiemonItems.sol";
import "./TokiemonNFT.sol";
import "./RandomnessHandler.sol";
import "./ManaSystem.sol";
import "./TokiemonEquipment.sol";
using SafeERC20 for IERC20;
contract TokiemonCapturing is Ownable, ReentrancyGuard {
TokiemonItems public itemsContract;
TokiemonNFT public tokiemonNFT;
RandomnessHandler public randomnessHandler;
ManaSystem public manaSystem;
TokiemonEquipment public tokiemonEquipment;
uint256[5] public coinBagItemIds = [132, 133, 134, 135, 136];
uint256 public constant ENCOUNTER_PRECISION = 10000;
uint256 public encounterId = 50000;
uint256 public tokiedustItemId = 149;
uint256 public nextPassPackId;
bool public freePassMintingEnabled = false;
uint256 public passClaimCutoffId = 65000;
mapping(uint256 => uint256) public captureItemBaseRates;
mapping(address => EncounterInfo) public pendingEncounters;
mapping(uint256 => EncounterInfo) public encounterHistory;
mapping(uint256 => uint256) private passIdToTier;
mapping(uint256 => BattleRewardConfig) public tierBattleRewardConfigs;
mapping(uint256 => uint256[]) public tierSpecialRewards;
mapping(uint256 => uint256) public keyAmountProbabilities;
mapping(uint256 => mapping(uint256 => uint256)) public passCoinBagProbabilities;
mapping(uint256 => PassPack) public passPacks;
mapping(address => bool) public hasClaimedFreeTierPass;
mapping(uint256 => bool) public hasClaimedTokiemonPass;
mapping(uint256 => uint256) public levelRangeEncounterBonus;
mapping(uint256 => uint256) public tierDifficultyMultipliers;
enum CoinBagSize {
SMALL,
MEDIUM,
BIG,
GIANT,
MASSIVE
}
struct EncounterInfo {
uint256 passTier;
uint256 challengerTokenId;
bool isComplete;
uint256 encounterId;
uint256 startBlock;
}
struct BattleRewardConfig {
uint256 manaMin;
uint256 manaMax;
uint256 specialRewardProbability;
}
struct PassPack {
uint256 tier;
uint256 quantity;
uint256 price;
bool active;
}
struct ClaimablePassInfo {
bool canClaimFreeTierPass;
uint256[] tokiemonPassIds;
uint256[] tiers;
}
event EncounterStarted(address indexed user, uint256 indexed encounterId, uint256 passTier, uint256 challengerTokenId);
event BattleRewardsGranted(address indexed user, uint256 indexed encounterId, uint256 manaAmount, uint256[] itemIds, uint256[] amounts);
event PassPackPurchased(address indexed buyer, uint256 packId, uint256 tier, uint256 quantity);
event EncounterResult(
address indexed user,
uint256 indexed encounterId,
string encounterType,
uint256 passTier,
uint256 challengerTokenId,
uint256 successRate,
uint256 randomRoll,
bool success
);
constructor(
address _itemsContract,
address _tokiemonNFT,
address _randomnessHandler,
address _manaSystem,
address _tokiemonEquipment
) Ownable(msg.sender) {
itemsContract = TokiemonItems(_itemsContract);
tokiemonNFT = TokiemonNFT(_tokiemonNFT);
randomnessHandler = RandomnessHandler(_randomnessHandler);
manaSystem = ManaSystem(_manaSystem);
tokiemonEquipment = TokiemonEquipment(_tokiemonEquipment);
passIdToTier[126] = 1;
passIdToTier[127] = 2;
passIdToTier[128] = 3;
passIdToTier[129] = 4;
addPassPack(1, 3, 0.003 ether);
addPassPack(1, 10, 0.009 ether);
addPassPack(1, 25, 0.02 ether);
addPassPack(2, 3, 0.012 ether);
addPassPack(2, 10, 0.038 ether);
addPassPack(2, 25, 0.09 ether);
addPassPack(3, 3, 0.05 ether);
addPassPack(3, 10, 0.16 ether);
addPassPack(3, 25, 0.37 ether);
levelRangeEncounterBonus[30] = 0;
levelRangeEncounterBonus[60] = 1000;
levelRangeEncounterBonus[100] = 1500;
levelRangeEncounterBonus[150] = 2500;
levelRangeEncounterBonus[200] = 4000;
levelRangeEncounterBonus[250] = 5500;
levelRangeEncounterBonus[297] = 7000;
tierDifficultyMultipliers[4] = 4000;
tierDifficultyMultipliers[1] = 6000;
tierDifficultyMultipliers[2] = 8000;
tierDifficultyMultipliers[3] = 10000;
}
function getTierFromPassId(uint256 passId) public view returns (uint256) {
return passIdToTier[passId];
}
function isRandomnessFulfilled(address user) public view returns (bool) {
require(pendingEncounters[user].passTier > 0, "No pending encounter for this user");
return randomnessHandler.getRandomResult(user) != 0;
}
function getEncounterHistory(uint256 _encounterId) external view returns (EncounterInfo memory) {
require(_encounterId > 0 && _encounterId <= encounterId, "Invalid encounter ID");
return encounterHistory[_encounterId];
}
function getPassIdFromTier(uint256 tier) public view returns (uint256) {
for (uint256 id = 126; id <= 129; id++) {
if (passIdToTier[id] == tier) {
return id;
}
}
revert("Invalid tier");
}
function canClaimPasses(address user) public view returns (bool) {
ClaimablePassInfo memory info = getClaimablePassInfo(user);
return info.canClaimFreeTierPass || info.tokiemonPassIds.length > 0;
}
function getClaimablePassInfo(address user) public view returns (ClaimablePassInfo memory) {
uint256 balance = tokiemonNFT.balanceOf(user);
uint256[] memory eligibleTokenIds = new uint256[](balance);
uint256[] memory eligibleTiers = new uint256[](balance);
uint256 validCount = 0;
for (uint256 i = 0; i < balance; i++) {
uint256 tokenId = tokiemonNFT.tokenOfOwnerByIndex(user, i);
(, , uint256 tier, ) = tokiemonNFT.getTokiemonData(tokenId);
if (tier != 4 && tokenId >= passClaimCutoffId && !hasClaimedTokiemonPass[tokenId]) {
eligibleTokenIds[validCount] = tokenId;
eligibleTiers[validCount] = tier;
validCount++;
}
}
assembly {
mstore(eligibleTokenIds, validCount)
mstore(eligibleTiers, validCount)
}
return
ClaimablePassInfo({
canClaimFreeTierPass: freePassMintingEnabled && !hasClaimedFreeTierPass[user],
tokiemonPassIds: eligibleTokenIds,
tiers: eligibleTiers
});
}
function encounterSuccessRate(address user) public view returns (uint256) {
uint256 challengerTokenId = pendingEncounters[user].challengerTokenId;
uint256 passTier = pendingEncounters[user].passTier;
(, , uint256 challengerTier, TokiemonNFT.Rarity challengerRarity) = tokiemonNFT.getTokiemonData(challengerTokenId);
TokiemonEquipment.ItemEffect memory equipment = tokiemonEquipment.getTotalEquipmentEffect(challengerTokenId);
uint256 baseRate;
if (challengerTier == 4)
baseRate = 3000;
else if (challengerTier == 1)
baseRate = 4000;
else if (challengerTier == 2)
baseRate = 5000;
else if (challengerTier == 3) baseRate = 6000;
uint256 rarityBonus;
if (challengerRarity == TokiemonNFT.Rarity.Common)
rarityBonus = 0;
else if (challengerRarity == TokiemonNFT.Rarity.Uncommon)
rarityBonus = 250;
else if (challengerRarity == TokiemonNFT.Rarity.Rare)
rarityBonus = 500;
else if (challengerRarity == TokiemonNFT.Rarity.Epic)
rarityBonus = 750;
else if (challengerRarity == TokiemonNFT.Rarity.Legendary) rarityBonus = 1000;
uint256 totalLevel = uint256(tokiemonNFT.getTokiemonSkill(challengerTokenId, 0).level) +
uint256(tokiemonNFT.getTokiemonSkill(challengerTokenId, 1).level) +
uint256(tokiemonNFT.getTokiemonSkill(challengerTokenId, 2).level) +
equipment.attackBonus +
equipment.defenseBonus +
equipment.magicBonus;
uint256 levelBonus = calculateLevelBonus(totalLevel);
uint256 equipmentOverallBonus = equipment.overallBonus * 100;
uint256 totalRate = baseRate + levelBonus + rarityBonus + equipmentOverallBonus;
uint256 rateWithDifficulty = (totalRate * 10000) / tierDifficultyMultipliers[passTier];
return Math.min(rateWithDifficulty, 10000);
}
function calculateLevelBonus(uint256 totalLevel) public view returns (uint256) {
if (totalLevel <= 30) return levelRangeEncounterBonus[30];
if (totalLevel <= 60) return levelRangeEncounterBonus[60];
if (totalLevel <= 100) return levelRangeEncounterBonus[100];
if (totalLevel <= 150) return levelRangeEncounterBonus[150];
if (totalLevel <= 200) return levelRangeEncounterBonus[200];
if (totalLevel <= 250) return levelRangeEncounterBonus[250];
if (totalLevel <= 297) return levelRangeEncounterBonus[297];
return levelRangeEncounterBonus[297];
}
function startEncounter(uint256 passItemId, uint256 challengerTokenId) external nonReentrant {
require(pendingEncounters[msg.sender].passTier == 0, "Already has active encounter");
require(tokiemonNFT.ownerOf(challengerTokenId) == msg.sender, "Must own the Tokiemon");
require(itemsContract.balanceOf(msg.sender, passItemId) > 0, "Must have encounter pass");
uint256 tier = getTierFromPassId(passItemId);
require(tier > 0 && tier <= 4, "Invalid pass tier");
encounterId++;
itemsContract.spendItem(msg.sender, passItemId, 1);
uint256 requestId = randomnessHandler.requestRandomness(msg.sender);
require(requestId != 0, "Failed to request randomness");
pendingEncounters[msg.sender] = EncounterInfo({
passTier: tier,
challengerTokenId: challengerTokenId,
isComplete: false,
encounterId: encounterId,
startBlock: block.number
});
encounterHistory[encounterId] = EncounterInfo({
passTier: tier,
challengerTokenId: challengerTokenId,
isComplete: false,
encounterId: encounterId,
startBlock: block.number
});
emit EncounterStarted(msg.sender, encounterId, tier, challengerTokenId);
}
function attemptBattle() external nonReentrant {
(EncounterInfo memory encounter, uint256 randomNumber) = resolveEncounterBase();
uint256 successRate = encounterSuccessRate(msg.sender);
uint256 randomRoll = randomNumber % 10000;
bool success = randomRoll < successRate;
if (success) {
handleBattleRewards(msg.sender, encounter, randomNumber);
} else {
itemsContract.mint(msg.sender, tokiedustItemId, 1, "");
}
emit EncounterResult(
msg.sender,
encounter.encounterId,
"BATTLE",
encounter.passTier,
encounter.challengerTokenId,
successRate,
randomRoll,
success
);
encounterHistory[encounter.encounterId].isComplete = true;
delete pendingEncounters[msg.sender];
}
function attemptCapture(uint256 captureItemId, string memory communityId) external nonReentrant {
(EncounterInfo memory encounter, uint256 randomNumber) = resolveEncounterBase();
require(encounter.passTier != 4, "Cannot capture from free tier encounters");
require(captureItemBaseRates[captureItemId] > 0, "Invalid capture item");
require(itemsContract.balanceOf(msg.sender, captureItemId) > 0, "Must have capture item");
itemsContract.spendItem(msg.sender, captureItemId, 1);
uint256 successRate = (encounterSuccessRate(msg.sender) * captureItemBaseRates[captureItemId]) / 10000;
uint256 randomRoll = randomNumber % 10000;
bool success = randomRoll < successRate;
if (success) {
handleCaptureMint(msg.sender, encounter.passTier, communityId);
} else {
itemsContract.mint(msg.sender, tokiedustItemId, 1, "");
}
emit EncounterResult(
msg.sender,
encounter.encounterId,
"CAPTURE",
encounter.passTier,
encounter.challengerTokenId,
successRate,
randomRoll,
success
);
encounterHistory[encounter.encounterId].isComplete = true;
delete pendingEncounters[msg.sender];
}
function buyPassPack(uint256 packId) external payable nonReentrant {
PassPack memory pack = passPacks[packId];
require(pack.active, "Pass pack is not active");
require(msg.value >= pack.price, "Insufficient payment");
uint256 passId = getPassIdFromTier(pack.tier);
itemsContract.mint(msg.sender, passId, pack.quantity, "");
emit PassPackPurchased(msg.sender, packId, pack.tier, pack.quantity);
}
function claimPasses() external nonReentrant {
ClaimablePassInfo memory info = getClaimablePassInfo(msg.sender);
require((info.canClaimFreeTierPass && freePassMintingEnabled) || info.tokiemonPassIds.length > 0, "No passes available to claim");
if (info.canClaimFreeTierPass && freePassMintingEnabled) {
itemsContract.mint(msg.sender, getPassIdFromTier(4), 1, "");
hasClaimedFreeTierPass[msg.sender] = true;
}
for (uint256 i = 0; i < info.tokiemonPassIds.length; i++) {
itemsContract.mint(msg.sender, getPassIdFromTier(info.tiers[i]), 1, "");
hasClaimedTokiemonPass[info.tokiemonPassIds[i]] = true;
}
}
function resolveEncounterBase() internal view returns (EncounterInfo memory encounter, uint256 randomNumber) {
require(pendingEncounters[msg.sender].passTier > 0, "No active encounter");
require(isRandomnessFulfilled(msg.sender), "Randomness not fulfilled yet");
require(block.number > pendingEncounters[msg.sender].startBlock, "Must wait 1 block");
randomNumber = randomnessHandler.getRandomResult(msg.sender);
encounter = pendingEncounters[msg.sender];
return (encounter, randomNumber);
}
function handleBattleRewards(
address user,
EncounterInfo memory encounter,
uint256 randomNumber
) internal returns (uint256 manaAmount, uint256[] memory itemIds, uint256[] memory amounts) {
BattleRewardConfig memory config = tierBattleRewardConfigs[encounter.passTier];
itemIds = new uint256[](5);
amounts = new uint256[](5);
uint256 rewardCount;
manaAmount = config.manaMin + (randomNumber % (config.manaMax - config.manaMin + 1));
manaSystem.addMana(user, manaAmount);
uint256 coinBagRandom = uint256(keccak256(abi.encode(randomNumber, "COIN_BAG"))) % 10000;
uint256 cumulativeProbability = 0;
for (uint256 i = 0; i < uint256(type(CoinBagSize).max) + 1; i++) {
uint256 probability = passCoinBagProbabilities[encounter.passTier][i];
if (probability > 0) {
cumulativeProbability += probability;
if (coinBagRandom < cumulativeProbability) {
itemIds[rewardCount] = coinBagItemIds[i];
amounts[rewardCount] = 1;
rewardCount++;
break;
}
}
}
if (encounter.passTier < 4) {
uint256 keyRandom = uint256(keccak256(abi.encode(randomNumber, "KEY"))) % 10000;
uint256 cumulativeKeyProbability = 0;
for (uint256 i = 0; i <= 3; i++) {
cumulativeKeyProbability += keyAmountProbabilities[i];
if (keyRandom < cumulativeKeyProbability) {
if (i > 0) {
itemIds[rewardCount] = encounter.passTier;
amounts[rewardCount] = i;
rewardCount++;
}
break;
}
}
}
uint256[] storage possibleRewards = tierSpecialRewards[encounter.passTier];
if (possibleRewards.length > 0) {
uint256 specialRandom = uint256(keccak256(abi.encode(randomNumber, "SPECIAL"))) % 10000;
if (specialRandom < config.specialRewardProbability) {
uint256 selectedIndex = uint256(keccak256(abi.encode(randomNumber, "SPECIAL_INDEX"))) % possibleRewards.length;
itemIds[rewardCount] = possibleRewards[selectedIndex];
amounts[rewardCount] = 1;
rewardCount++;
}
}
if (rewardCount > 0) {
assembly {
mstore(itemIds, rewardCount)
mstore(amounts, rewardCount)
}
itemsContract.mintBatch(user, itemIds, amounts, "");
}
emit BattleRewardsGranted(user, encounter.encounterId, manaAmount, itemIds, amounts);
return (manaAmount, itemIds, amounts);
}
function handleCaptureMint(address to, uint256 tier, string memory communityId) internal returns (uint256) {
TokiemonNFT.Skill[] memory initialSkills = new TokiemonNFT.Skill[](3);
for (uint256 i = 0; i < 3; i++) {
initialSkills[i] = TokiemonNFT.Skill({level: 1, manaPerLevelMultiplier: 48, manaUntilNextLevel: 96, cumulativeMana: 0});
}
return tokiemonNFT.safeMint(to, tier, communityId, address(0xdead), initialSkills);
}
function setCaptureItemBaseRate(uint256 itemId, uint256 baseRate) external onlyOwner {
captureItemBaseRates[itemId] = baseRate;
}
function setTierRewardConfig(
uint256 tier,
uint256 manaMin,
uint256 manaMax,
uint256[] calldata specialRewardIds,
uint256 specialRewardProbability
) external onlyOwner {
BattleRewardConfig storage config = tierBattleRewardConfigs[tier];
config.manaMin = manaMin;
config.manaMax = manaMax;
config.specialRewardProbability = specialRewardProbability;
delete tierSpecialRewards[tier];
for (uint256 i = 0; i < specialRewardIds.length; i++) {
tierSpecialRewards[tier].push(specialRewardIds[i]);
}
}
function setItemsContract(address _itemsContract) external onlyOwner {
require(_itemsContract != address(0), "Invalid address");
itemsContract = TokiemonItems(_itemsContract);
}
function setTokiemonNFT(address _tokiemonNFT) external onlyOwner {
require(_tokiemonNFT != address(0), "Invalid address");
tokiemonNFT = TokiemonNFT(_tokiemonNFT);
}
function setRandomnessHandler(address _randomnessHandler) external onlyOwner {
require(_randomnessHandler != address(0), "Invalid address");
randomnessHandler = RandomnessHandler(_randomnessHandler);
}
function setManaSystem(address _manaSystem) external onlyOwner {
require(_manaSystem != address(0), "Invalid address");
manaSystem = ManaSystem(_manaSystem);
}
function setKeyAmountProbability(uint256 amount, uint256 probability) external onlyOwner {
require(probability <= 10000, "Invalid probability");
keyAmountProbabilities[amount] = probability;
}
function setCoinBagItemId(CoinBagSize size, uint256 itemId) external onlyOwner {
require(itemId > 0, "Invalid item ID");
coinBagItemIds[uint256(size)] = itemId;
}
function setPassCoinBagProbability(uint256 passTier, CoinBagSize size, uint256 probability) external onlyOwner {
require(probability <= 10000, "Invalid probability");
passCoinBagProbabilities[passTier][uint256(size)] = probability;
}
function setPassCoinBagProbabilities(
uint256 passTier,
CoinBagSize[] calldata sizes,
uint256[] calldata probabilities
) external onlyOwner {
require(sizes.length == probabilities.length, "Length mismatch");
for (uint256 i = 0; i < sizes.length; i++) {
require(probabilities[i] <= 10000, "Invalid probability");
passCoinBagProbabilities[passTier][uint256(sizes[i])] = probabilities[i];
}
}
function addPassPack(uint256 tier, uint256 quantity, uint256 price) public onlyOwner {
require(tier >= 1 && tier <= 3, "Invalid tier");
passPacks[nextPassPackId] = PassPack(tier, quantity, price, true);
nextPassPackId++;
}
function updatePassPack(uint256 packId, uint256 tier, uint256 quantity, uint256 price, bool active) external onlyOwner {
require(tier >= 1 && tier <= 3, "Invalid tier");
require(passPacks[packId].price > 0, "Pack does not exist");
passPacks[packId] = PassPack(tier, quantity, price, active);
}
function removePassPack(uint256 packId) external onlyOwner {
require(passPacks[packId].price > 0, "Pack does not exist");
passPacks[packId].active = false;
}
function setLevelRangeBonus(uint256 threshold, uint256 bonus) external onlyOwner {
require(bonus <= 10000, "Bonus cannot exceed 100%");
levelRangeEncounterBonus[threshold] = bonus;
}
function setTierDifficultyMultiplier(uint256 tier, uint256 multiplier) external onlyOwner {
require(tier >= 1 && tier <= 4, "Invalid tier");
require(multiplier > 0, "Multiplier must be positive");
tierDifficultyMultipliers[tier] = multiplier;
}
function setFreePassMintingEnabled(bool enabled) external onlyOwner {
freePassMintingEnabled = enabled;
}
function setPassClaimCutoffId(uint256 cutoffId) external onlyOwner {
passClaimCutoffId = cutoffId;
}
function setTokiedustItemId(uint256 newItemId) external onlyOwner {
require(newItemId > 0, "Invalid item ID");
tokiedustItemId = newItemId;
}
function withdrawFunds(address payable recipient) external onlyOwner {
uint256 balance = address(this).balance;
require(recipient != address(0), "Invalid recipient");
(bool success, ) = recipient.call{value: balance}("");
require(success, "Transfer failed");
}
function forceResolveStuckEncounter(address user) external onlyOwner {
require(pendingEncounters[user].passTier > 0, "No active encounter for this user");
uint256 currentEncounterId = pendingEncounters[user].encounterId;
encounterHistory[currentEncounterId].isComplete = true;
delete pendingEncounters[user];
emit EncounterResult(
user,
currentEncounterId,
"FORCE_RESOLVED",
0,
0,
0,
0,
false
);
}
function batchMarkTokiemonPassesClaimed(uint256[] calldata tokenIds) external onlyOwner {
for (uint256 i = 0; i < tokenIds.length; i++) {
hasClaimedTokiemonPass[tokenIds[i]] = true;
}
}
receive() external payable {}
}
文件 47 的 52:TokiemonEquipment.sol
pragma solidity 0.8.24;
import {PermissionCallable} from "./permissions/PermissionCallable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./TokiemonNFT.sol";
import "./TokiemonItems.sol";
contract TokiemonEquipment is ERC1155Holder, AccessControl, ReentrancyGuard, PermissionCallable {
TokiemonNFT public tokiemonNFT;
ITokiemonItems public tokiemonItems;
uint256 public tokiemonSkillCount = 3;
mapping(uint256 => mapping(Slot => uint256)) public equippedItems;
mapping(uint256 => ItemConfig) public itemConfigs;
enum Slot {
None,
Head,
Weapon,
Torso,
Offhand,
Legs,
Special,
Mystical,
Amazing,
Superior
}
struct ItemConfig {
Slot primarySlot;
Slot secondarySlot;
SkillRequirement[5] skillRequirements;
ItemEffect effect;
uint8 skillRequirementCount;
}
struct SkillRequirement {
uint256 skillId;
uint8 skillLevel;
bool isOverallSkill;
}
struct ItemEffect {
uint256 attackBonus;
uint256 defenseBonus;
uint256 magicBonus;
uint256 overallBonus;
}
event ItemConfigured(
uint256 itemId,
Slot primarySlot,
Slot secondarySlot,
SkillRequirement[] skillRequirements,
uint256 attackBonus,
uint256 defenseBonus,
uint256 magicBonus,
uint256 overallBonus
);
event TokiemonEquipmentUpdated(uint256 indexed tokenId, uint256[9] equippedItems);
constructor(address _tokiemonNFT, address _tokiemonItems) {
tokiemonNFT = TokiemonNFT(_tokiemonNFT);
tokiemonItems = ITokiemonItems(_tokiemonItems);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function getEquippedItem(uint256 tokenId, Slot slot) external view returns (uint256) {
return equippedItems[tokenId][slot];
}
function getAllEquippedItems(uint256 tokenId) external view returns (uint256[9] memory) {
uint256[9] memory items;
for (uint256 i = 1; i <= 9; i++) {
items[i - 1] = equippedItems[tokenId][Slot(i)];
}
return items;
}
function getTotalEquipmentEffect(uint256 tokenId) public view returns (ItemEffect memory) {
ItemEffect memory totalEffect;
for (uint256 i = 1; i <= uint256(Slot.Superior); i++) {
uint256 itemId = equippedItems[tokenId][Slot(i)];
if (itemId != 0) {
ItemConfig memory config = itemConfigs[itemId];
totalEffect.attackBonus += config.effect.attackBonus;
totalEffect.defenseBonus += config.effect.defenseBonus;
totalEffect.magicBonus += config.effect.magicBonus;
totalEffect.overallBonus += config.effect.overallBonus;
}
}
return totalEffect;
}
function getItemConfigData(
uint256 itemId
)
external
view
returns (
Slot primarySlot,
Slot secondarySlot,
SkillRequirement[5] memory skillRequirements,
ItemEffect memory effect,
uint8 skillRequirementCount
)
{
ItemConfig memory config = itemConfigs[itemId];
return (config.primarySlot, config.secondarySlot, config.skillRequirements, config.effect, config.skillRequirementCount);
}
function equipItem(uint256 tokenId, uint256 itemId) external nonReentrant {
require(tokiemonNFT.ownerOf(tokenId) == msg.sender, "Not the owner of the Tokiemon");
_equipItem(tokenId, itemId);
_emitEquipmentUpdate(tokenId);
}
function unequipItem(uint256 tokenId, Slot slot) external nonReentrant {
require(tokiemonNFT.ownerOf(tokenId) == msg.sender, "Not the owner of the Tokiemon");
_unequipItem(tokenId, slot);
_emitEquipmentUpdate(tokenId);
}
function bulkEquipItems(uint256 tokenId, uint256[] calldata itemIds) external nonReentrant {
require(tokiemonNFT.ownerOf(tokenId) == msg.sender, "Not the owner of the Tokiemon");
require(itemIds.length > 0, "No items to equip");
for (uint256 i = 0; i < itemIds.length; i++) {
_equipItem(tokenId, itemIds[i]);
}
_emitEquipmentUpdate(tokenId);
}
function bulkUnequipItems(uint256 tokenId, Slot[] calldata slots) external nonReentrant {
require(tokiemonNFT.ownerOf(tokenId) == msg.sender, "Not the owner of the Tokiemon");
require(slots.length > 0, "No slots to unequip");
for (uint256 i = 0; i < slots.length; i++) {
_unequipItem(tokenId, slots[i]);
}
_emitEquipmentUpdate(tokenId);
}
function updateAllEquipment(uint256 tokenId, uint256[9] calldata itemIds) external nonReentrant {
require(tokiemonNFT.ownerOf(tokenId) == msg.sender, "Not the owner of the Tokiemon");
for (uint256 i = 0; i < 9; i++) {
Slot slot = Slot(i + 1);
uint256 itemId = itemIds[i];
if (itemId == 0) {
if (equippedItems[tokenId][slot] != 0) {
_unequipItem(tokenId, slot);
}
} else {
_equipItem(tokenId, itemId);
}
}
_emitEquipmentUpdate(tokenId);
}
function _equipItem(uint256 tokenId, uint256 itemId) internal returns (Slot) {
ItemConfig memory config = itemConfigs[itemId];
require(config.primarySlot != Slot.None, "Item is not equippable");
uint256 totalSkillLevel = 0;
TokiemonNFT.Skill[3] memory skills;
for (uint256 i = 0; i < tokiemonSkillCount; i++) {
skills[i] = tokiemonNFT.getTokiemonSkill(tokenId, i);
totalSkillLevel += skills[i].level;
}
for (uint8 i = 0; i < config.skillRequirementCount; i++) {
SkillRequirement memory req = config.skillRequirements[i];
if (req.isOverallSkill) {
require(totalSkillLevel >= req.skillLevel, "Tokiemon does not meet the required overall skill level");
} else {
require(req.skillId < tokiemonSkillCount, "Invalid skill ID");
require(skills[req.skillId].level >= req.skillLevel, "Tokiemon does not meet the required skill level");
}
}
bool alreadyEquipped = equippedItems[tokenId][config.primarySlot] == itemId &&
(config.secondarySlot == Slot.None || equippedItems[tokenId][config.secondarySlot] == itemId);
if (alreadyEquipped) {
return config.primarySlot;
}
uint256[] memory itemIds = new uint256[](1);
itemIds[0] = itemId;
uint256[] memory amounts = new uint256[](1);
amounts[0] = 1;
tokiemonItems.whitelistTransfer(msg.sender, address(this), itemIds, amounts);
if (equippedItems[tokenId][config.primarySlot] != 0) {
_unequipItem(tokenId, config.primarySlot);
}
if (config.secondarySlot != Slot.None && equippedItems[tokenId][config.secondarySlot] != 0) {
_unequipItem(tokenId, config.secondarySlot);
}
equippedItems[tokenId][config.primarySlot] = itemId;
if (config.secondarySlot != Slot.None) {
equippedItems[tokenId][config.secondarySlot] = itemId;
}
return config.primarySlot;
}
function _unequipItem(uint256 tokenId, Slot slot) internal returns (uint256) {
uint256 itemId = equippedItems[tokenId][slot];
require(itemId != 0, "No item equipped in this slot");
tokiemonItems.safeTransferFrom(address(this), tokiemonNFT.ownerOf(tokenId), itemId, 1, "");
ItemConfig memory config = itemConfigs[itemId];
delete equippedItems[tokenId][slot];
if (config.primarySlot != slot && equippedItems[tokenId][config.primarySlot] == itemId) {
delete equippedItems[tokenId][config.primarySlot];
}
if (config.secondarySlot != Slot.None && config.secondarySlot != slot && equippedItems[tokenId][config.secondarySlot] == itemId) {
delete equippedItems[tokenId][config.secondarySlot];
}
return itemId;
}
function _emitEquipmentUpdate(uint256 tokenId) internal {
uint256[9] memory items;
for (uint256 i = 1; i <= 9; i++) {
items[i - 1] = equippedItems[tokenId][Slot(i)];
}
emit TokiemonEquipmentUpdated(tokenId, items);
}
function setTokiemonSkillCount(uint256 newCount) external onlyRole(DEFAULT_ADMIN_ROLE) {
tokiemonSkillCount = newCount;
}
function configureItem(
uint256 itemId,
Slot primarySlot,
Slot secondarySlot,
SkillRequirement[] memory skillRequirements,
ItemEffect memory effect
) public onlyRole(DEFAULT_ADMIN_ROLE) {
_configureItem(itemId, primarySlot, secondarySlot, skillRequirements, effect);
}
function configureMultipleItems(
uint256[] calldata itemIds,
Slot[] calldata primarySlots,
Slot[] calldata secondarySlots,
SkillRequirement[][] calldata skillRequirements,
ItemEffect[] calldata effects
) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(
itemIds.length == primarySlots.length &&
itemIds.length == secondarySlots.length &&
itemIds.length == skillRequirements.length &&
itemIds.length == effects.length,
"Input arrays must have the same length"
);
for (uint256 i = 0; i < itemIds.length; i++) {
_configureItem(itemIds[i], primarySlots[i], secondarySlots[i], skillRequirements[i], effects[i]);
}
}
function _configureItem(
uint256 itemId,
Slot primarySlot,
Slot secondarySlot,
SkillRequirement[] memory skillRequirements,
ItemEffect memory effect
) internal {
require(primarySlot != Slot.None, "Primary slot cannot be None");
require(skillRequirements.length <= 5, "Too many skill requirements");
ItemConfig storage config = itemConfigs[itemId];
config.primarySlot = primarySlot;
config.secondarySlot = secondarySlot;
config.effect = effect;
config.skillRequirementCount = uint8(skillRequirements.length);
for (uint8 i = 0; i < skillRequirements.length; i++) {
config.skillRequirements[i] = skillRequirements[i];
}
emit ItemConfigured(
itemId,
primarySlot,
secondarySlot,
skillRequirements,
effect.attackBonus,
effect.defenseBonus,
effect.magicBonus,
effect.overallBonus
);
}
function emergencyWithdraw(
uint256[] calldata ids,
uint256[] calldata amounts,
address recipient
) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(ids.length == amounts.length, "Mismatched ids and amounts");
require(recipient != address(0), "Invalid recipient");
tokiemonItems.safeBatchTransferFrom(address(this), recipient, ids, amounts, "");
}
function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Holder, AccessControl) returns (bool) {
return super.supportsInterface(interfaceId);
}
function supportsPermissionedCallSelector(bytes4 ) public pure override returns (bool) {
return true;
}
}
文件 48 的 52:TokiemonItems.sol
pragma solidity 0.8.24;
import {PermissionCallable} from "./permissions/PermissionCallable.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
interface ITokiemonItems is IERC1155 {
function whitelistTransfer(address from, address to, uint256[] memory ids, uint256[] memory amounts) external;
}
contract TokiemonItems is ERC1155, AccessControl, ERC1155Burnable, ERC1155Supply, PermissionCallable {
bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant SPENDER_ROLE = keccak256("SPENDER_ROLE");
bytes32 public constant EQUIPMENT_ROLE = keccak256("EQUIPMENT_ROLE");
uint256 currentHighestId;
mapping(uint256 => ItemConfig) public itemConfigs;
mapping(address => bool) public whitelistedContracts;
enum TransferRestriction {
Transferable,
NonTransferable,
WhitelistOnly
}
enum ItemType {
Spendable,
Consumable,
Equipable,
Collectible,
Quest,
Other
}
struct ItemConfig {
TransferRestriction transferRestriction;
ItemType itemType;
uint256 maxSupply;
}
constructor() ERC1155("https://api.tokiemon.io/items/{id}") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
}
function getItemBalances(address account) public view returns (uint256[] memory ids, uint256[] memory balances) {
uint256 count = 0;
for (uint256 i = 1; i <= currentHighestId; i++) {
if (balanceOf(account, i) > 0) {
count++;
}
}
ids = new uint256[](count);
balances = new uint256[](count);
uint256 index = 0;
for (uint256 i = 1; i <= currentHighestId; i++) {
uint256 balance = balanceOf(account, i);
if (balance > 0) {
ids[index] = i;
balances[index] = balance;
index++;
}
}
return (ids, balances);
}
function isAvailable(uint256 id) external view returns (bool) {
ItemConfig memory config = itemConfigs[id];
if (config.maxSupply == 0) {
return true;
}
return totalSupply(id) < config.maxSupply;
}
function getItemConfigData(uint256 id) external view returns (
TransferRestriction transferRestriction,
ItemType itemType,
uint256 maxSupply
) {
ItemConfig memory config = itemConfigs[id];
return (config.transferRestriction, config.itemType, config.maxSupply);
}
function mint(address account, uint256 id, uint256 amount, bytes memory data) public onlyRole(MINTER_ROLE) {
ItemConfig memory config = itemConfigs[id];
if (config.maxSupply > 0) {
require(totalSupply(id) + amount <= config.maxSupply, "Exceeds maximum supply");
}
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public onlyRole(MINTER_ROLE) {
for (uint256 i = 0; i < ids.length; i++) {
ItemConfig memory config = itemConfigs[ids[i]];
if (config.maxSupply > 0) {
require(totalSupply(ids[i]) + amounts[i] <= config.maxSupply, "Exceeds maximum supply");
}
}
_mintBatch(to, ids, amounts, data);
}
function spendItem(address from, uint256 id, uint256 amount) public onlyRole(SPENDER_ROLE) {
_burn(from, id, amount);
}
function spendBatchItems(address from, uint256[] memory ids, uint256[] memory amounts) public onlyRole(SPENDER_ROLE) {
_burnBatch(from, ids, amounts);
}
function whitelistTransfer(address from, address to, uint256[] memory ids, uint256[] memory amounts) public onlyRole(EQUIPMENT_ROLE) {
_update(from, to, ids, amounts);
}
function setURI(string memory newuri) public onlyRole(URI_SETTER_ROLE) {
_setURI(newuri);
}
function setItemConfig(
uint256 id,
TransferRestriction restriction,
uint256 maxSupply,
ItemType itemType
) public onlyRole(DEFAULT_ADMIN_ROLE) {
_setAndUpdateHighestId(id, restriction, maxSupply, itemType);
}
function setBulkItemConfigs(
uint256[] memory ids,
TransferRestriction[] memory restrictions,
uint256[] memory maxSupplies,
ItemType[] memory itemTypes
) public onlyRole(DEFAULT_ADMIN_ROLE) {
require(
ids.length == restrictions.length && ids.length == maxSupplies.length && ids.length == itemTypes.length,
"Input arrays must have the same length"
);
for (uint256 i = 0; i < ids.length; i++) {
_setAndUpdateHighestId(ids[i], restrictions[i], maxSupplies[i], itemTypes[i]);
}
}
function _setAndUpdateHighestId(uint256 id, TransferRestriction restriction, uint256 maxSupply, ItemType itemType) internal {
itemConfigs[id] = ItemConfig(restriction, itemType, maxSupply);
if (id > currentHighestId) {
currentHighestId = id;
}
}
function setWhitelistedContract(address contractAddress, bool isWhitelisted) public onlyRole(DEFAULT_ADMIN_ROLE) {
whitelistedContracts[contractAddress] = isWhitelisted;
}
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal override(ERC1155, ERC1155Supply) {
for (uint256 i = 0; i < ids.length; i++) {
ItemConfig memory config = itemConfigs[ids[i]];
if (from != address(0) && to != address(0)) {
if (config.transferRestriction == TransferRestriction.NonTransferable) {
revert("Item is not transferable");
} else if (config.transferRestriction == TransferRestriction.WhitelistOnly) {
require(whitelistedContracts[from] || whitelistedContracts[to], "Either sender or recipient must be whitelisted");
}
}
}
super._update(from, to, ids, values);
}
function supportsInterface(bytes4 interfaceId) public view override(ERC1155, AccessControl) returns (bool) {
return super.supportsInterface(interfaceId);
}
function supportsPermissionedCallSelector(bytes4 ) public pure override returns (bool) {
return true;
}
}
文件 49 的 52:TokiemonNFT.sol
pragma solidity 0.8.24;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./ManaSystem.sol";
contract TokiemonNFT is ERC721, ERC721Enumerable, ERC721Pausable, AccessControl, ERC721Burnable, ERC2981, ReentrancyGuard {
using Strings for uint256;
uint16 public constant RARITY_PRECISION = 10000;
uint96 public constant DEFAULT_ROYALTY_PERCENTAGE = 250;
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
uint256 private _nextTokenId;
uint256[] public activeTiers;
ManaSystem public manaSystem;
address public royaltyReceiver;
struct TokiemonData {
string communityId;
string name;
uint256 purchaseTier;
Rarity rarity;
mapping(uint256 => Skill) skills;
}
mapping(uint256 => TokiemonData) private _tokiemonData;
mapping(uint256 => uint8) public skillMaxLevels;
mapping(uint256 => RarityProbabilities) public tierToRarityProbabilities;
enum Rarity {
Common,
Uncommon,
Rare,
Epic,
Legendary
}
struct RarityProbabilities {
uint16 common;
uint16 uncommon;
uint16 rare;
uint16 epic;
uint16 legendary;
}
struct Skill {
uint8 level;
uint32 manaPerLevelMultiplier;
uint32 manaUntilNextLevel;
uint256 cumulativeMana;
}
event SkillManaApplied(uint256 indexed tokenId, uint256 indexed skillId, uint256 manaAmount, uint8 newLevel, uint256 newMana);
event TokiemonSkillsUpdated(uint256 indexed tokenId, uint256[] skillIds, Skill[] updatedSkills);
event NameChanged(uint256 indexed tokenId, string newName);
event TokiemonMinted(
uint256 indexed tokenId,
address indexed to,
string communityId,
uint256 purchaseTier,
Rarity rarity,
address paymentToken
);
constructor(address defaultAdmin, address pauser, address minter) ERC721("Tokiemon", "TOKIE") {
_grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
_grantRole(PAUSER_ROLE, pauser);
_grantRole(MINTER_ROLE, minter);
royaltyReceiver = defaultAdmin;
_setDefaultRoyalty(royaltyReceiver, DEFAULT_ROYALTY_PERCENTAGE);
addRarityProbability(1, RarityProbabilities(6200, 3000, 500, 250, 50));
addRarityProbability(2, RarityProbabilities(3000, 3800, 2000, 1000, 200));
addRarityProbability(3, RarityProbabilities(500, 1500, 5000, 2500, 500));
skillMaxLevels[0] = 99;
skillMaxLevels[1] = 99;
skillMaxLevels[2] = 99;
}
function _baseURI() internal pure override returns (string memory) {
return "https://api.tokiemon.io/tokiemon/";
}
function getActiveTiers() public view returns (uint256[] memory) {
return activeTiers;
}
function getRarity(uint256 tokenId) public view returns (Rarity) {
_requireOwned(tokenId);
return _tokiemonData[tokenId].rarity;
}
function getTierRarityProbabilities(uint256 tier) public view returns (uint16, uint16, uint16, uint16, uint16) {
RarityProbabilities memory probs = tierToRarityProbabilities[tier];
return (probs.common, probs.uncommon, probs.rare, probs.epic, probs.legendary);
}
function _tierExists(uint256 tier) private view returns (bool) {
return tierToRarityProbabilities[tier].common != 0;
}
function getTokiemonSkill(uint256 tokenId, uint256 skillId) public view returns (Skill memory) {
_requireOwned(tokenId);
return _tokiemonData[tokenId].skills[skillId];
}
function getTokiemonData(
uint256 tokenId
) public view returns (string memory community, string memory name, uint256 purchaseTier, Rarity rarity) {
_requireOwned(tokenId);
TokiemonData storage data = _tokiemonData[tokenId];
return (data.communityId, data.name, data.purchaseTier, data.rarity);
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
function safeMint(
address to,
uint256 tier,
string memory communityId,
address paymentToken,
Skill[] memory initialSkills
) public onlyRole(MINTER_ROLE) returns (uint256) {
require(_tierExists(tier), "Invalid tier");
uint256 tokenId = _nextTokenId++;
TokiemonData storage newTokiemon = _tokiemonData[tokenId];
newTokiemon.communityId = communityId;
newTokiemon.name = string(abi.encodePacked("Tokiemon #", tokenId.toString()));
newTokiemon.purchaseTier = tier;
newTokiemon.rarity = _determineRarity(tier);
for (uint256 i = 0; i < initialSkills.length; i++) {
newTokiemon.skills[i] = initialSkills[i];
}
_safeMint(to, tokenId);
emit TokiemonMinted(tokenId, to, communityId, tier, newTokiemon.rarity, paymentToken);
return tokenId;
}
function _determineRarity(uint256 purchaseTier) private view returns (Rarity) {
RarityProbabilities memory probs = tierToRarityProbabilities[purchaseTier];
uint256 randomNumber = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, _nextTokenId, msg.sender))) %
RARITY_PRECISION;
uint256 cumulativeProbability = 0;
if (randomNumber < (cumulativeProbability += probs.common)) return Rarity.Common;
if (randomNumber < (cumulativeProbability += probs.uncommon)) return Rarity.Uncommon;
if (randomNumber < (cumulativeProbability += probs.rare)) return Rarity.Rare;
if (randomNumber < (cumulativeProbability += probs.epic)) return Rarity.Epic;
return Rarity.Legendary;
}
function levelUpSkill(uint256 tokenId, uint256 skillId, uint256 manaAmount) public nonReentrant {
require(msg.sender == ownerOf(tokenId), "Only owner can level up skills");
require(manaSystem.getMana(msg.sender) >= manaAmount, "Insufficient mana");
Skill storage skill = _tokiemonData[tokenId].skills[skillId];
require(skill.level < skillMaxLevels[skillId], "Skill already at maximum level");
manaSystem.spendMana(msg.sender, manaAmount);
uint8 newLevel = skill.level;
skill.cumulativeMana += manaAmount;
uint256 surplusMana = calculateAnyLevelCost(tokenId, skillId, newLevel + 1) - skill.manaUntilNextLevel;
uint256 manaAvailable = manaAmount + surplusMana;
while (manaAvailable >= calculateAnyLevelCost(tokenId, skillId, newLevel + 1) && newLevel < skillMaxLevels[skillId]) {
uint256 levelCost = calculateAnyLevelCost(tokenId, skillId, newLevel + 1);
manaAvailable -= levelCost;
newLevel++;
}
skill.level = newLevel;
skill.manaUntilNextLevel = uint32(calculateAnyLevelCost(tokenId, skillId, newLevel + 1) - manaAvailable);
emit SkillManaApplied(tokenId, skillId, manaAmount, newLevel, skill.manaUntilNextLevel);
}
function calculateAnyLevelCost(uint256 tokenId, uint256 skillId, uint8 level) public view returns (uint256) {
Skill memory skill = _tokiemonData[tokenId].skills[skillId];
return uint256(skill.manaPerLevelMultiplier) * uint256(level);
}
function getCumulativeManaRequiredToLevelUp(uint256 tokenId, uint256 skillId, uint8 level) public view returns (uint256) {
uint256 totalMana = 0;
Skill memory skill = _tokiemonData[tokenId].skills[skillId];
for (uint8 i = skill.level + 1; i <= level; i++) {
totalMana += i * skill.manaPerLevelMultiplier;
}
return totalMana;
}
function getNextLevelDelta(uint256 tokenId, uint256 skillId) public view returns (uint32) {
Skill memory skill = _tokiemonData[tokenId].skills[skillId];
return skill.manaUntilNextLevel;
}
function changeName(uint256 tokenId, string memory newName) public {
require(ownerOf(tokenId) == msg.sender, "Only token owner can change name");
_tokiemonData[tokenId].name = newName;
emit NameChanged(tokenId, newName);
}
function addRarityProbability(uint256 tier, RarityProbabilities memory probs) public onlyRole(DEFAULT_ADMIN_ROLE) {
require(!_tierExists(tier), "Tier already exists");
require(
probs.common + probs.uncommon + probs.rare + probs.epic + probs.legendary == RARITY_PRECISION,
"Probabilities must sum to 10000"
);
tierToRarityProbabilities[tier] = probs;
activeTiers.push(tier);
}
function updateRarityProbability(uint256 tier, RarityProbabilities memory probs) public onlyRole(DEFAULT_ADMIN_ROLE) {
require(_tierExists(tier), "Tier does not exist");
require(
probs.common + probs.uncommon + probs.rare + probs.epic + probs.legendary == RARITY_PRECISION,
"Probabilities must sum to 10000"
);
tierToRarityProbabilities[tier] = probs;
}
function removeRarityProbability(uint256 tier) public onlyRole(DEFAULT_ADMIN_ROLE) {
require(_tierExists(tier), "Tier does not exist");
for (uint256 i = 0; i < activeTiers.length; i++) {
if (activeTiers[i] == tier) {
activeTiers[i] = activeTiers[activeTiers.length - 1];
activeTiers.pop();
break;
}
}
delete tierToRarityProbabilities[tier];
}
function pause() public onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() public onlyRole(PAUSER_ROLE) {
_unpause();
}
function setManaSystem(address _manaSystem) external onlyRole(DEFAULT_ADMIN_ROLE) {
manaSystem = ManaSystem(_manaSystem);
}
function updateSkillMaxLevel(uint256 skillId, uint8 newMaxLevel) external onlyRole(DEFAULT_ADMIN_ROLE) {
skillMaxLevels[skillId] = newMaxLevel;
}
function setRoyaltyInfo(address receiver, uint96 feeNumerator) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setDefaultRoyalty(receiver, feeNumerator);
royaltyReceiver = receiver;
}
function updateTokiemonSkills(
uint256 tokenId,
uint256[] memory skillIds,
Skill[] memory updatedSkills
) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(skillIds.length == updatedSkills.length, "Skill IDs and updated skills must have the same length");
_requireOwned(tokenId);
for (uint256 i = 0; i < skillIds.length; i++) {
uint256 skillId = skillIds[i];
Skill memory updatedSkill = updatedSkills[i];
require(updatedSkill.level <= skillMaxLevels[skillId], "Skill level exceeds maximum");
_tokiemonData[tokenId].skills[skillId] = updatedSkill;
}
emit TokiemonSkillsUpdated(tokenId, skillIds, updatedSkills);
}
function _update(
address to,
uint256 tokenId,
address auth
) internal override(ERC721, ERC721Enumerable, ERC721Pausable) returns (address) {
return super._update(to, tokenId, auth);
}
function _increaseBalance(address account, uint128 value) internal override(ERC721, ERC721Enumerable) {
super._increaseBalance(account, value);
}
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable, AccessControl, ERC2981) returns (bool) {
return super.supportsInterface(interfaceId);
}
}
文件 50 的 52:VRFConsumerBaseV2Plus.sol
pragma solidity ^0.8.4;
import {IVRFCoordinatorV2Plus} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {IVRFMigratableConsumerV2Plus} from "./interfaces/IVRFMigratableConsumerV2Plus.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
abstract contract VRFConsumerBaseV2Plus is IVRFMigratableConsumerV2Plus, ConfirmedOwner {
error OnlyCoordinatorCanFulfill(address have, address want);
error OnlyOwnerOrCoordinator(address have, address owner, address coordinator);
error ZeroAddress();
IVRFCoordinatorV2Plus public s_vrfCoordinator;
constructor(address _vrfCoordinator) ConfirmedOwner(msg.sender) {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
}
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;
function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
if (msg.sender != address(s_vrfCoordinator)) {
revert OnlyCoordinatorCanFulfill(msg.sender, address(s_vrfCoordinator));
}
fulfillRandomWords(requestId, randomWords);
}
function setCoordinator(address _vrfCoordinator) external override onlyOwnerOrCoordinator {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
emit CoordinatorSet(_vrfCoordinator);
}
modifier onlyOwnerOrCoordinator() {
if (msg.sender != owner() && msg.sender != address(s_vrfCoordinator)) {
revert OnlyOwnerOrCoordinator(msg.sender, owner(), address(s_vrfCoordinator));
}
_;
}
}
文件 51 的 52:VRFV2PlusClient.sol
pragma solidity ^0.8.4;
library VRFV2PlusClient {
bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1"));
struct ExtraArgsV1 {
bool nativePayment;
}
struct RandomWordsRequest {
bytes32 keyHash;
uint256 subId;
uint16 requestConfirmations;
uint32 callbackGasLimit;
uint32 numWords;
bytes extraArgs;
}
function _argsToBytes(ExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs);
}
}
文件 52 的 52:draft-IERC6093.sol
pragma solidity ^0.8.20;
interface IERC20Errors {
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
error ERC20InvalidSender(address sender);
error ERC20InvalidReceiver(address receiver);
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
error ERC20InvalidApprover(address approver);
error ERC20InvalidSpender(address spender);
}
interface IERC721Errors {
error ERC721InvalidOwner(address owner);
error ERC721NonexistentToken(uint256 tokenId);
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
error ERC721InvalidSender(address sender);
error ERC721InvalidReceiver(address receiver);
error ERC721InsufficientApproval(address operator, uint256 tokenId);
error ERC721InvalidApprover(address approver);
error ERC721InvalidOperator(address operator);
}
interface IERC1155Errors {
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
error ERC1155InvalidSender(address sender);
error ERC1155InvalidReceiver(address receiver);
error ERC1155MissingApprovalForAll(address operator, address owner);
error ERC1155InvalidApprover(address approver);
error ERC1155InvalidOperator(address operator);
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
{
"compilationTarget": {
"src/TokiemonCapturing.sol": "TokiemonCapturing"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_itemsContract","type":"address"},{"internalType":"address","name":"_tokiemonNFT","type":"address"},{"internalType":"address","name":"_randomnessHandler","type":"address"},{"internalType":"address","name":"_manaSystem","type":"address"},{"internalType":"address","name":"_tokiemonEquipment","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"encounterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"manaAmount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"itemIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"BattleRewardsGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"encounterId","type":"uint256"},{"indexed":false,"internalType":"string","name":"encounterType","type":"string"},{"indexed":false,"internalType":"uint256","name":"passTier","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"challengerTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"successRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"randomRoll","type":"uint256"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"EncounterResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"encounterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"passTier","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"challengerTokenId","type":"uint256"}],"name":"EncounterStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"packId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tier","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"PassPackPurchased","type":"event"},{"inputs":[],"name":"ENCOUNTER_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tier","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"addPassPack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"attemptBattle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"captureItemId","type":"uint256"},{"internalType":"string","name":"communityId","type":"string"}],"name":"attemptCapture","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"batchMarkTokiemonPassesClaimed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"packId","type":"uint256"}],"name":"buyPassPack","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"totalLevel","type":"uint256"}],"name":"calculateLevelBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"canClaimPasses","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"captureItemBaseRates","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimPasses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"coinBagItemIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"encounterHistory","outputs":[{"internalType":"uint256","name":"passTier","type":"uint256"},{"internalType":"uint256","name":"challengerTokenId","type":"uint256"},{"internalType":"bool","name":"isComplete","type":"bool"},{"internalType":"uint256","name":"encounterId","type":"uint256"},{"internalType":"uint256","name":"startBlock","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"encounterId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"encounterSuccessRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"forceResolveStuckEncounter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freePassMintingEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getClaimablePassInfo","outputs":[{"components":[{"internalType":"bool","name":"canClaimFreeTierPass","type":"bool"},{"internalType":"uint256[]","name":"tokiemonPassIds","type":"uint256[]"},{"internalType":"uint256[]","name":"tiers","type":"uint256[]"}],"internalType":"struct TokiemonCapturing.ClaimablePassInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_encounterId","type":"uint256"}],"name":"getEncounterHistory","outputs":[{"components":[{"internalType":"uint256","name":"passTier","type":"uint256"},{"internalType":"uint256","name":"challengerTokenId","type":"uint256"},{"internalType":"bool","name":"isComplete","type":"bool"},{"internalType":"uint256","name":"encounterId","type":"uint256"},{"internalType":"uint256","name":"startBlock","type":"uint256"}],"internalType":"struct TokiemonCapturing.EncounterInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tier","type":"uint256"}],"name":"getPassIdFromTier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"passId","type":"uint256"}],"name":"getTierFromPassId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasClaimedFreeTierPass","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"hasClaimedTokiemonPass","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isRandomnessFulfilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"itemsContract","outputs":[{"internalType":"contract TokiemonItems","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"keyAmountProbabilities","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"levelRangeEncounterBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manaSystem","outputs":[{"internalType":"contract ManaSystem","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextPassPackId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"passClaimCutoffId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"passCoinBagProbabilities","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"passPacks","outputs":[{"internalType":"uint256","name":"tier","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pendingEncounters","outputs":[{"internalType":"uint256","name":"passTier","type":"uint256"},{"internalType":"uint256","name":"challengerTokenId","type":"uint256"},{"internalType":"bool","name":"isComplete","type":"bool"},{"internalType":"uint256","name":"encounterId","type":"uint256"},{"internalType":"uint256","name":"startBlock","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomnessHandler","outputs":[{"internalType":"contract RandomnessHandler","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"packId","type":"uint256"}],"name":"removePassPack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"itemId","type":"uint256"},{"internalType":"uint256","name":"baseRate","type":"uint256"}],"name":"setCaptureItemBaseRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum TokiemonCapturing.CoinBagSize","name":"size","type":"uint8"},{"internalType":"uint256","name":"itemId","type":"uint256"}],"name":"setCoinBagItemId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setFreePassMintingEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_itemsContract","type":"address"}],"name":"setItemsContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"probability","type":"uint256"}],"name":"setKeyAmountProbability","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"uint256","name":"bonus","type":"uint256"}],"name":"setLevelRangeBonus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manaSystem","type":"address"}],"name":"setManaSystem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cutoffId","type":"uint256"}],"name":"setPassClaimCutoffId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"passTier","type":"uint256"},{"internalType":"enum TokiemonCapturing.CoinBagSize[]","name":"sizes","type":"uint8[]"},{"internalType":"uint256[]","name":"probabilities","type":"uint256[]"}],"name":"setPassCoinBagProbabilities","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"passTier","type":"uint256"},{"internalType":"enum TokiemonCapturing.CoinBagSize","name":"size","type":"uint8"},{"internalType":"uint256","name":"probability","type":"uint256"}],"name":"setPassCoinBagProbability","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_randomnessHandler","type":"address"}],"name":"setRandomnessHandler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tier","type":"uint256"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"setTierDifficultyMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tier","type":"uint256"},{"internalType":"uint256","name":"manaMin","type":"uint256"},{"internalType":"uint256","name":"manaMax","type":"uint256"},{"internalType":"uint256[]","name":"specialRewardIds","type":"uint256[]"},{"internalType":"uint256","name":"specialRewardProbability","type":"uint256"}],"name":"setTierRewardConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newItemId","type":"uint256"}],"name":"setTokiedustItemId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokiemonNFT","type":"address"}],"name":"setTokiemonNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"passItemId","type":"uint256"},{"internalType":"uint256","name":"challengerTokenId","type":"uint256"}],"name":"startEncounter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tierBattleRewardConfigs","outputs":[{"internalType":"uint256","name":"manaMin","type":"uint256"},{"internalType":"uint256","name":"manaMax","type":"uint256"},{"internalType":"uint256","name":"specialRewardProbability","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tierDifficultyMultipliers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tierSpecialRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokiedustItemId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokiemonEquipment","outputs":[{"internalType":"contract TokiemonEquipment","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokiemonNFT","outputs":[{"internalType":"contract TokiemonNFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"packId","type":"uint256"},{"internalType":"uint256","name":"tier","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"name":"updatePassPack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"recipient","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]