Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
AccountManager
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 2000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "../interfaces/IAccountManager.sol";
import "../interfaces/IMigrationManager.sol";
import "../interfaces/ILockManager.sol";
import "../interfaces/IMunchNFT.sol";
import "../interfaces/INFTAttributesManager.sol";
import "../interfaces/IClaimManager.sol";
import "../interfaces/IRNGProxy.sol";
import "../interfaces/IBonusManager.sol";
import "../interfaces/ISnuggeryManager.sol";
import "../interfaces/ILandManager.sol";
import "./BaseBlastManagerUpgradeable.sol";
contract AccountManager is BaseBlastManagerUpgradeable, IAccountManager {
uint16 MAX_SCHNIBBLE_SPRAY;
mapping(address => MunchablesCommonLib.Player) players;
mapping(address => address) public mainAccounts;
mapping(address => address[]) public subAccounts;
mapping(address => SprayProposal) public sprayProposals;
mapping(address => uint256) public unclaimedSchnibbles;
mapping(address => bool) internal _tempSprayPlayerCheck;
ILockManager lockManager;
IMigrationManager migrationManager;
INFTAttributesManager nftAttributesManager;
IRNGProxy rngProxy;
IBonusManager bonusManager;
ISnuggeryManager snuggeryManager;
uint256 maxRewardSpray;
ILandManager landManager;
modifier onlyUnregistered(address account) {
if (players[account].registrationDate != 0)
revert PlayerAlreadyRegisteredError();
_;
}
modifier onlyRegistered(address account) {
if (players[account].registrationDate == 0)
revert PlayerNotRegisteredError();
_;
}
constructor() {
_disableInitializers();
}
function initialize(address _configStorage) public override initializer {
BaseBlastManagerUpgradeable.initialize(_configStorage);
_reconfigure();
}
function _reconfigure() internal {
// load config from the config storage contract and configure myself
lockManager = ILockManager(
configStorage.getAddress(StorageKey.LockManager)
);
// munchNFT = IMunchNFT(configStorage.getAddress(StorageKey.MunchNFT));
migrationManager = IMigrationManager(
configStorage.getAddress(StorageKey.MigrationManager)
);
rngProxy = IRNGProxy(
configStorage.getAddress(StorageKey.RNGProxyContract)
);
nftAttributesManager = INFTAttributesManager(
configStorage.getAddress(StorageKey.NFTAttributesManager)
);
bonusManager = IBonusManager(
configStorage.getAddress(StorageKey.BonusManager)
);
snuggeryManager = ISnuggeryManager(
configStorage.getAddress(StorageKey.SnuggeryManager)
);
MAX_SCHNIBBLE_SPRAY = uint16(
configStorage.getSmallInt(StorageKey.MaxSchnibbleSpray)
); // 100
// Min ETH Pet bonus = artifact, unused
maxRewardSpray = configStorage.getUint(StorageKey.MinETHPetBonus);
landManager = ILandManager(
// signifies landmanager
configStorage.getAddress(StorageKey.PrimordialsEnabled)
);
__BaseBlastManagerUpgradeable_reconfigure();
}
function configUpdated() external override onlyConfigStorage {
_reconfigure();
}
/// @inheritdoc IAccountManager
function register(
MunchablesCommonLib.Realm _snuggeryRealm,
address _referrer
) external onlyUnregistered(msg.sender) {
if (mainAccounts[msg.sender] != address(0))
_removeSubAccount(mainAccounts[msg.sender], msg.sender);
if (_snuggeryRealm >= MunchablesCommonLib.Realm.Invalid)
revert InvalidRealmError();
if (_referrer == msg.sender) revert SelfReferralError();
MunchablesCommonLib.Player memory player;
player.registrationDate = uint32(block.timestamp);
player.maxSnuggerySize = uint16(
configStorage.getSmallInt(StorageKey.DefaultSnuggerySize)
);
player.snuggeryRealm = _snuggeryRealm;
player.referrer = _referrer;
player.lastHarvestDate = uint32(block.timestamp);
if (unclaimedSchnibbles[msg.sender] != 0) {
player.unfedSchnibbles = unclaimedSchnibbles[msg.sender];
delete unclaimedSchnibbles[msg.sender];
}
players[msg.sender] = player;
emit PlayerRegistered(msg.sender, _snuggeryRealm, _referrer);
}
/// @inheritdoc IAccountManager
function harvest() external notPaused returns (uint256 _harvested) {
address _caller = _getMainAccountRequireRegistered(msg.sender);
_harvested = _harvest(_caller);
}
/// @inheritdoc IAccountManager
function forceHarvest(
address _player
)
external
onlyConfiguredContract2(
StorageKey.LockManager,
StorageKey.MunchadexManager
)
{
(, MunchablesCommonLib.Player memory player) = this.getPlayer(_player);
if (player.registrationDate != 0) {
_harvest(_player);
if (
msg.sender == configStorage.getAddress(StorageKey.LockManager)
) {
landManager.updatePlotMetadata(_player);
}
}
}
/// @inheritdoc IAccountManager
function spraySchnibblesPropose(
address[] calldata _players,
uint256[] calldata _schnibbles
)
external
onlyOneOfRoles(
[
Role.Social_1,
Role.Social_2,
Role.Social_3,
Role.Social_4,
Role.Social_5
]
)
{
address proposer = msg.sender;
uint256 numberEntries = _players.length;
if (numberEntries == 0) revert EmptyParameterError();
if (numberEntries != _schnibbles.length)
revert UnMatchedParametersError();
if (numberEntries > uint256(MAX_SCHNIBBLE_SPRAY))
revert TooManyEntriesError();
if (sprayProposals[proposer].proposedDate > 0)
revert ExistingProposalError();
delete sprayProposals[proposer];
uint256 i;
for (; i < numberEntries; i++) {
_tempSprayPlayerCheck[_players[i]] = false;
}
sprayProposals[proposer].proposedDate = uint32(block.timestamp);
for (i = 0; i < numberEntries; i++) {
if (_tempSprayPlayerCheck[_players[i]])
revert DuplicateSprayerError();
sprayProposals[proposer].squirts.push(
Squirt({player: _players[i], schnibbles: _schnibbles[i]})
);
_tempSprayPlayerCheck[_players[i]] = true;
}
}
/// @inheritdoc IAccountManager
function execSprayProposal(
address _proposer
)
external
onlyOneOfRoles(
[
Role.SocialApproval_1,
Role.SocialApproval_2,
Role.SocialApproval_3,
Role.SocialApproval_4,
Role.SocialApproval_5
]
)
{
if (sprayProposals[_proposer].proposedDate == 0)
revert EmptyProposalError();
uint256 numberEntries = sprayProposals[_proposer].squirts.length;
for (uint256 i; i < numberEntries; i++) {
address player = sprayProposals[_proposer].squirts[i].player;
uint256 schnibbles = sprayProposals[_proposer]
.squirts[i]
.schnibbles;
if (players[player].registrationDate != 0) {
players[player].unfedSchnibbles += schnibbles;
} else {
unclaimedSchnibbles[player] += schnibbles;
}
emit SchnibblesSprayed(player, schnibbles);
}
delete sprayProposals[_proposer];
emit SprayProposalExecuted(_proposer);
}
/// @inheritdoc IAccountManager
function removeSprayProposal(
address _proposer
)
external
onlyOneOfRoles(
[
Role.SocialApproval_1,
Role.SocialApproval_2,
Role.SocialApproval_3,
Role.SocialApproval_4,
Role.SocialApproval_5
]
)
{
delete sprayProposals[_proposer];
emit SprayProposalRemoved(_proposer);
}
function rewardSpray(
address _player,
uint256 _schnibbles
)
external
// NFTOracle = Reward EOA
onlyRole(Role.NFTOracle)
{
if (_schnibbles == 0) revert EmptyProposalError();
if (_schnibbles > maxRewardSpray) revert TooHighSprayAmountError();
if (players[_player].registrationDate != 0) {
players[_player].unfedSchnibbles += _schnibbles;
} else {
unclaimedSchnibbles[_player] += _schnibbles;
}
emit SchnibblesSprayed(_player, _schnibbles);
}
function removeSpray(
address _player,
uint256 _schnibbles
)
external
// NFTOracle = Reward EOA
onlyRole(Role.NFTOracle)
{
if (players[_player].registrationDate != 0) {
players[_player].unfedSchnibbles -= _schnibbles;
} else {
unclaimedSchnibbles[_player] -= _schnibbles;
}
emit SchnibblesSprayedRemoved(_player, _schnibbles);
}
/// @inheritdoc IAccountManager
function addSubAccount(
address _subAccount
)
external
notPaused
onlyRegistered(msg.sender)
onlyUnregistered(_subAccount)
{
if (subAccounts[msg.sender].length >= 5)
revert TooManySubAccountsError();
if (mainAccounts[_subAccount] != address(0))
revert SubAccountAlreadyRegisteredError();
mainAccounts[_subAccount] = msg.sender;
for (uint256 i; i < subAccounts[msg.sender].length; i++) {
if (subAccounts[msg.sender][i] == _subAccount)
revert SubAccountAlreadyRegisteredError();
}
subAccounts[msg.sender].push(_subAccount);
emit SubAccountAdded(msg.sender, _subAccount);
}
/// @inheritdoc IAccountManager
function removeSubAccount(
address _subAccount
) external notPaused onlyRegistered(msg.sender) {
_removeSubAccount(msg.sender, _subAccount);
}
function _removeSubAccount(
address _mainAccount,
address _subAccount
) internal {
delete mainAccounts[_subAccount];
uint256 subAccountLength = subAccounts[_mainAccount].length;
bool found = false;
for (uint256 i = 0; i < subAccountLength; i++) {
if (subAccounts[_mainAccount][i] == _subAccount) {
subAccounts[_mainAccount][i] = subAccounts[_mainAccount][
subAccountLength - 1
];
found = true;
subAccounts[_mainAccount].pop();
break;
}
}
if (!found) revert SubAccountNotRegisteredError();
emit SubAccountRemoved(_mainAccount, _subAccount);
}
/// @inheritdoc IAccountManager
function updatePlayer(
address _account,
MunchablesCommonLib.Player calldata _player
)
external
onlyConfiguredContract3(
StorageKey.SnuggeryManager,
StorageKey.PrimordialManager,
// Signifies LandManager
StorageKey.PrimordialsEnabled
)
{
players[_account] = _player;
}
/// @inheritdoc IAccountManager
function getMainAccount(
address _maybeSubAccount
) external view returns (address _mainAccount) {
_mainAccount = _getMainAccount(_maybeSubAccount);
}
/// @inheritdoc IAccountManager
//noinspection NoReturn
function getSubAccounts(
address _player,
uint256 _start
) external view returns (address[20] memory _subAccounts, bool _more) {
uint256 subAccountsLength = subAccounts[_player].length;
_more = false;
uint256 MAX_SUB = 20;
for (uint256 i = _start; i < _start + MAX_SUB; i++) {
if (i >= subAccountsLength) break;
_subAccounts[i] = subAccounts[_player][i];
}
if (subAccountsLength > _start + MAX_SUB) {
_more = true;
}
}
/// @inheritdoc IAccountManager
function getPlayer(
address _account
)
external
view
returns (
address _mainAccount,
MunchablesCommonLib.Player memory _player
)
{
address _caller = _getMainAccount(_account);
_player = players[_caller];
_mainAccount = _caller;
}
/// @inheritdoc IAccountManager
function getFullPlayerData(
address _account
)
external
view
returns (
address _mainAccount,
MunchablesCommonLib.Player memory _player,
MunchablesCommonLib.SnuggeryNFT[] memory _snuggery
)
{
_mainAccount = _getMainAccount(_account);
_player = players[_mainAccount];
_snuggery = snuggeryManager.getSnuggery(_mainAccount);
}
/// @inheritdoc IAccountManager
function getDailySchnibbles(
address _caller
) public view returns (uint256 _dailySchnibbles, uint256 _bonus) {
uint256 weightedValue = lockManager.getLockedWeightedValue(_caller);
// Arbitrary division here... If we remove it, we just need to make sure we modify level thresholds, & social/pet bonuses
_dailySchnibbles = (weightedValue / 10);
_bonus = bonusManager.getHarvestBonus(_caller);
}
function _harvest(address _caller) private returns (uint256 _harvested) {
(uint256 dailySchnibbles, uint256 bonus) = getDailySchnibbles(_caller);
dailySchnibbles += ((dailySchnibbles * bonus) / 1e18);
uint256 secondsToClaim = block.timestamp -
players[_caller].lastHarvestDate;
uint256 harvestedSchnibbles = (dailySchnibbles * secondsToClaim) /
1 days;
players[_caller].unfedSchnibbles += harvestedSchnibbles;
players[_caller].lastHarvestDate = uint32(block.timestamp);
_harvested = harvestedSchnibbles;
emit Harvested(_caller, harvestedSchnibbles);
}
function _getMainAccount(
address _maybeSubAccount
) internal view returns (address _mainAccount) {
_mainAccount = mainAccounts[_maybeSubAccount];
if (_mainAccount == address(0)) {
_mainAccount = _maybeSubAccount;
}
}
function _getMainAccountRequireRegistered(
address _maybeSubAccount
) internal view returns (address _mainAccount) {
_mainAccount = _getMainAccount(_maybeSubAccount);
if (players[_mainAccount].registrationDate == 0)
revert MainAccountNotRegisteredError(_mainAccount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "../../interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/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";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
// Token name
string private _name;
// Token symbol
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;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
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()) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
* core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
*/
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
/**
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
* particular (ignoring whether it is owned by `owner`).
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
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);
}
/**
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
* Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
* the `spender` for the specific `tokenId`.
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
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);
}
}
}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
*
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
* remain consistent with one another.
*/
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
/**
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that
* `auth` is either the owner of the token, or approved to operate on the token (by the owner).
*
* Emits a {Transfer} event.
*
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_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;
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
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));
}
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
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);
}
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
* are aware of the ERC721 standard to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is like {safeTransferFrom} in the sense that it invokes
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `tokenId` token must exist and be owned by `from`.
* - `to` cannot be the zero address.
* - `from` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
* either the owner of the token, or approved to operate on all tokens held by this owner.
*
* Emits an {Approval} event.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
/**
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
* emitted in the context of transfers.
*/
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Requirements:
* - operator can't be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
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);
}
/**
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
* Returns the owner.
*
* Overrides to ownership logic should be done to {_ownerOf}.
*/
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
*/
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 {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "../libraries/MunchablesCommonLib.sol";
/// @title Interface for the Account Manager
/// @notice This interface manages player accounts including their snuggery, schibbles, chonks, and sub-accounts
interface IAccountManager {
/// @notice Struct representing a "Squirt", which is a distribution of schnibbles to a player
struct Squirt {
address player; // The address of the player receiving schnibbles
uint256 schnibbles; // The amount of schnibbles being distributed to that player
}
/// @notice Struct representing a proposal to spray schnibbles across multiple accounts
struct SprayProposal {
uint32 proposedDate; // The date the proposal was made
Squirt[] squirts; // Array of "Squirt" structs detailing the distribution
}
/// @notice Register a new account, create a new Player record, and set snuggery and referrer
/// @dev This should be the first function called when onboarding a new user
/// @param _snuggeryRealm The realm of the new snuggery, which cannot be changed later
/// @param _referrer The account referring this user, use the null address if there is no referrer
/// @custom:frontend Register a new account
function register(
MunchablesCommonLib.Realm _snuggeryRealm,
address _referrer
) external;
/// @notice Calculate schnibbles to distribute and credit to unfedSchnibbles, set lastHarvestDate
/// @custom:frontend Harvest schnibbles
function harvest() external returns (uint256 _harvested);
/// @notice Used when a user adds to their lock to force claim at the previous locked value
/// @param _player Address of the player whose harvest to force
function forceHarvest(address _player) external;
/// @notice Propose a spray of schnibbles to multiple accounts
/// @param _players Array of player addresses
/// @param _schnibbles Array of schnibbles amounts corresponding to each player
function spraySchnibblesPropose(
address[] calldata _players,
uint256[] calldata _schnibbles
) external;
/// @notice Approve a proposed spray of schnibbles
/// @param _proposer Address of the proposer of the spray
function execSprayProposal(address _proposer) external;
/// @notice Remove a proposed spray of schnibbles
/// @param _proposer Address of the proposer of the spray to remove
function removeSprayProposal(address _proposer) external;
/// @notice Add a sub-account for a player
/// @param _subAccount The sub-account to add
/// @custom:frontend Use to add a new sub-account
function addSubAccount(address _subAccount) external;
/// @notice Remove a previously added sub-account
/// @param _subAccount The sub-account to remove
/// @custom:frontend Use to remove an existing sub-account
function removeSubAccount(address _subAccount) external;
/// @notice Restricted to the Munchable Manager only
function updatePlayer(
address _account,
MunchablesCommonLib.Player memory _player
) external;
/// @notice Look up the main account associated with a potentially sub-account
/// @param _maybeSubAccount Account to check
/// @return _mainAccount Main account associated, or the input if not a sub-account
function getMainAccount(
address _maybeSubAccount
) external view returns (address _mainAccount);
/// @notice Get a list of sub-accounts associated with a main account
/// @param _player Main account to check
/// @param _start Index to start pagination
/// @return _subAccounts List of sub-accounts
/// @return _more Whether there are more sub-accounts beyond the returned list
/// @custom:frontend Use this to populate a UI for managing sub accounts
function getSubAccounts(
address _player,
uint256 _start
) external view returns (address[20] memory _subAccounts, bool _more);
/// @notice Retrieve player data for a given account
/// @param _account Account to retrieve data for
/// @return _mainAccount Main account associated, or the input if not a sub-account
/// @return _player Player data structure
/// @custom:frontend Call this straight after log in to get the data about this player. The account
/// logging in may be a sub account and in this case the _mainAccount parameter
/// will be different from the logged in user. In this case the UI should show only
/// functions available to a sub-account
function getPlayer(
address _account
)
external
view
returns (
address _mainAccount,
MunchablesCommonLib.Player memory _player
);
/// @notice Retrieve detailed player and snuggery data
/// @param _account Address of the player
/// @return _mainAccount Main account associated
/// @return _player Player data
/// @return _snuggery List of snuggery NFTs
/// @custom:frontend Use this to fetch player and snuggery data
function getFullPlayerData(
address _account
)
external
view
returns (
address _mainAccount,
MunchablesCommonLib.Player memory _player,
MunchablesCommonLib.SnuggeryNFT[] memory _snuggery
);
/// @notice Get daily schnibbles that an account is accrueing
/// @param _player The address of the player
function getDailySchnibbles(
address _player
) external view returns (uint256 _dailySchnibbles, uint256 _bonus);
/// @notice Emitted when a player registers for a new account
/// @param _player The address of the player who registered
/// @param _snuggeryRealm The realm associated with the new snuggery chosen by the player
/// @param _referrer The address of the referrer, if any; otherwise, the zero address
/// @custom:frontend You should only receive this event once and only if you are onboarding a new user
/// safe to ignore if you are in the onboarding process
event PlayerRegistered(
address indexed _player,
MunchablesCommonLib.Realm _snuggeryRealm,
address _referrer
);
/// @notice Emitted when a player's schnibbles are harvested
/// @param _player The address of the player who harvested schnibbles
/// @param _harvestedSchnibbles The total amount of schnibbles that were harvested
/// @custom:frontend Listen for events where _player is your mainAccount and update unfedSchnibbles total
event Harvested(address indexed _player, uint256 _harvestedSchnibbles);
/// @notice Emitted when a sub-account is added to a player's account
/// @param _player The address of the main account to which a sub-account was added
/// @param _subAccount The address of the sub-account that was added
/// @custom:frontend If you are managing sub accounts (ie the logged in user is not a subAccount), then use this
/// event to reload your cache of sub accounts
event SubAccountAdded(address indexed _player, address _subAccount);
/// @notice Emitted when a sub-account is removed from a player's account
/// @param _player The address of the main account from which a sub-account was removed
/// @param _subAccount The address of the sub-account that was removed
/// @custom:frontend If you are managing sub accounts (ie the logged in user is not a subAccount), then use this
/// event to reload your cache of sub accounts
event SubAccountRemoved(address indexed _player, address _subAccount);
/// @notice Emitted when a proposal to spray schnibbles is made
/// @param _proposer The address of the player who proposed the spray
/// @param _squirts An array of "Squirt" details defining the proposed schnibble distribution
/// @custom:admin
event ProposedScnibblesSpray(address indexed _proposer, Squirt[] _squirts);
/// @notice Emitted when a schnibble spray is executed for each player
/// @param _player The player receiving schnibbles
/// @param _schnibbles The amount of schnibbles received
event SchnibblesSprayed(address indexed _player, uint256 _schnibbles);
/// @notice Emitted when schnibbles are removed. This is used to reverse a schnibble spray in the case of some being improperly sent.
/// @param _player The schnibbles remove
/// @param _schnibbles The amount of schnibbles removed
event SchnibblesSprayedRemoved(
address indexed _player,
uint256 _schnibbles
);
/// @notice Emitted when a spray proposal is executed
/// @param _proposer The account which proposed the spray
event SprayProposalExecuted(address indexed _proposer);
/// @notice Emitted when a spray proposal is removed
/// @param _proposer Account that proposed the proposal
event SprayProposalRemoved(address indexed _proposer);
// Errors
/// @notice Error thrown when a player is already registered and attempts to register again
error PlayerAlreadyRegisteredError();
/// @notice Error thrown when an action is attempted that requires the player to be registered, but they are not
error PlayerNotRegisteredError();
/// @notice Error thrown when the main account of a player is not registered
error MainAccountNotRegisteredError(address _mainAccount);
/// @notice Error thrown when there are no pending reveals for a player
error NoPendingRevealError();
/// @notice Error thrown when a sub-account is already registered and an attempt is made to register it again
error SubAccountAlreadyRegisteredError();
/// @notice Error thrown when a sub-account attempts to register as a main account
error SubAccountCannotRegisterError();
/// @notice Error thrown when a spray proposal already exists and another one is attempted
error ExistingProposalError();
/// @notice Error thrown when the parameters provided to a function do not match in quantity or type
error UnMatchedParametersError();
/// @notice Error thrown when too many entries are attempted to be processed at once
error TooManyEntriesError();
/// @notice Error thrown when an expected parameter is empty
error EmptyParameterError();
/// @notice Error thrown when a realm is invalid
error InvalidRealmError();
/// @notice Error thrown when a sub-account is not registered and is tried to be removed
error SubAccountNotRegisteredError();
/// @notice Error thrown when a proposal is attempted to be executed, but none exists
error EmptyProposalError();
/// @notice Error thrown when a player attempts to refer themselves
error SelfReferralError();
/// @notice Error thrown when the same sprayer gets added twice in a proposal
error DuplicateSprayerError();
/// @notice When a user tries to create too many sub accounts (currently 5 max)
error TooManySubAccountsError();
error TooHighSprayAmountError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "../libraries/MunchablesCommonLib.sol";
interface IOldNFT {
// This function needs to be added when upgrading the old NFT contract
function burn(uint256 _tokenId) external;
}
/// @title Interface for Migration Manager
/// @notice Handles the migration of NFTs with specific attributes and immutable attributes
interface IMigrationManager {
enum UserLockedChoice {
NONE,
LOCKED_FULL_MIGRATION,
LOCKED_BURN
}
/// @dev Struct to hold data during migration
/// @param tokenId The ID of the token being migrated
/// @param lockAmount Amount of tokens to lock during migration
/// @param lockDuration Duration for which tokens will be locked
/// @param tokenType Type of the token
/// @param attributes Attributes of the NFT
/// @param immutableAttributes Immutable attributes of the NFT
/// @param claimed Status of the NFT claim
struct MigrationSnapshotData {
uint256 tokenId;
uint256 lockAmount;
address token;
MunchablesCommonLib.NFTAttributes attributes;
MunchablesCommonLib.NFTImmutableAttributes immutableAttributes;
MunchablesCommonLib.NFTGameAttribute[] gameAttributes;
bool claimed;
}
struct MigrationTotals {
uint256 totalPurchasedAmount;
uint256 totalLockedAmount;
address tokenLocked;
}
/// @notice Load the migration snapshot for a batch of users
/// @dev This function sets up migration data for users
/// @param users Array of user addresses
/// @param data Array of migration data corresponding to each user
function loadMigrationSnapshot(
address[] calldata users,
MigrationSnapshotData[] calldata data
) external;
/// @notice Load the unrevealed snapshot for a batch of users
/// @dev This function sets up unrevealed data for users
/// @param users Array of user addresses
/// @param unrevealed Array of number of unrevealed for each user
function loadUnrevealedSnapshot(
address[] calldata users,
uint16[] calldata unrevealed
) external;
/// @notice Seals migration data loading
function sealData() external; // onlyRole(DEFAULT_ADMIN_ROLE)
/// @notice Burns NFTs
/// @dev This function handles multiple NFT burn process
function burnNFTs(address _user, uint32 _skip) external;
/// @notice Burns remaining purchased NFTs
/// @dev This function handles burning all remaining purchased NFTs
function burnRemainingPurchasedNFTs(address _user, uint32 _skip) external;
/// @notice Lock funds for migration
/// @dev This function handles locking funds and changing state to migrate
function lockFundsForAllMigration() external payable;
/// @notice Migrates all NFTs to the new version
/// @dev This function handles multiple NFT migration processes. They need to lock funds first before migrating.
function migrateAllNFTs(address _user, uint32 _skip) external;
/// @notice Migrates purchased NFTs to the new version
/// @dev This function handles multiple NFT migration processes
function migratePurchasedNFTs(uint256[] memory tokenIds) external payable;
/// @notice Burn unrevealed NFTs for points
function burnUnrevealedForPoints() external;
/// @notice Return funds which are stuck in the contract (admin only)
/// @param _tokenContract The token contract address(0) for ETH
/// @param _quantity Amount to return
/// @param _returnAddress Address to return to
function rescue(
address _tokenContract,
uint256 _quantity,
address _returnAddress
) external;
/// @notice Gets the migration data for a user and token ID
/// @param _user The user address
/// @param _tokenId The token ID
function getUserMigrationData(
address _user,
uint256 _tokenId
) external view returns (MigrationSnapshotData memory);
/// @notice Gets the overall migration data for a user
/// @param _user The user address
function getUserMigrationCompletedData(
address _user
) external view returns (bool, MigrationTotals memory);
/// @notice Gets the total number of NFTs owned by a user
/// @param _user The user address
function getUserNFTsLength(address _user) external view returns (uint256);
/// @notice Emitted when the migration snapshot is loaded
/// @param users The array of user addresses involved in the migration
/// @param data The migration data corresponding to each user
event MigrationSnapshotLoaded(
address[] users,
MigrationSnapshotData[] data
);
/// @notice Emitted when unreveal data is loaded
/// @param users The accounts
/// @param unrevealed Number of unrevealed NFTs
event UnrevealedSnapshotLoaded(address[] users, uint16[] unrevealed);
/// @notice Emitted when an NFT migration is successful
/// @param user The user who owns the NFTs
/// @param _oldTokenIds The token IDs of the old NFTs
/// @param _newTokenIds The token IDs of the new NFTs
event MigrationSucceeded(
address user,
uint256[] _oldTokenIds,
uint256[] _newTokenIds
);
/// @notice Emitted when an NFT burn is successful
/// @param user The user who owns the NFTs
/// @param _oldTokenIds The token IDs of the old NFTs
event BurnSucceeded(address user, uint256[] _oldTokenIds);
/// @notice Emitted when an NFT burn is successful
/// @param user The user who owns the NFTs
/// @param _oldTokenIds The token IDs of the old NFTs
event BurnPurchasedSucceeded(address user, uint256[] _oldTokenIds);
/// @notice Emitted after a player swaps unrevealed NFTs for points
/// @param user The account that swapped the unrevealed NFTS
/// @param amountSwapped The amount of unrevealed NFTs which will be swapped for points
event UnrevealedSwapSucceeded(address user, uint256 amountSwapped);
/// @notice Emitted when the migration data is sealed
event MigrationDataSealed();
/// @notice Emitted when a user locks their funds for a full migration
event LockedForMigration(address user, uint256 amount, address token);
error NotBoughtNFTError();
error NFTPurchasedContractError();
error UnrevealedNFTError();
error NoMigrationExistsError();
error InvalidMigrationOwnerError(address _owner, address _sender);
error InvalidMigrationAmountError();
error InvalidMigrationTokenError();
error AllowanceTooLowError();
error MigrationDataSealedError();
error MigrationDataNotSealedError();
error NoUnrevealedError();
error NoNFTsToBurnError();
error InvalidDataLengthError();
error DataAlreadyLoadedError();
error DifferentLockActionError();
error SelfNeedsToChooseError();
error InvalidSkipAmountError();
error InvalidMigrationTokenIdError();
error RescueTransferError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
/// @title ILockManager interface
/// @notice Provides an interface for managing token locks, including price updates, lock configurations, and user interactions.
interface ILockManager {
/// @notice Struct representing a lockdrop event
/// @param start Unix timestamp for when the lockdrop starts
/// @param end Unix timestamp for when the lockdrop ends
/// @param minLockDuration Minimum lock duration allowed when locking a token
struct Lockdrop {
uint32 start;
uint32 end;
uint32 minLockDuration;
}
/// @notice Struct holding details about tokens that can be locked
/// @param usdPrice USD price per token
/// @param nftCost Cost of the NFT associated with locking this token
/// @param decimals Number of decimals for the token
/// @param active Boolean indicating if the token is currently active for locking
struct ConfiguredToken {
uint256 usdPrice;
uint256 nftCost;
uint8 decimals;
bool active;
}
/// @notice Struct describing tokens locked by a player
/// @param quantity Amount of tokens locked
/// @param remainder Tokens left over after locking, not meeting the full NFT cost
/// @param lastLockTime The last time tokens were locked
/// @param unlockTime When the tokens will be unlocked
struct LockedToken {
uint256 quantity;
uint256 remainder;
uint32 lastLockTime;
uint32 unlockTime;
}
/// @notice Struct to hold locked tokens and their metadata
/// @param lockedToken LockedToken struct containing lock details
/// @param tokenContract Address of the token contract
struct LockedTokenWithMetadata {
LockedToken lockedToken;
address tokenContract;
}
/// @notice Struct to keep player-specific settings
/// @param lockDuration Duration in seconds for which tokens are locked
struct PlayerSettings {
uint32 lockDuration;
}
/// @notice Struct to manage USD price update proposals
/// @param proposedDate Timestamp when the price was proposed
/// @param proposer Address of the oracle proposing the new price
/// @param contracts Array of contracts whose prices are proposed to be updated
/// @param proposedPrice New proposed price in USD
struct USDUpdateProposal {
uint32 proposedDate;
address proposer;
address[] contracts;
uint256 proposedPrice;
mapping(address => uint32) approvals;
mapping(address => uint32) disapprovals;
uint8 approvalsCount;
uint8 disapprovalsCount;
}
/// @notice Configures the start and end times for a lockdrop event
/// @param _lockdropData Struct containing the start and end times
function configureLockdrop(Lockdrop calldata _lockdropData) external;
/// @notice Adds or updates a token configuration for locking purposes
/// @param _tokenContract The contract address of the token to configure
/// @param _tokenData The configuration data for the token
function configureToken(
address _tokenContract,
ConfiguredToken memory _tokenData
) external;
/// @notice Sets the thresholds for approving or disapproving USD price updates
/// @param _approve Number of approvals required to accept a price update
/// @param _disapprove Number of disapprovals required to reject a price update
function setUSDThresholds(uint8 _approve, uint8 _disapprove) external;
/// @notice Proposes a new USD price for one or more tokens
/// @param _price The new proposed price in USD
/// @param _contracts Array of token contract addresses to update
function proposeUSDPrice(
uint256 _price,
address[] calldata _contracts
) external;
/// @notice Approves a proposed USD price update
/// @param _price The price that needs to be approved
function approveUSDPrice(uint256 _price) external;
/// @notice Disapproves a proposed USD price update
/// @param _price The price that needs to be disapproved
function disapproveUSDPrice(uint256 _price) external;
/// @notice Sets the lock duration for a player's tokens
/// @param _duration The lock duration in seconds
function setLockDuration(uint256 _duration) external;
/// @notice Locks tokens on behalf of a player
/// @param _tokenContract Contract address of the token to be locked
/// @param _quantity Amount of tokens to lock
/// @param _onBehalfOf Address of the player for whom tokens are being locked
function lockOnBehalf(
address _tokenContract,
uint256 _quantity,
address _onBehalfOf
) external payable;
/// @notice Locks tokens
/// @param _tokenContract Contract address of the token to be locked
/// @param _quantity Amount of tokens to lock
function lock(address _tokenContract, uint256 _quantity) external payable;
/// @notice Unlocks the player's tokens
/// @param _tokenContract Contract address of the token to be unlocked
/// @param _quantity Amount of tokens to unlock
function unlock(address _tokenContract, uint256 _quantity) external;
/// @notice Retrieves locked tokens for a player
/// @param _player Address of the player
/// @return _lockedTokens Array of LockedTokenWithMetadata structs for all tokens configured
function getLocked(
address _player
) external view returns (LockedTokenWithMetadata[] memory _lockedTokens);
/// @notice Calculates the USD value of all tokens locked by a player, weighted by their yield
/// @param _player Address of the player
/// @return _lockedWeightedValue Total weighted USD value of locked tokens
function getLockedWeightedValue(
address _player
) external view returns (uint256 _lockedWeightedValue);
/// @notice Retrieves configuration for a token given its contract address
/// @param _tokenContract The contract address of the token
/// @return _token Struct containing the token's configuration
function getConfiguredToken(
address _tokenContract
) external view returns (ConfiguredToken memory _token);
/// @notice Retrieves lock settings for a player
/// @param _player Address of the player
/// @return _settings PlayerSettings struct containing the player's lock settings
function getPlayerSettings(
address _player
) external view returns (PlayerSettings calldata _settings);
/// @notice Emitted when a new token is configured
/// @param _tokenContract The token contract being configured
/// @param _tokenData ConfiguredToken struct with new config
event TokenConfigured(address _tokenContract, ConfiguredToken _tokenData);
/// @notice Emitted when a new lockdrop has been configured
/// @param _lockdrop_data Lockdrop struct containing the new lockdrop configuration
event LockDropConfigured(Lockdrop _lockdrop_data);
/// @notice Emitted when a new USD price has been proposed by one of the oracles
/// @param _proposer The oracle proposing the new price
/// @param _price New proposed price, specified in whole dollars
event ProposedUSDPrice(address _proposer, uint256 _price);
/// @notice Emitted when a USD price proposal has been approved by the required number of oracles
/// @param _approver The oracle who approved the new price
event ApprovedUSDPrice(address _approver);
/// @notice Emitted when an oracle disapproves of the proposed USD price
/// @param _disapprover The oracle disapproving of the new price
event DisapprovedUSDPrice(address _disapprover);
/// @notice Emitted when a USD price proposal is removed after receiving sufficient disapprovals
event RemovedUSDProposal();
/// @notice Emitted when the thresholds for USD oracle approvals and disapprovals are updated
/// @param _approve New threshold for approvals
/// @param _disapprove New threshold for disapprovals
event USDThresholdUpdated(uint8 _approve, uint8 _disapprove);
/// @notice Emitted when a player updates their lock duration
/// @param _player The player whose lock duration is updated
/// @param _duration New lock duration, specified in seconds
event LockDuration(address indexed _player, uint256 _duration);
/// @notice Emitted when a player locks tokens
/// @param _player The player locking the tokens
/// @param _sender The sender of the lock transaction
/// @param _tokenContract The contract address of the locked token
/// @param _quantity The amount of tokens locked
/// @param _remainder The remainder of tokens left after locking (not reaching an NFT cost)
/// @param _numberNFTs The number of NFTs the player is entitled to due to the lock
event Locked(
address indexed _player,
address _sender,
address _tokenContract,
uint256 _quantity,
uint256 _remainder,
uint256 _numberNFTs,
uint256 _lockDuration
);
/// @notice Emitted when a player unlocks tokens
/// @param _player The player unlocking the tokens
/// @param _tokenContract The contract address of the unlocked token
/// @param _quantity The amount of tokens unlocked
event Unlocked(
address indexed _player,
address _tokenContract,
uint256 _quantity
);
/// @notice Emitted when the discount factor for token locking is updated
/// @param discountFactor The new discount factor
event DiscountFactorUpdated(uint256 discountFactor);
/// @notice Emitted when the USD price is updated for a token
/// @param _tokenContract The token contract updated
/// @param _newPrice The new USD price
event USDPriceUpdated(address _tokenContract, uint256 _newPrice);
/// @notice Error thrown when an action is attempted by an entity other than the Account Manager
error OnlyAccountManagerError();
/// @notice Error thrown when an operation is attempted on a token that is not configured
error TokenNotConfiguredError();
/// @notice Error thrown when an action is attempted after the lockdrop period has ended
/// @param end The ending time of the lockdrop period
/// @param block_timestamp The current block timestamp, indicating the time of the error
error LockdropEndedError(uint32 end, uint32 block_timestamp);
/// @notice Error thrown when the lockdrop configuration is invalid
error LockdropInvalidError();
/// @notice Error thrown when the NFT cost specified is invalid or not allowed
error NFTCostInvalidError();
/// @notice Error thrown when the USD price proposed is deemed invalid
error USDPriceInvalidError();
/// @notice Error thrown when there is already a proposal in progress and another cannot be started
error ProposalInProgressError();
/// @notice Error thrown when the contracts specified in a proposal are invalid
error ProposalInvalidContractsError();
/// @notice Error thrown when there is no active proposal to operate on
error NoProposalError();
/// @notice Error thrown when the proposer is not allowed to approve their own proposal
error ProposerCannotApproveError();
/// @notice Error thrown when a proposal has already been approved
error ProposalAlreadyApprovedError();
/// @notice Error thrown when a proposal has already been disapproved
error ProposalAlreadyDisapprovedError();
/// @notice Error thrown when the price specified does not match the price in the active proposal
error ProposalPriceNotMatchedError();
/// @notice Error thrown when the lock duration specified exceeds the maximum allowed limit
error MaximumLockDurationError();
/// @notice Error thrown when the ETH value provided in a transaction is incorrect for the intended operation
error ETHValueIncorrectError();
/// @notice Error thrown when the message value provided in a call is invalid
error InvalidMessageValueError();
/// @notice Error thrown when the allowance provided for an operation is insufficient
error InsufficientAllowanceError();
/// @notice Error thrown when the amount locked is insufficient for the intended operation
error InsufficientLockAmountError();
/// @notice Error thrown when tokens are still locked and cannot be unlocked due to the lock period not expiring
error TokenStillLockedError();
/// @notice Error thrown when an invalid call is made to the Lock Manager
error LockManagerInvalidCallError();
/// @notice Error thrown when the Lock Manager refuses to accept ETH for a transaction
error LockManagerRefuseETHError();
/// @notice Error thrown when an invalid token contract address is provided for an operation
error InvalidTokenContractError();
/// @notice Lock duration out of range
error InvalidLockDurationError();
/// @notice User tries to reduce the unlock time
error LockDurationReducedError();
/// @notice If a sub account tries to lock tokens
error SubAccountCannotLockError();
/// @notice Account not registered with AccountManager
error AccountNotRegisteredError();
/// @notice If the player tries to lock too many tokens resulting in too many NFTs being minted
error TooManyNFTsError();
/// @notice Failed to transfer ETH
error FailedTransferError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
interface IMunchNFT {
/// @notice Get the next token ID
/// @return The next token ID
function nextTokenId() external view returns (uint256);
/// @notice Mint a new, empty token. Restrict access to only the NFTOverlord
/// @param _owner The owner of the newly minted NFT
function mint(address _owner) external returns (uint256 _tokenId);
/// @notice Update the token URL, restricted to off-chain role
/// @param _tokenId The token ID to update
/// @param _tokenURI The new URI, will be an IPFS hash
function setTokenURI(uint256 _tokenId, string memory _tokenURI) external;
/// @notice Blacklist an account from transferring tokens
/// @param _account The account to blacklist
function blAccount(address _account) external;
/// @notice Blacklist an token from being transferred
/// @param _tokenId The token ID to blacklist
function blToken(uint256 _tokenId) external;
/// @notice Remove blacklist for an account
/// @param _account The account to remove from the blacklist
function removeBlAccount(address _account) external;
/// @notice Remove blacklist on a token
/// @param _tokenId The token ID to remove from the blacklist
function removeBlToken(uint256 _tokenId) external;
/// @notice Error when a blacklisted account/token tries to transfer
error ForbiddenTransferError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "../libraries/MunchablesCommonLib.sol";
/// @title Interface for NFT Attributes Manager V1
/// @notice This interface manages the attributes and metadata of NFTs within the Munch ecosystem.
interface INFTAttributesManager {
/// @notice Called from MunchableManager to initialise a new record
function createWithImmutable(
uint256 _tokenId,
MunchablesCommonLib.NFTImmutableAttributes memory _immutableAttributes
) external;
/// @notice Sets dynamic attributes for a specific NFT, typically called after feeding or interaction events
/// @param _tokenId The ID of the NFT
/// @param _attributes Struct of new attributes
function setAttributes(
uint256 _tokenId,
MunchablesCommonLib.NFTAttributes calldata _attributes
) external;
/// @notice Sets game attributes for a specific NFT, typically called after level up
/// @param _tokenId The ID of the NFT
/// @param _attributes Array of new game attributes
function setGameAttributes(
uint256 _tokenId,
MunchablesCommonLib.NFTGameAttribute[] calldata _attributes
) external;
/// @notice Retrieves all data associated with an NFT in a single call
/// @param _tokenId The ID of the NFT
/// @return _nftData A struct containing all attributes (dynamic, immutable, and game-specific)
// function getFullNFTData(
// uint256 _tokenId
// ) external view returns (NFTFull memory _nftData);
/// @notice Retrieves dynamic attributes for a specific token
/// @param _tokenId The ID of the NFT
/// @return _attributes Struct of the NFT's dynamic attributes
function getAttributes(
uint256 _tokenId
)
external
view
returns (MunchablesCommonLib.NFTAttributes memory _attributes);
/// @notice Retrieves immutable attributes for a specific token
/// @param _tokenId The ID of the NFT
/// @return _immutableAttributes Struct of the NFT's immutable attributes
function getImmutableAttributes(
uint256 _tokenId
)
external
view
returns (
MunchablesCommonLib.NFTImmutableAttributes
memory _immutableAttributes
);
/// @notice Retrieves game-specific attributes for a specific token
/// @param _tokenId The ID of the NFT
/// @param _requestedIndexes Array of GameAttributeIndex to define subset of attributes to include in the result
/// @return _gameAttributes Struct of the NFT's game attributes
function getGameAttributes(
uint256 _tokenId,
MunchablesCommonLib.GameAttributeIndex[] calldata _requestedIndexes
)
external
view
returns (MunchablesCommonLib.NFTGameAttribute[] memory _gameAttributes);
function getGameAttributeDataType(
uint8 _index
) external pure returns (MunchablesCommonLib.GameAttributeType _dataType);
event CreatedWithImmutable(
uint256 _tokenId,
MunchablesCommonLib.NFTImmutableAttributes _immutableAttributes
);
/// @notice Event emitted when NFT attributes are updated
event AttributesUpdated(uint256 indexed _tokenId);
/// @notice Event emitted when NFT game attributes are updated
event GameAttributesUpdated(uint256 indexed _tokenId);
/// @notice Error when the owner of the NFT does not match the expected address
error IncorrectOwnerError();
/// @notice Error when the 'from' level specified is invalid
error InvalidLevelFromError();
/// @notice Error when the oracle recovering the signature is invalid
/// @param _recoveredSigner The address of the invalid signer
error InvalidOracleError(address _recoveredSigner);
/// @notice Error when a call is made by a non-authorized migration manager
error NotAuthorizedMigrationManagerError();
/// @notice When user tries to set attributes when the record hasnt been created
error NotCreatedError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "../libraries/MunchablesCommonLib.sol";
interface IClaimManager {
struct Period {
uint32 id;
uint32 startTime;
uint32 endTime;
uint256 available;
uint256 claimed;
uint256 globalTotalChonk;
}
/// @notice Starts a new periods
/// @dev This will start a new period if the previous one has ended
function newPeriod() external; // onlyRole(NEW_PERIOD_ROLE)
/// @notice Claims points for the current period
/// @dev This will claim the sender's points for the current period
function claimPoints() external; // onlyValidPeriod
/// @notice Used by the account manager to force a claim before doing anything which would affect the total chonks
/// for the player
function forceClaimPoints(address _player) external; // onlyValidPeriod only SnuggeryManager.sol
/// @notice Spends points for the _player
/// @dev This will spend the _player's points
/// @param _player The player to spend the points for
/// @param _spendPoints The number of points to spend
function spendPoints(address _player, uint256 _spendPoints) external; // only SnuggeryManager.sol
/// @notice Convert accumulated points to tokens
/// @dev This will convert the sender's accumulated points to tokens
/// @param _points The number of points to convert
function convertPointsToTokens(uint256 _points) external; // onlySwapEnabled
// @notice If the player chooses to not migrate over their NFTs, they can burn them for points
/// @dev This will burn the player's NFTs for points. Can only be called by Migration Manager
/// @param _player The player to burn the NFTs for
/// @param _tokenIdsByRarity List of token IDs separated by rarity
function burnNFTsForPoints(
address _player,
uint8[] memory _tokenIdsByRarity
) external returns (uint256 _receivedPoints); // OnlyMigrationManager
/// @notice Called by MigrationManager to give the player points from their unrevealed NFTs
/// @param _player The address of the player
/// @param _unrevealed Number of unrevealed NFTs which are being swapped
function burnUnrevealedForPoints(
address _player,
uint256 _unrevealed
) external returns (uint256 _receivedPoints); // OnlyMigrationManager
/// @notice Gets the current period data
/// @return _period The current period data
function getCurrentPeriod() external view returns (Period memory _period);
/// @notice Gets the current points for a player
/// @param _player The player to get the points for
function getPoints(address _player) external view returns (uint256 _points);
event Claimed(
address indexed _sender,
address indexed _player,
uint32 _periodId,
uint256 _pointsClaimed,
address indexed _referrer,
uint256 _referralBonus
);
event PointsPerPeriodSet(uint256 _oldPoints, uint256 _newPoints);
event AccountManagerSet(
address _oldAccountManager,
address _newAccountManager
);
event LockManagerSet(address _oldLockManager, address _newLockManager);
event NewPeriodStarted(
uint32 _periodId,
uint32 _startTime,
uint32 _endTime,
uint256 _availablePoints,
uint256 _prevPeriodPointsClaimed,
uint256 _excessPoints,
uint256 _totalGlobalChonk
);
event ClaimModuleSet(address _claimModule, bool _isValid);
event YieldClaimed(
address _claimModule,
address _tokenContract,
uint256 _yieldClaimed
);
event SwapEnabled(bool _enabled);
event PointsPerTokenSet(
uint256 _oldPointsPerToken,
uint256 _newPointsPerToken
);
event MunchTokenSet(address _oldMunchToken, address _newMunchToken);
event PointsConverted(
address indexed _player,
uint256 _points,
uint256 _tokens
);
event ReferralBonusSet(uint256 _oldReferralBonus, uint256 _referralBonus);
event PointsSpent(address indexed _player, uint256 _pointsSpent);
event NFTsBurnedForPoints(
address indexed _player,
uint8[] _tokenIdsByRarity,
uint256 _points
);
event UnrevealedSwappedForPoints(
address indexed _player,
uint256 _unrevealed,
uint256 _points
);
event ClaimPeriodHit(
address indexed _player,
uint32 _lastClaimPeriod,
uint32 _currentPeriod
);
error InvalidSnapshotDataError();
error SnapshotIsFinalizedError();
error SnapshotIsNotFinalizedError();
error NotAccountManagerError();
error InvalidPeriodError(uint32 _now, uint32 _startTime, uint32 _endTime);
error CurrentPeriodNotEndedError();
error AlreadyClaimedError();
error NoClaimablePointsError();
error NotEnoughPointsError();
error SwapDisabledError();
error PointsPerTokenNotSetError();
error NoSnapshotDataError();
error PointAmountToSmallError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
interface IRNGProxy {
/// @notice Request a random number to be provided back to the contract specified
/// @param _contract The contract that will receive the data
/// @param _selector The function on the contract to call
/// @param _index A unique identifier which the contract can use to identify the target for the data
function requestRandom(
address _contract,
bytes4 _selector,
uint256 _index
) external;
event RandomRequested(
address indexed _target,
bytes4 _selector,
uint256 indexed _index
);
event RandomRequestComplete(
uint256 indexed _index,
bool _success,
bytes _data
);
error NoRequestError();
error CallbackFailedError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
/// @title Interface for the Bonus Manager
/// @notice This interface defines the functions that the Bonus Manager contract should implement. Each function is a getter responsible for returning a percentage multiplier
interface IBonusManager {
function getFeedBonus(
address _caller,
uint256 _tokenId
) external view returns (int256 _amount);
function getHarvestBonus(
address _caller
) external view returns (uint256 _amount);
function getPetBonus(
address _petter
) external view returns (uint256 _amount);
function getReferralBonus() external view returns (uint256 _amount);
error InvalidRarityError(uint256 _rarity);
error InvalidRealmBonus(uint256 _realmIndex);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "../libraries/MunchablesCommonLib.sol";
/// @title Interface for the Account Manager
/// @notice This interface manages player accounts including their snuggery, schibbles, chonks, and sub-accounts
interface ISnuggeryManager {
/// @notice Imports a munchable to the player's snuggery
/// @dev Check that the NFT is approved to transfer by this contract
/// @param _tokenId The token ID to import
/// @custom:frontend Import a munchable
function importMunchable(uint256 _tokenId) external;
/// @notice Exports a munchable from the player's snuggery, the munchable will be returned directly
/// @param _tokenId The token ID to export
/// @custom:frontend Export a munchable
function exportMunchable(uint256 _tokenId) external;
/// @notice Feed a munchable to increase its chonks, chonks will be schnibbles multiplied by any feed bonus
/// @param _tokenId Token ID of the munchable to feed
/// @param _schnibbles Amount of schnibbles to feed
/// @custom:frontend Feed a munchable, use event data to show how much chonk was added
function feed(uint256 _tokenId, uint256 _schnibbles) external;
/// @notice Increase the number of slots in a player's snuggery
/// @param _quantity Quantity to increase the snuggery size by
function increaseSnuggerySize(uint8 _quantity) external;
/// @notice Pet another player's munchable to give both petter and petted some schnibbles
/// @param _pettedOwner The owner of the token being petted (the token must be in that player's snuggery)
/// @param _tokenId Token ID of the munchable to pet
/// @custom:frontend Pet another user's munchable. Check last pet and petted times to see if this function
/// should be available
function pet(address _pettedOwner, uint256 _tokenId) external;
/// @notice Retrieve the total schnibbles count for a player's snuggery
/// @param _player Address of the player
/// @return _totalChonk Total schnibbles count
function getTotalChonk(
address _player
) external view returns (uint256 _totalChonk);
/// @notice Retrieve the global total schnibbles count across all snuggeries
function getGlobalTotalChonk()
external
view
returns (uint256 _totalGlobalChonk);
/// @notice Gets a snuggery (array of SnuggeryNFT)
/// @param _player Address of the player to get snuggery for
/// @return _snuggery Array of SnuggeryNFT items
function getSnuggery(
address _player
)
external
view
returns (MunchablesCommonLib.SnuggeryNFT[] memory _snuggery);
/// @notice Emitted when a munchable is imported into a player's snuggery
/// @param _player The address of the player who imported the munchable
/// @param _tokenId The token ID of the munchable that was imported
/// @custom:frontend Listen for events for the mainAccount, when it is received update your snuggery data
event MunchableImported(address indexed _player, uint256 _tokenId);
/// @notice Emitted when a munchable is exported from a player's snuggery
/// @param _player The address of the player who exported the munchable
/// @param _tokenId The token ID of the munchable that was exported
/// @custom:frontend Listen for events for the mainAccount, when it is received update your snuggery data
event MunchableExported(address indexed _player, uint256 _tokenId);
/// @notice Emitted when a munchable is fed schnibbles
/// @param _player The address of the player who fed the munchable
/// @param _tokenId The token ID of the munchable that was fed
/// @param _baseChonks The base amount of chonks that were gained by feeding, will be equal to the schnibbles fed
/// @param _bonusChonks The additional bonus chonks that were awarded during the feeding
/// @custom:frontend Listen for events for your mainAccount and when this is received update the particular token
/// in the snuggery by reloading the NFT data
event MunchableFed(
address indexed _player,
uint256 _tokenId,
uint256 _baseChonks,
int256 _bonusChonks
);
/// @notice Emitted when a munchable is petted, distributing schnibbles to both the petter and the petted
/// @param _petter The address of the player who petted the munchable
/// @param _petted The address of the player who owns the petted munchable
/// @param _tokenId The token ID of the munchable that was petted
/// @param _petterSchnibbles The amount of schnibbles awarded to the petter
/// @param _pettedSchnibbles The amount of schnibbles awarded to the owner of the petted munchable
/// @custom:frontend Listen for events where your mainAccount petted and where it was pet
/// - If your mainAccount was petted, update the unfedMunchables total
/// - If your account was petted then, update the unfedMunchables total, also optionally load the
/// lastPetTime for the munchable if you use that
event MunchablePetted(
address indexed _petter,
address indexed _petted,
uint256 _tokenId,
uint256 _petterSchnibbles,
uint256 _pettedSchnibbles
);
/// @notice Event emitted when a snuggery size is increased
event SnuggerySizeIncreased(
address _player,
uint16 _previousSize,
uint16 _newSize
);
/// @notice Error thrown when a token ID is not found in the snuggery
error TokenNotFoundInSnuggeryError();
/// @notice Error thrown when a player's snuggery is already full and cannot accept more munchables
error SnuggeryFullError();
/// @notice Someone tries to import a munchable they do not own
error IncorrectOwnerError();
/// @notice Error if user tries to import someone else's NFT
error InvalidOwnerError();
/// @notice Error thrown when an action is attempted that requires the player to be registered, but they are not
error PlayerNotRegisteredError();
/// @notice Error thrown when a munchable is not found in a player's snuggery
error MunchableNotInSnuggeryError();
/// @notice Error thrown when a player attempts to pet their own munchable
error CannotPetOwnError();
/// @notice Error thrown when a munchable is petted too soon after the last petting
error PettedTooSoonError();
/// @notice Error thrown when a player attempts to pet too soon after their last petting action
error PetTooSoonError();
/// @notice Error thrown when a player tries to feed a munchable but does not have enough schnibbles
/// @param _currentUnfedSchnibbles The current amount of unfed schnibbles available to the player
error InsufficientSchnibblesError(uint256 _currentUnfedSchnibbles);
/// @notice Error thrown when a player attempts swap a primordial but they dont have one
error NoPrimordialInSnuggeryError();
/// @notice Invalid token id passed (normally if 0)
error InvalidTokenIDError();
/// @notice Contract is not approved to transfer NFT on behalf of user
error NotApprovedError();
/// @notice Something not configured
error NotConfiguredError();
/// @notice This is thrown by the claim manager but we need it here to decode selector
error NotEnoughPointsError();
/// @notice When petting the user petting must supply the main account being petted
error PettedIsSubAccount();
/// @notice Player tries to increase their snuggery size beyond global max size
error SnuggeryMaxSizeError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
/// @title ILandManager interface
/// @notice Provides an interface for handling plots of land and toiling
interface ILandManager {
struct ToilerState {
uint256 lastToilDate;
uint256 plotId;
address landlord;
uint256 latestTaxRate;
bool dirty;
}
struct PlotMetadata {
uint256 lastUpdated;
uint256 currentTaxRate;
}
struct Plot {
bool occupied;
uint256 tokenId;
}
function triggerPlotMetadata() external;
function updatePlotMetadata(address landlord) external;
function updateTaxRate(uint256 new_tax_rate) external;
// stake munchable by giving in landlord’s address + token id + plot you want to stake in
// will revert if num_taken >= total
function stakeMunchable(
address landlord,
uint256 tokenId,
uint256 plotId
) external;
// unstake munchable
function unstakeMunchable(uint256 tokenId) external;
// if you’re in a plot that got moved because the landlord unlocked partial amount, move your
// munchable to an unoccupied plot
function transferToUnoccupiedPlot(uint256 tokenId, uint256 plotId) external;
function farmPlots() external;
event FarmPlotTaken(ToilerState toilerState, uint256 tokenId);
event FarmPlotLeave(address landlord, uint256 tokenId, uint256 plot);
event FarmedSchnibbles(
address landlord,
address renter,
uint256 tokenId,
uint256 plot,
uint256 rentersSchnibbles,
uint256 landlordsSchnibbles
);
event UpdatePlotsMeta(address landlord);
event TaxRateChanged(
address landlord,
uint256 oldTaxRate,
uint256 newTaxRate
);
error OccupiedPlotError(address, uint256);
error PlotTooHighError();
error TooManyStakedMunchiesError();
error NotStakedError();
error InvalidOwnerError();
error PlayerNotRegisteredError();
error NotApprovedError();
error InvalidTokenIdError();
error InvalidTaxRateError();
error PlotMetadataNotUpdatedError();
error PlotMetadataTriggeredError();
error CantStakeToSelfError();
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IConfigStorage.sol";
import "../interfaces/IConfigNotifiable.sol";
import "../config/BaseConfigStorageUpgradeable.sol";
import "../interfaces/IBaseBlastManager.sol";
import "../interfaces/IHoldsGovernorship.sol";
import "../interfaces/IBlast.sol";
import "./BaseBlastManager.sol";
abstract contract BaseBlastManagerUpgradeable is
BaseBlastManager,
BaseConfigStorageUpgradeable
{
function initialize(
address _configStorage
) public virtual override initializer {
BaseConfigStorageUpgradeable.initialize(_configStorage);
}
function __BaseBlastManagerUpgradeable_reconfigure() internal {
__BaseBlastManager_reconfigure();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
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);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
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));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
library MunchablesCommonLib {
enum Rarity {
Primordial,
Common,
Rare,
Epic,
Legendary,
Mythic,
Invalid
}
enum Realm {
Everfrost,
Drench,
Moltania,
Arridia,
Verdentis,
Invalid
}
struct NFTImmutableAttributes {
Rarity rarity;
uint16 species;
Realm realm;
uint8 generation;
uint32 hatchedDate;
}
struct NFTAttributes {
uint256 chonks;
uint16 level;
uint16 evolution;
uint256 lastPettedTime;
}
struct NFTGameAttribute {
GameAttributeType dataType;
bytes value;
}
struct Munchadex {
mapping(Realm => uint256) numInRealm;
mapping(Rarity => uint256) numInRarity;
mapping(bytes32 => uint256) unique;
uint256 numUnique;
}
enum GameAttributeIndex {
Strength,
Agility,
Stamina,
Defence,
Voracity,
Cuteness,
Charisma,
Trustworthiness,
Leadership,
Empathy,
Intelligence,
Cunning,
Creativity,
Adaptability,
Wisdom,
IsOriginal,
IndexCount // Do not use and keep at the end to detect number of indexes
}
enum GameAttributeType {
NotSet,
Bool,
String,
SmallInt,
BigUInt,
Bytes
}
struct PrimordialData {
uint256 chonks;
uint32 createdDate;
int8 level;
bool hatched;
}
struct SnuggeryNFT {
uint256 tokenId;
uint32 importedDate;
}
struct NFTFull {
uint256 tokenId;
NFTImmutableAttributes immutableAttributes;
NFTAttributes attributes;
NFTGameAttribute[] gameAttributes;
}
struct Player {
uint32 registrationDate;
uint32 lastPetMunchable;
uint32 lastHarvestDate;
Realm snuggeryRealm;
uint16 maxSnuggerySize;
uint256 unfedSchnibbles;
address referrer;
}
// Pure Functions
/// @notice Error when insufficient random data is provided for operations
error NotEnoughRandomError();
function calculateRaritySpeciesPercentage(
bytes memory randomBytes
) internal pure returns (uint32, uint32) {
if (randomBytes.length < 5) revert NotEnoughRandomError();
uint32 rarityBytes;
uint8 speciesByte;
uint32 rarityPercentage;
uint32 speciesPercent;
rarityBytes =
(uint32(uint8(randomBytes[0])) << 24) |
(uint32(uint8(randomBytes[1])) << 16) |
(uint32(uint8(randomBytes[2])) << 8) |
uint32(uint8(randomBytes[3]));
speciesByte = uint8(randomBytes[4]);
uint256 rarityPercentageTmp = (uint256(rarityBytes) * 1e6) /
uint256(4294967295);
uint256 speciesPercentTmp = (uint256(speciesByte) * 1e6) / uint256(255);
rarityPercentage = uint32(rarityPercentageTmp);
speciesPercent = uint32(speciesPercentTmp);
return (rarityPercentage, speciesPercent);
}
function getLevelThresholds(
uint256[] memory levelThresholds,
uint256 _chonk
)
internal
pure
returns (uint16 _currentLevel, uint256 _currentLevelThreshold)
{
if (_chonk >= levelThresholds[99]) {
return (101, levelThresholds[99]);
}
if (_chonk < levelThresholds[0]) {
return (1, 0);
}
uint256 low = 0;
uint256 high = levelThresholds.length;
uint256 mid = 0;
uint16 answer = 0;
while (low < high) {
mid = (low + high) / 2;
if (levelThresholds[mid] <= _chonk) {
low = mid + 1;
} else {
answer = uint16(mid);
high = mid;
}
}
_currentLevel = answer + 1;
_currentLevelThreshold = levelThresholds[uint256(answer - 1)];
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
enum StorageKey {
Many,
Paused,
LockManager,
AccountManager,
ClaimManager,
MigrationManager,
NFTOverlord,
SnuggeryManager,
PrimordialManager,
MunchadexManager,
MunchNFT,
MunchToken,
RewardsManager,
YieldDistributor,
GasFeeDistributor,
BlastContract,
BlastPointsContract,
BlastPointsOperator,
USDBContract,
WETHContract,
RNGProxyContract,
NFTAttributesManager,
Treasury,
OldMunchNFT,
MaxLockDuration,
DefaultSnuggerySize,
MaxSnuggerySize,
MaxRevealQueue,
MaxSchnibbleSpray,
PetTotalSchnibbles,
NewSlotCost,
PrimordialsEnabled,
BonusManager,
ReferralBonus,
RealmBonuses,
RarityBonuses,
LevelThresholds,
PrimordialLevelThresholds,
TotalMunchables,
MunchablesPerRealm,
MunchablesPerRarity,
RaritySetBonuses,
PointsPerPeriod,
PointsPerToken,
SwapEnabled,
PointsPerMigratedNFT,
PointsPerUnrevealedNFT,
MinETHPetBonus,
MaxETHPetBonus,
PetBonusMultiplier,
RealmLookups,
// Species & Probabilities
CommonSpecies,
RareSpecies,
EpicSpecies,
LegendarySpecies,
MythicSpecies,
CommonPercentage,
RarePercentage,
EpicPercentage,
LegendaryPercentage,
MythicPercentage,
MigrationBonus,
MigrationBonusEndTime,
MigrationDiscountFactor
}
enum Role {
Admin,
Social_1,
Social_2,
Social_3,
Social_4,
Social_5,
SocialApproval_1,
SocialApproval_2,
SocialApproval_3,
SocialApproval_4,
SocialApproval_5,
PriceFeed_1,
PriceFeed_2,
PriceFeed_3,
PriceFeed_4,
PriceFeed_5,
Snapshot,
NewPeriod,
ClaimYield,
Minter,
NFTOracle
}
enum StorageType {
Uint,
SmallUintArray,
UintArray,
SmallInt,
SmallIntArray,
Bool,
Address,
AddressArray,
Bytes32
}
interface IConfigStorage {
// Manual notify
function manualNotify(uint8 _index, uint8 _length) external;
// Manual notify for a specific contract
function manualNotifyAddress(address _contract) external;
// Setters
function setRole(Role _role, address _contract, address _addr) external;
function setUniversalRole(Role _role, address _addr) external;
function setUint(StorageKey _key, uint256 _value, bool _notify) external;
function setUintArray(
StorageKey _key,
uint256[] memory _value,
bool _notify
) external;
function setSmallUintArray(
StorageKey _key,
uint8[] calldata _smallUintArray,
bool _notify
) external;
function setSmallInt(StorageKey _key, int16 _value, bool _notify) external;
function setSmallIntArray(
StorageKey _key,
int16[] memory _value,
bool _notify
) external;
function setBool(StorageKey _key, bool _value, bool _notify) external;
function setAddress(StorageKey _key, address _value, bool _notify) external;
function setAddresses(
StorageKey[] memory _keys,
address[] memory _values,
bool _notify
) external;
function setAddressArray(
StorageKey _key,
address[] memory _value,
bool _notify
) external;
function setBytes32(StorageKey _key, bytes32 _value, bool _notify) external;
// Getters
function getRole(Role _role) external view returns (address);
function getContractRole(
Role _role,
address _contract
) external view returns (address);
function getUniversalRole(Role _role) external view returns (address);
function getUint(StorageKey _key) external view returns (uint256);
function getUintArray(
StorageKey _key
) external view returns (uint256[] memory);
function getSmallUintArray(
StorageKey _key
) external view returns (uint8[] memory _smallUintArray);
function getSmallInt(StorageKey _key) external view returns (int16);
function getSmallIntArray(
StorageKey _key
) external view returns (int16[] memory);
function getBool(StorageKey _key) external view returns (bool);
function getAddress(StorageKey _key) external view returns (address);
function getAddressArray(
StorageKey _key
) external view returns (address[] memory);
function getBytes32(StorageKey _key) external view returns (bytes32);
// Notification Address Management
function addNotifiableAddress(address _addr) external;
function addNotifiableAddresses(address[] memory _addresses) external;
function removeNotifiableAddress(address _addr) external;
function getNotifiableAddresses()
external
view
returns (address[] memory _addresses);
error ArrayTooLongError();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import "./IConfigStorage.sol";
interface IConfigNotifiable {
function configUpdated() external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol";
import "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import "../interfaces/IConfigStorage.sol";
import {BaseConfigStorage} from "./BaseConfigStorage.sol";
abstract contract BaseConfigStorageUpgradeable is
Initializable,
BaseConfigStorage,
UUPSUpgradeable
{
function _authorizeUpgrade(address _input) internal override onlyAdmin {}
function initialize(address _configStorage) public virtual initializer {
__BaseConfigStorage_setConfigStorage(_configStorage);
}
function __BaseConfigStorageUpgradable_reconfigure() internal {
__BaseConfigStorage_reconfigure();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
interface IBaseBlastManager {
function getConfiguredGovernor() external view returns (address _governor);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
/// @notice Contracts which implement this interface will be the governor for other contracts and
/// give it up on request from the contract
interface IHoldsGovernorship {
function reassignBlastGovernor(address _newAddress) external;
function isGovernorOfContract(
address _contract
) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
enum GasMode {
VOID,
CLAIMABLE
}
interface IBlastPoints {
function configurePointsOperator(address operator) external;
}
interface IERC20Rebasing {
function configure(YieldMode) external returns (uint256);
function claim(
address recipient,
uint256 amount
) external returns (uint256);
function getClaimableAmount(
address account
) external view returns (uint256);
}
interface IBlast {
// configure
function configureContract(
address contractAddress,
YieldMode _yield,
GasMode gasMode,
address governor
) external;
function configure(
YieldMode _yield,
GasMode gasMode,
address governor
) external;
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(
address _newGovernor,
address contractAddress
) external;
// claim yield
function claimYield(
address contractAddress,
address recipientOfYield,
uint256 amount
) external returns (uint256);
function claimAllYield(
address contractAddress,
address recipientOfYield
) external returns (uint256);
// claim gas
function claimAllGas(
address contractAddress,
address recipientOfGas
) external returns (uint256);
function claimGasAtMinClaimRate(
address contractAddress,
address recipientOfGas,
uint256 minClaimRateBips
) external returns (uint256);
function claimMaxGas(
address contractAddress,
address recipientOfGas
) external returns (uint256);
function claimGas(
address contractAddress,
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external returns (uint256);
// read functions
function readClaimableYield(
address contractAddress
) external view returns (uint256);
function readYieldConfiguration(
address contractAddress
) external view returns (uint8);
function readGasParams(
address contractAddress
)
external
view
returns (
uint256 etherSeconds,
uint256 etherBalance,
uint256 lastUpdated,
GasMode
);
/**
* @notice Checks if the caller is authorized
* @param contractAddress The address of the contract
* @return A boolean indicating if the caller is authorized
*/
function isAuthorized(address contractAddress) external view returns (bool);
function isGovernor(address contractAddress) external view returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IConfigStorage.sol";
import "../interfaces/IConfigNotifiable.sol";
import "../config/BaseConfigStorage.sol";
import "../interfaces/IBaseBlastManager.sol";
import "../interfaces/IHoldsGovernorship.sol";
import "../interfaces/IERC20YieldClaimable.sol";
import "../interfaces/IBlast.sol";
abstract contract BaseBlastManager is
IBaseBlastManager,
IERC20YieldClaimable,
BaseConfigStorage
{
IBlast public blastContract;
IBlastPoints public blastPointsContract;
address private _governorConfigured;
address private _pointsOperatorConfigured;
bool private _blastClaimableConfigured;
IERC20 public USDB;
IERC20 public WETH;
error InvalidGovernorError();
function __BaseBlastManager_reconfigure() internal {
// load config from the config storage contract and configure myself
address blastAddress = configStorage.getAddress(
StorageKey.BlastContract
);
if (blastAddress != address(blastContract)) {
blastContract = IBlast(blastAddress);
if (blastContract.isAuthorized(address(this))) {
blastContract.configureClaimableGas();
// fails on cloned networks
(bool success, ) = blastAddress.call(
abi.encodeWithSelector(
bytes4(keccak256("configureClaimableYield()"))
)
);
if (success) {
// not on a cloned network and no compiler error!
}
}
}
address pointsContractAddress = configStorage.getAddress(
StorageKey.BlastPointsContract
);
if (pointsContractAddress != address(blastPointsContract)) {
blastPointsContract = IBlastPoints(pointsContractAddress);
address pointsOperator = configStorage.getAddress(
StorageKey.BlastPointsOperator
);
if (_pointsOperatorConfigured == address(0)) {
// Reassignment must be called from the point operator itself
blastPointsContract.configurePointsOperator(pointsOperator);
_pointsOperatorConfigured = pointsOperator;
}
}
address usdbAddress = configStorage.getAddress(StorageKey.USDBContract);
address wethAddress = configStorage.getAddress(StorageKey.WETHContract);
if (usdbAddress != address(USDB)) {
USDB = IERC20(usdbAddress);
IERC20Rebasing _USDB = IERC20Rebasing(usdbAddress);
_USDB.configure(YieldMode.CLAIMABLE);
}
if (wethAddress != address(WETH)) {
WETH = IERC20(wethAddress);
IERC20Rebasing _WETH = IERC20Rebasing(wethAddress);
_WETH.configure(YieldMode.CLAIMABLE);
}
address rewardsManagerAddress = configStorage.getAddress(
StorageKey.RewardsManager
);
if (rewardsManagerAddress != address(0)) {
setBlastGovernor(rewardsManagerAddress);
}
super.__BaseConfigStorage_reconfigure();
}
function setBlastGovernor(address _governor) internal {
if (_governor == address(0)) revert InvalidGovernorError();
if (address(blastContract) == address(0)) return;
if (_governorConfigured == address(0)) {
// if this contract is the governor then it should claim its own yield/gas
if (_governor != address(this)) {
// Once this is called the governor will be the only account allowed to configure
blastContract.configureGovernor(_governor);
}
} else {
IHoldsGovernorship(_governorConfigured).reassignBlastGovernor(
_governor
);
}
_governorConfigured = _governor;
}
function claimERC20Yield(
address _tokenContract,
uint256 _amount
) external onlyConfiguredContract(StorageKey.RewardsManager) {
IERC20Rebasing(_tokenContract).claim(
configStorage.getAddress(StorageKey.RewardsManager),
_amount
);
}
function getConfiguredGovernor() external view returns (address _governor) {
_governor = _governorConfigured;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
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();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
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;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
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);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
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;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
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);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 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;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 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;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
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;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
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);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "../interfaces/IConfigStorage.sol";
abstract contract BaseConfigStorage {
IConfigStorage public configStorage;
bool _paused;
modifier onlyConfigStorage() {
if (msg.sender != address(configStorage)) revert OnlyStorageError();
_;
}
modifier onlyConfiguredContract(StorageKey _key) {
address configuredContract = configStorage.getAddress(_key);
if (configuredContract == address(0)) revert UnconfiguredError(_key);
if (configuredContract != msg.sender) revert UnauthorisedError();
_;
}
modifier onlyConfiguredContract2(StorageKey _key, StorageKey _key2) {
address configuredContract = configStorage.getAddress(_key);
address configuredContract2 = configStorage.getAddress(_key2);
if (
configuredContract != msg.sender &&
configuredContract2 != msg.sender
) {
if (configuredContract == address(0))
revert UnconfiguredError(_key);
if (configuredContract2 == address(0))
revert UnconfiguredError(_key2);
revert UnauthorisedError();
}
_;
}
modifier onlyConfiguredContract3(
StorageKey _key,
StorageKey _key2,
StorageKey _key3
) {
address configuredContract = configStorage.getAddress(_key);
address configuredContract2 = configStorage.getAddress(_key2);
address configuredContract3 = configStorage.getAddress(_key3);
if (
configuredContract != msg.sender &&
configuredContract2 != msg.sender &&
configuredContract3 != msg.sender
) {
if (configuredContract == address(0))
revert UnconfiguredError(_key);
if (configuredContract2 == address(0))
revert UnconfiguredError(_key2);
if (configuredContract3 == address(0))
revert UnconfiguredError(_key3);
revert UnauthorisedError();
}
_;
}
modifier onlyOneOfRoles(Role[5] memory roles) {
for (uint256 i = 0; i < roles.length; i++) {
if (msg.sender == configStorage.getRole(roles[i])) {
_;
return;
}
}
revert InvalidRoleError();
}
modifier onlyRole(Role role) {
if (msg.sender != configStorage.getRole(role))
revert InvalidRoleError();
_;
}
modifier onlyUniversalRole(Role role) {
if (msg.sender != configStorage.getUniversalRole(role))
revert InvalidRoleError();
_;
}
modifier onlyAdmin() {
if (msg.sender != configStorage.getUniversalRole(Role.Admin))
revert InvalidRoleError();
_;
}
modifier notPaused() {
if (_paused) revert ContractsPausedError();
_;
}
error UnconfiguredError(StorageKey _key);
error UnauthorisedError();
error OnlyStorageError();
error InvalidRoleError();
error ContractsPausedError();
function configUpdated() external virtual;
function __BaseConfigStorage_setConfigStorage(
address _configStorage
) internal {
configStorage = IConfigStorage(_configStorage);
}
function __BaseConfigStorage_reconfigure() internal {
_paused = configStorage.getBool(StorageKey.Paused);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
/// @notice Contracts which implement this interface can be instructed by Rewards Manager to claim their yield for
/// ERC20 tokens and send the yield back to the rewards manager
interface IERC20YieldClaimable {
function claimERC20Yield(address _tokenContract, uint256 _amount) external;
}{
"remappings": [
"@api3/=node_modules/@api3/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 2000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"ContractsPausedError","type":"error"},{"inputs":[],"name":"DuplicateSprayerError","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"EmptyParameterError","type":"error"},{"inputs":[],"name":"EmptyProposalError","type":"error"},{"inputs":[],"name":"ExistingProposalError","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidGovernorError","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidRealmError","type":"error"},{"inputs":[],"name":"InvalidRoleError","type":"error"},{"inputs":[{"internalType":"address","name":"_mainAccount","type":"address"}],"name":"MainAccountNotRegisteredError","type":"error"},{"inputs":[],"name":"NoPendingRevealError","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyStorageError","type":"error"},{"inputs":[],"name":"PlayerAlreadyRegisteredError","type":"error"},{"inputs":[],"name":"PlayerNotRegisteredError","type":"error"},{"inputs":[],"name":"SelfReferralError","type":"error"},{"inputs":[],"name":"SubAccountAlreadyRegisteredError","type":"error"},{"inputs":[],"name":"SubAccountCannotRegisterError","type":"error"},{"inputs":[],"name":"SubAccountNotRegisteredError","type":"error"},{"inputs":[],"name":"TooHighSprayAmountError","type":"error"},{"inputs":[],"name":"TooManyEntriesError","type":"error"},{"inputs":[],"name":"TooManySubAccountsError","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"UnMatchedParametersError","type":"error"},{"inputs":[],"name":"UnauthorisedError","type":"error"},{"inputs":[{"internalType":"enum StorageKey","name":"_key","type":"uint8"}],"name":"UnconfiguredError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_player","type":"address"},{"indexed":false,"internalType":"uint256","name":"_harvestedSchnibbles","type":"uint256"}],"name":"Harvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_player","type":"address"},{"indexed":false,"internalType":"enum MunchablesCommonLib.Realm","name":"_snuggeryRealm","type":"uint8"},{"indexed":false,"internalType":"address","name":"_referrer","type":"address"}],"name":"PlayerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_proposer","type":"address"},{"components":[{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"schnibbles","type":"uint256"}],"indexed":false,"internalType":"struct IAccountManager.Squirt[]","name":"_squirts","type":"tuple[]"}],"name":"ProposedScnibblesSpray","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_player","type":"address"},{"indexed":false,"internalType":"uint256","name":"_schnibbles","type":"uint256"}],"name":"SchnibblesSprayed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_player","type":"address"},{"indexed":false,"internalType":"uint256","name":"_schnibbles","type":"uint256"}],"name":"SchnibblesSprayedRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_proposer","type":"address"}],"name":"SprayProposalExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_proposer","type":"address"}],"name":"SprayProposalRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_player","type":"address"},{"indexed":false,"internalType":"address","name":"_subAccount","type":"address"}],"name":"SubAccountAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_player","type":"address"},{"indexed":false,"internalType":"address","name":"_subAccount","type":"address"}],"name":"SubAccountRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDB","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subAccount","type":"address"}],"name":"addSubAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blastContract","outputs":[{"internalType":"contract IBlast","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blastPointsContract","outputs":[{"internalType":"contract IBlastPoints","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenContract","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"claimERC20Yield","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"configStorage","outputs":[{"internalType":"contract IConfigStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configUpdated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_proposer","type":"address"}],"name":"execSprayProposal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_player","type":"address"}],"name":"forceHarvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getConfiguredGovernor","outputs":[{"internalType":"address","name":"_governor","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"}],"name":"getDailySchnibbles","outputs":[{"internalType":"uint256","name":"_dailySchnibbles","type":"uint256"},{"internalType":"uint256","name":"_bonus","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getFullPlayerData","outputs":[{"internalType":"address","name":"_mainAccount","type":"address"},{"components":[{"internalType":"uint32","name":"registrationDate","type":"uint32"},{"internalType":"uint32","name":"lastPetMunchable","type":"uint32"},{"internalType":"uint32","name":"lastHarvestDate","type":"uint32"},{"internalType":"enum MunchablesCommonLib.Realm","name":"snuggeryRealm","type":"uint8"},{"internalType":"uint16","name":"maxSnuggerySize","type":"uint16"},{"internalType":"uint256","name":"unfedSchnibbles","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"internalType":"struct MunchablesCommonLib.Player","name":"_player","type":"tuple"},{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint32","name":"importedDate","type":"uint32"}],"internalType":"struct MunchablesCommonLib.SnuggeryNFT[]","name":"_snuggery","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_maybeSubAccount","type":"address"}],"name":"getMainAccount","outputs":[{"internalType":"address","name":"_mainAccount","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getPlayer","outputs":[{"internalType":"address","name":"_mainAccount","type":"address"},{"components":[{"internalType":"uint32","name":"registrationDate","type":"uint32"},{"internalType":"uint32","name":"lastPetMunchable","type":"uint32"},{"internalType":"uint32","name":"lastHarvestDate","type":"uint32"},{"internalType":"enum MunchablesCommonLib.Realm","name":"snuggeryRealm","type":"uint8"},{"internalType":"uint16","name":"maxSnuggerySize","type":"uint16"},{"internalType":"uint256","name":"unfedSchnibbles","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"internalType":"struct MunchablesCommonLib.Player","name":"_player","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_player","type":"address"},{"internalType":"uint256","name":"_start","type":"uint256"}],"name":"getSubAccounts","outputs":[{"internalType":"address[20]","name":"_subAccounts","type":"address[20]"},{"internalType":"bool","name":"_more","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[{"internalType":"uint256","name":"_harvested","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_configStorage","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mainAccounts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum MunchablesCommonLib.Realm","name":"_snuggeryRealm","type":"uint8"},{"internalType":"address","name":"_referrer","type":"address"}],"name":"register","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_player","type":"address"},{"internalType":"uint256","name":"_schnibbles","type":"uint256"}],"name":"removeSpray","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_proposer","type":"address"}],"name":"removeSprayProposal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_subAccount","type":"address"}],"name":"removeSubAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_player","type":"address"},{"internalType":"uint256","name":"_schnibbles","type":"uint256"}],"name":"rewardSpray","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"sprayProposals","outputs":[{"internalType":"uint32","name":"proposedDate","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_players","type":"address[]"},{"internalType":"uint256[]","name":"_schnibbles","type":"uint256[]"}],"name":"spraySchnibblesPropose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"subAccounts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unclaimedSchnibbles","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"components":[{"internalType":"uint32","name":"registrationDate","type":"uint32"},{"internalType":"uint32","name":"lastPetMunchable","type":"uint32"},{"internalType":"uint32","name":"lastHarvestDate","type":"uint32"},{"internalType":"enum MunchablesCommonLib.Realm","name":"snuggeryRealm","type":"uint8"},{"internalType":"uint16","name":"maxSnuggerySize","type":"uint16"},{"internalType":"uint256","name":"unfedSchnibbles","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"internalType":"struct MunchablesCommonLib.Player","name":"_player","type":"tuple"}],"name":"updatePlayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]Contract Creation Code
60a0806040523460c857306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660b957506001600160401b036002600160401b0319828216016075575b604051613ce590816100ce82396080518181816110ba01526111b10152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880806056565b63f92ee8a960e01b8152600490fd5b600080fdfe608080604052600436101561001357600080fd5b600090813560e01c90816305225a8f146120be575080630ba3aae3146120765780630f3dfd4a146120385780631739cfcd14611cc35780631f374c7d14611b475780632cd4dbe914611b0c5780632f2ab364146117c857806331a0edec146117a157806334d573f3146117465780633cdad82c1461171f5780634030bdb9146116fd578063443b1786146116d75780634641257d1461164a5780634b03eeb41461148a5780634f1ef2861461113457806352d1902d1461109f5780635c12cd4b14610fe15780635ec78cad14610fa95780638c93caec14610f745780638da52e9314610f20578063984b930f14610da35780639d138fb814610b65578063ad3cb1cc14610ace578063ad5c464814610aa7578063b27a69dc146107af578063b52e1eff1461055f578063b930b48214610538578063c4d66de81461025e578063d1c4573a1461022e578063dda39047146101cf578063fce14af6146101a85763fe0b77701461018157600080fd5b346101a55760206003193601126101a5576101a261019d6121bf565b612b2c565b80f35b80fd5b50346101a557806003193601126101a55760206001600160a01b0360015416604051908152f35b50346101a55760406003193601126101a55767ffffffffffffffff60043581811161022a5761020290369060040161232d565b906024359283116102265761021e6101a293369060040161232d565b929091612777565b8380fd5b8280fd5b50346101a55760206003193601126101a557604061025261024d6121bf565b612664565b82519182526020820152f35b50346101a55760206003193601126101a5576102786121bf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0080549160ff8360401c16159067ffffffffffffffff80851694851580610531575b60018097149081610527575b15908161051e575b506104805767ffffffffffffffff199084878383161787556104ff575b5084549260ff8460401c16159383811688811591826104f7575b1490816104ed575b1590816104e4575b506104805784888483161788556104c5575b50855460ff8160401c161593811688811591826104bd575b1490816104b3575b1590816104aa575b506104805783886001600160a01b03948316178855610461575b501673ffffffffffffffffffffffffffffffffffffffff19875416178655610422575b6103e3575b61039961303b565b6103a1578280f35b7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2918168ff00000000000000001960209354169055604051908152a138808280f35b68ff00000000000000001982541682557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26020604051858152a1610391565b68ff00000000000000001983541683557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26020604051868152a161038c565b68ffffffffffffffffff19166801000000000000000117865538610369565b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b9050153861034f565b303b159150610347565b86925061033f565b68ffffffffffffffffff19166801000000000000000117865538610327565b90501538610315565b303b15915061030d565b879250610305565b68ffffffffffffffffff191668010000000000000001178555386102eb565b905015386102ce565b303b1591506102c6565b50836102ba565b50346101a557806003193601126101a55760206001600160a01b0360025416604051908152f35b50346101a557602090816003193601126101a55761057b6121bf565b9060ff815460a01c16610785573381526007835263ffffffff91826040832054161561075b576001600160a01b0391828216938482526007865260408220541661073157338152600994600981526005604083205410156107075784825260088152836040832054166106465784825260088152604082203373ffffffffffffffffffffffffffffffffffffffff19825416179055815b33835286825260408320805482101561067057856106318389936121d5565b90549060031b1c161461064657600101610612565b60046040517fbe592acd000000000000000000000000000000000000000000000000000000008152fd5b838784873384526009825260408420805490680100000000000000008210156106f357916106c8827e861aca61348df7264e2219d6502d9be3b0faea03b4649e08aa285403ef644e959460016106e7950181556121d5565b9091906001600160a01b038084549260031b9316831b921b1916179055565b6040519283523392a280f35b602486634e487b7160e01b81526041600452fd5b60046040517fb7337cab000000000000000000000000000000000000000000000000000000008152fd5b60046040517f11ac60b6000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3e3a049f000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8f85eea2000000000000000000000000000000000000000000000000000000008152fd5b50346101a55760406003193601126101a5576004356006811015610aa3576024356001600160a01b03808216809203610a9e573384526020906007825263ffffffff908160408720541661073157338652600883528060408720541680610a8d575b506005851015610a6357338414610a395761082a612607565b82421690818152602485848a5416604051928380927fc4c379cd000000000000000000000000000000000000000000000000000000008252601960048301525afa908115610a2e578991610a01575b506080820161ffff8092168152606083016108948a8261263f565b61092660c08501978a895260408601968752338d52600b8a5260408d2054806109e9575b5060078a5260408d2086518154888d015167ffffffff00000000199285169290921667ffffffffffffffff199091161790831660201b67ffffffff0000000016178155965187546bffffffff00000000000000001916911660401b6bffffffff000000000000000016178655565b5160068110156109d55760a092859492610945610975936002986123c6565b5185546effff000000000000000000000000001916911660681b6effff0000000000000000000000000016178455565b015160018201550191511673ffffffffffffffffffffffffffffffffffffffff198254161790556109a960405180946122aa565b8201527f129d52cd18b556e6c499d4f4d5633e26b4efe19850879f849e20cd70b795770160403392a280f35b60248b634e487b7160e01b81526021600452fd5b60a0870152338d52600b8a528c6040812055386108b8565b610a219150863d8811610a27575b610a19818361226b565b81019061264b565b38610879565b503d610a0f565b6040513d8b823e3d90fd5b60046040517f3d5062bb000000000000000000000000000000000000000000000000000000008152fd5b60046040517f937fc9f7000000000000000000000000000000000000000000000000000000008152fd5b610a98903390612d78565b38610811565b600080fd5b5080fd5b50346101a557806003193601126101a55760206001600160a01b0360065416604051908152f35b50346101a557806003193601126101a557604051610aeb81612233565b6005815260207f352e302e30000000000000000000000000000000000000000000000000000000602083015260405192839160208352835193846020850152825b858110610b4e57505050601f83601f1992604080968601015201168101030190f35b818101830151878201604001528694508201610b2c565b50346101a557602080600319360112610aa3576024610b9f91610b866121bf565b610b8e612607565b506001600160a01b03938491613012565b169081855260078152604085209460405194610bba86612203565b8187549161ffff63ffffffff938481168a528481881c16878b0152848160401c1660408b0152610bf360ff8260601c1660608c0161263f565b60681c16608089015280600260019a600181015460a08c015201541660c089015260125416604051968780927f0659b1880000000000000000000000000000000000000000000000000000000082528860048301525afa948515610d98578295610cbf575b506040969496939291935195610c7a61012091828901958952848901906122cd565b6101008701528651809352816101408701970193905b838210610c9d5786880387f35b8451805189528301518116838901526040909701969382019390850190610c90565b9094503d8083833e610cd1818361226b565b810190838183031261022a57805167ffffffffffffffff91828211610d9457019082601f83011215610226578151908111610d805760405192610d19868360051b018561226b565b818452858085019260061b84010192818411610d7c578601915b838310610d4557505050509338610c58565b604083830312610d7c57866040918251610d5e81612233565b85518152610d6d8387016123a8565b83820152815201920191610d33565b8580fd5b602484634e487b7160e01b81526041600452fd5b8480fd5b6040513d84823e3d90fd5b50346101a55760406003193601126101a557610dbd6121bf565b6040516024803592610280830167ffffffffffffffff811184821017610f0a576040526102803684376001600160a01b0381168552600960205260408520548590855b601487018711610ef75760148701811015610eeb5781811015610e77576001600160a01b03841688526009602052610e3b8160408a206121d5565b9054906014831015610e645760031b1c6001600160a01b0316600582901b870152600101610e00565b868a634e487b7160e01b81526032600452fd5b509250949390505b601481018111610ed75760140110610ece575b60405191825b60148210610eaf576102a084861515610280820152f35b6020806001926001600160a01b03865116815201930191019091610e98565b60019250610e92565b602484634e487b7160e01b81526011600452fd5b50925094939050610e7f565b8488634e487b7160e01b81526011600452fd5b634e487b7160e01b600052604160045260246000fd5b50346101a557806003193601126101a5576001600160a01b038154163303610f4a576101a261303b565b60046040517fe1010280000000000000000000000000000000000000000000000000000000008152fd5b50346101a55760206003193601126101a5576020610f98610f936121bf565b613012565b6001600160a01b0360405191168152f35b50346101a55760206003193601126101a55760406020916001600160a01b03610fd06121bf565b168152600b83522054604051908152f35b50346101a55760206003193601126101a5576101009061109d61102060406110076121bf565b61100f612607565b506001600160a01b03928391613012565b16938481526007602052209060026040519261103b84612203565b61ffff815463ffffffff8082168752808260201c1660208801528160401c16604087015261107260ff8260601c166060880161263f565b60681c166080850152600181015460a085015201541660c082015260405192835260208301906122cd565bf35b50346101a557806003193601126101a5576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016300361110a5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b60046040517fe07c8dba000000000000000000000000000000000000000000000000000000008152fd5b5060406003193601126101a5576111496121bf565b602491823567ffffffffffffffff8111610aa35736602382011215610aa3578060040135926111778461228e565b611184604051918261226b565b8481526020948582019336888383010111610d7c57818692898993018737830101526001600160a01b03807f00000000000000000000000000000000000000000000000000000000000000001680301490811561145c575b5061110a57868682875416604051928380927fd91078070000000000000000000000000000000000000000000000000000000082528a60048301525afa908115611451579082918791611419575b5016330361140857821694604051907f52d1902d00000000000000000000000000000000000000000000000000000000825280826004818a5afa91829187936113d8575b50506112a4578686604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b8590877f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc918281036113aa5750843b1561137b5750805473ffffffffffffffffffffffffffffffffffffffff1916821790556040518592917fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a281511561134557506113419382915190845af461133b613a97565b91613adf565b5080f35b935050505034611353575080f35b807fb398979f0000000000000000000000000000000000000000000000000000000060049252fd5b82604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b604051907faa1d49a40000000000000000000000000000000000000000000000000000000082526004820152fd5b9080929350813d8311611401575b6113f0818361226b565b81010312610d7c575190388061126e565b503d6113e6565b6004604051637bd5018b60e11b8152fd5b809250888092503d831161144a575b611432818361226b565b81010312610d7c57611444829161235e565b3861122a565b503d611428565b6040513d88823e3d90fd5b9050817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54161415386111dc565b50346101a55760406003193601126101a5576114a46121bf565b6001600160a01b0390818354166040519263bcaa0c5560e01b808552600c60048601526020948581602481875afa801561163f5783918891611603575b501680156115eb5733036115da5784906024604051809581938252600c60048301525afa80156115cf5784928691611597575b50604490868360405196879586947faad3ec960000000000000000000000000000000000000000000000000000000086521660048501526024356024850152165af1801561158c57611564578280f35b813d8311611585575b611577818361226b565b81010312610a9e5738808280f35b503d61156d565b6040513d85823e3d90fd5b83819492503d83116115c8575b6115ae818361226b565b81010312610d945760446115c2859361235e565b90611514565b503d6115a4565b6040513d87823e3d90fd5b60046040516306923c2160e21b8152fd5b602460405163092bc2cd60e41b8152600c6004820152fd5b809250878092503d8311611638575b61161c818361226b565b810103126116345761162e839161235e565b386114e1565b8680fd5b503d611612565b6040513d89823e3d90fd5b50346101a557806003193601126101a55760ff815460a01c166107855761167033613012565b9063ffffffff60406001600160a01b038416928381526007602052205416156116a657602061169e83612f2d565b604051908152f35b602490604051907f96e543170000000000000000000000000000000000000000000000000000000082526004820152fd5b50346101a557806003193601126101a5576001600160a01b036020915416604051908152f35b50346101a55760206003193601126101a5576101a261171a6121bf565b61243f565b50346101a557806003193601126101a55760206001600160a01b0360035416604051908152f35b50346101a55760406003193601126101a5576117606121bf565b602435916001600160a01b0380921681526009602052604081209081548410156101a55750602092611791916121d5565b9190546040519260031b1c168152f35b50346101a557806003193601126101a55760206001600160a01b0360055416604051908152f35b50346101a5576101006003193601126101a5576117e36121bf565b60e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610aa3576001600160a01b039081835416906040519063bcaa0c5560e01b918281526007600482015260209260249484838781845afa928315611a8e578893611ad5575b506040518281526008600482015285818881855afa928315610a2e5786918a94611a99575b5087604051809481938252601f60048301525afa928315611a8e5787918994611a50575b50163381141580611a44575b80611a38575b6119cc5750505090836007921685525260408320903563ffffffff90818116809103610a9e5763ffffffff198354161782556044358181168103610a9e57825467ffffffff00000000191660209190911b67ffffffff00000000161782556064359081168103610a9e5781546bffffffff0000000000000000191660409190911b6bffffffff00000000000000001617815560843560068110156102265761195090826123c6565b60a4359061ffff821682036102265761199660029282906effff00000000000000000000000000196effff0000000000000000000000000083549260681b169116179055565b60c4356001820155019060e435908116809103610a9e5773ffffffffffffffffffffffffffffffffffffffff1982541617905580f35b918691869315611a2157821615611a0a5716156119f45760046040516306923c2160e21b8152fd5b60405163092bc2cd60e41b8152601f6004820152fd5b8260405163092bc2cd60e41b815260086004820152fd5b8360405163092bc2cd60e41b815260076004820152fd5b503387841614156118a8565b503387831614156118a2565b86809295508193503d8311611a87575b611a6a818361226b565b81010312611a8357611a7c879161235e565b9238611896565b8780fd5b503d611a60565b6040513d8a823e3d90fd5b8281939295503d8311611ace575b611ab1818361226b565b81010312611aca57611ac3869161235e565b9238611872565b8880fd5b503d611aa7565b9092508481813d8311611b05575b611aed818361226b565b81010312611a8357611afe9061235e565b913861184d565b503d611ae3565b50346101a55760206003193601126101a55760209060406001600160a01b039182611b356121bf565b16815260088452205416604051908152f35b50346101a55760406003193601126101a557611b616121bf565b602435906001600160a01b03808454169060405180926318a2299b60e21b82526014600483015281602460209586935afa80156114515782918791611c8b575b50163303611408578315611c61576013548411611c37577fdefb2b4d25401a6c19a6dc5d6b883d2432f7dd04b16b5089efb5053e9099860c9216928385526007825263ffffffff6040862054161515600014611c1d578385526007825260016040862001611c108282546123b9565b90555b604051908152a280f35b600b825260408520611c308282546123b9565b9055611c13565b60046040517f7ee36909000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3908655a000000000000000000000000000000000000000000000000000000008152fd5b809250848092503d8311611cbc575b611ca4818361226b565b81010312610d7c57611cb6829161235e565b38611ba1565b503d611c9a565b50346101a557602090816003193601126101a557611cdf6121bf565b906001600160a01b039182825416926040519463bcaa0c5560e01b8087526002600488015260249582888881845afa9081156114515787988798969792611ff6575b508390604051998a8092868252600960048301525afa97881561163f578798611fbe575b508316963388141580611fb2575b611f655750859650604051947f5c12cd4b0000000000000000000000000000000000000000000000000000000086528381169586600482015261010080828881305afa908115610a2e578991611e9a575b5063ffffffff91505116611db6578680f35b611dbf90612f2d565b5081838754169185604051809481938252600260048301525afa9081156114515783928792611e62575b5050163314611dfa575b8080808680f35b6014541690813b15611e5d57839260405194859384927fcd67953200000000000000000000000000000000000000000000000000000000845260048401525af18015610d9857611e4e575b80808392611df3565b611e579061221f565b38611e45565b505050fd5b8193508092503d8311611e93575b611e7a818361226b565b81010312610d9457611e8c829161235e565b3880611de9565b503d611e70565b905081813d8311611f5e575b611eb0818361226b565b810103908112611aca57601f1960e091611ec98461235e565b500112611a8357604051611edc81612203565b611ee78583016123a8565b8152611ef5604083016123a8565b85820152611f05606083016123a8565b604082015260808201516006811015611f5a57606082015260a08201519161ffff83168303611f5a5760e0611f4f9163ffffffff94608085015260c081015160a08501520161235e565b60c082015238611da4565b8980fd5b503d611ea6565b9150508515611f9b571615611f855760046040516306923c2160e21b8152fd5b60405163092bc2cd60e41b815260096004820152fd5b8260405163092bc2cd60e41b815260026004820152fd5b50338482161415611d53565b9097508281813d8311611fef575b611fd6818361226b565b8101031261163457611fe8849161235e565b9790611d45565b503d611fcc565b84809697508193509193943d8311612031575b612013818361226b565b8101031261163457908361202a899695949361235e565b9190611d21565b503d612009565b50346101a55760206003193601126101a55763ffffffff60406020926001600160a01b036120646121bf565b168152600a8452205416604051908152f35b50346101a55760206003193601126101a5576120906121bf565b60ff825460a01c1661078557338252600760205263ffffffff6040832054161561075b576101a29033612d78565b905034610aa3576040600319360112610aa3576120d96121bf565b602435916001600160a01b0381818654166318a2299b60e21b82526014600483015281602460209586935afa80156114515782918791612187575b50163303611408577fd7ca3fb901656d5fcc7ae06d950d190a8dedb0bee0cd205d699ef5ecb4ae83749216928385526007825263ffffffff6040862054161515600014612174578385526007825260016040862001611c10828254612385565b600b825260408520611c30828254612385565b809250848092503d83116121b8575b6121a0818361226b565b81010312610d7c576121b2829161235e565b38612114565b503d612196565b600435906001600160a01b0382168203610a9e57565b80548210156121ed5760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b60e0810190811067ffffffffffffffff821117610f0a57604052565b67ffffffffffffffff8111610f0a57604052565b6040810190811067ffffffffffffffff821117610f0a57604052565b60a0810190811067ffffffffffffffff821117610f0a57604052565b90601f601f19910116810190811067ffffffffffffffff821117610f0a57604052565b67ffffffffffffffff8111610f0a57601f01601f191660200190565b9060068210156122b75752565b634e487b7160e01b600052602160045260246000fd5b6001600160a01b0360c0809263ffffffff808251168652806020830151166020870152604082015116604086015261230d606082015160608701906122aa565b61ffff608082015116608086015260a081015160a0860152015116910152565b9181601f84011215610a9e5782359167ffffffffffffffff8311610a9e576020808501948460051b010111610a9e57565b51906001600160a01b0382168203610a9e57565b9190602083019260158210156122b75752565b9190820391821161239257565b634e487b7160e01b600052601160045260246000fd5b519063ffffffff82168203610a9e57565b9190820180921161239257565b9060068110156122b7577fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff6cff00000000000000000000000083549260601b169116179055565b356001600160a01b0381168103610a9e5790565b9060058110156121ed5760051b0190565b5160158110156122b75790565b604090815161244d8161224f565b6006815260209060078282015260088482015260096060820152600a90600a608082015260005b6005811061248c5760048651637bd5018b60e11b8152fd5b6001600160a01b036124ca8582600054166124af6124aa8688612421565b612432565b908a5180809581946318a2299b60e21b835260048301612372565b03915afa90811561256857908291600091612531575b501633146124f15750600101612474565b91505061250a9394929416938460005252600020612586565b7faf42be50f1f5f8787a4bbed666dd5a7a36611d2e60a55e6db3f81dc56c9a5711600080a2565b91508682813d8311612561575b612548818361226b565b810103126101a5575061255b829161235e565b386124e0565b503d61253e565b88513d6000823e3d90fd5b8181029291811591840414171561239257565b6000808255600180920191825492828155836125a3575b50505050565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841684036125f3578252602082209260011b8301925b8381101561259d578083600292558383820155016125da565b602483634e487b7160e01b81526011600452fd5b6040519061261482612203565b8160c06000918281528260208201528260408201528260608201528260808201528260a08201520152565b60068210156122b75752565b90816020910312610a9e57518060010b8103610a9e5790565b906001600160a01b03809281600d5416906040519283917fdf0fbb65000000000000000000000000000000000000000000000000000000008352169182600483015281602460209586935afa801561273b578391600091612747575b50600a90049460115416916024604051809481937f679c93b900000000000000000000000000000000000000000000000000000000835260048301525afa91821561273b5760009261271157505090565b90809250813d8311612734575b612728818361226b565b81010312610a9e575190565b503d61271e565b6040513d6000823e3d90fd5b82819392503d8311612770575b61275e818361226b565b81010312610a9e57518290600a6126c0565b503d612754565b939290919260409384519261278b8461224f565b6001948585526020906002828701526003888701526004988960608801526005966005608082015260005b8881106127cc578b8b51637bd5018b60e11b8152fd5b8b612807868d6001600160a01b03938460005416906127ee6124aa888a612421565b92518095819482936318a2299b60e21b84528301612372565b03915afa908115612af557908291600091612abe575b5016331461282d575089016127b6565b95979b92939496999a985050508215612a9757858303612a705761ffff60065460a01c168311612a495733600052600a9586865263ffffffff80896000205416612a21573360005287875261288489600020612586565b8a60005b8681106129f6575050336000528787528860002090421663ffffffff1982541617905560005b8481106128c2575050505050505050505050565b856128d66128d1838888612b00565b61240d565b16600052600c80885260ff8a60002054166129ce57336000528888528a8c8b60002001612912848661290c6128d1838d8d612b00565b94612b00565b3590898d519361292185612233565b1683528a8301918252805490680100000000000000008210156129b9578f8201815561294d9190612b10565b9290926129a55751825473ffffffffffffffffffffffffffffffffffffffff1916908a1617825551908d01558b91908761298b6128d1848a8a612b00565b166000528852896000208260ff19825416179055016128ae565b600087634e487b7160e01b82525260246000fd5b604188634e487b7160e01b6000525260246000fd5b838a517f256fe3c7000000000000000000000000000000000000000000000000000000008152fd5b87612a056128d1838a8a612b00565b16600052600c89528a60002060ff198154169055018b90612888565b8289517f33cfa0ab000000000000000000000000000000000000000000000000000000008152fd5b86517f2c72acee000000000000000000000000000000000000000000000000000000008152fd5b86517f46d3a1b6000000000000000000000000000000000000000000000000000000008152fd5b86517f6dcdca7a000000000000000000000000000000000000000000000000000000008152fd5b91508782813d8311612aee575b612ad5818361226b565b810103126101a55750612ae8829161235e565b3861281d565b503d612acb565b8d513d6000823e3d90fd5b91908110156121ed5760051b0190565b80548210156121ed5760005260206000209060011b0190600090565b90604090815190612b3c8261224f565b600682526007602060078185015260088585015260096060850152600a9081608086015260005b60058110612b7b5760048751637bd5018b60e11b8152fd5b6001600160a01b038060005416612b956124aa848a612421565b90848a5180926318a2299b60e21b82528180612bb5600497888301612372565b03915afa908115612d6d57908391600091612d36575b50163314612bdd575050600101612b63565b92939496509788915096949616948560005283835263ffffffff918286600020541615612d0f575085600052838352600191828660002001549160005b838110612c6157505050505083612c3a9495965060005252600020612586565b7f2bd78aa6599e4965a32ba17ee89f34e7dfc8df640c4984ca2875c7905d0f4dbf600080a2565b80897fdefb2b4d25401a6c19a6dc5d6b883d2432f7dd04b16b5089efb5053e9099860c88888f8196828f8f612cb89489600052818852612ca685858560002001612b10565b50541698600052865260002001612b10565b50015460008481528783528d902054881615612cf55783600052868252858d60002001612ce68282546123b9565b90555b8c51908152a201612c1a565b600b82528c600020612d088282546123b9565b9055612ce9565b85517f3908655a000000000000000000000000000000000000000000000000000000008152fd5b91508582813d8311612d66575b612d4d818361226b565b810103126101a55750612d60839161235e565b38612bcb565b503d612d43565b8a513d6000823e3d90fd5b916001600160a01b0380921691600092808452602090600882528260409687872073ffffffffffffffffffffffffffffffffffffffff1981541690551692838652600980845287872054908792885b8a848210612e34575b505050505090919293945015612e0b577ff5558e1d420cd51517634d283f7c62af1bdc4bda1ad3ddf9ca08ebe983b4723f92939451908152a2565b600485517fa7c04edf000000000000000000000000000000000000000000000000000000008152fd5b86612e488385938e8d8152888d52206121d5565b929054600393841b1c1614612e605750600101612dc7565b9450878a949293969798999a528588528a8420917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92838201918211612f195784612eb2612ec894936106c8936121d5565b905490891b1c16918b8752888b528d87206121d5565b6001948884528752898320928354908115612f0557500192612eea84846121d5565b81939154921b1b19169055559084939291388080808a612dd0565b80634e487b7160e01b602492526031600452fd5b602486634e487b7160e01b81526011600452fd5b906001600160a01b03612f5d612f56670de0b6b3a7640000612f4e86612664565b819391612573565b04906123b9565b9216612fe560008281526007602052604062015180612f9263ffffffff97612f8c8985872054861c1642612385565b90612573565b04958483526007602052600182842001612fad8882546123b9565b9055848352600760205242169120906bffffffff0000000000000000196bffffffff000000000000000083549260401b169116179055565b7f121c5042302bae5fc561fbc64368f297ca60a880878e1e3a7f7e9380377260bf602084604051908152a2565b906001600160a01b038083166000526008602052604060002054169182156130375750565b9150565b60006001600160a01b038082541690604090815163bcaa0c5560e01b9384825260049460028684015260209260249284828581845afa9182156138b8578992613a60575b508573ffffffffffffffffffffffffffffffffffffffff19921682600d541617600d55865183815260058982015285818681855afa9081156135c3579087918b91613a28575b501682600e541617600e55865183815260148982015285818681855afa9081156135c3579087918b916139f0575b5016826010541617601055865183815260158982015285818681855afa9081156135c3579087918b916139b8575b501682600f541617600f558651838152858982015285818681855afa9081156135c3579087918b91613980575b5016826011541617601155865183815260078982015285818681855afa9081156135c3579087918b91613948575b501682601254161760125586517fc4c379cd000000000000000000000000000000000000000000000000000000008152601c8982015285818681855afa9081156135c3578a9161392b575b507fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000006006549260a01b1691161760065586517f42ab14dd000000000000000000000000000000000000000000000000000000008152602f8982015285818681855afa9081156135c3578a916138fe575b506013558651838152601f8982015285818681855afa9081156135c3579187879287948d916138c2575b5016846014541617601455885192838092878252600f8d8301525afa9081156138b8578991613883575b5060015486821690838882168303613771575b5050505084885416865183815260108982015285818681855afa9081156135c3578a9161373c575b5086600254911690878116820361363f575b50505084885416865183815260128982015285818681855afa9081156135c3578a9161360a575b50856013868b8b5195869384928a84528301525afa9182156135c357918a9391879385926135cd575b50886005549116908981168203613538575b50508760065491169188821683036134b4575b505050908590541691838751809481938252600c8b8301525afa9081156134aa5790839291889161346f575b5090818560019316613460575b50875496865195869384927ff2aac76c00000000000000000000000000000000000000000000000000000000845283015288165afa92831561345757509174ff0000000000000000000000000000000000000000917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff93869261342a575b5050151560a01b169116179055565b6134499250803d10613450575b613441818361226b565b810190613ac7565b388061341b565b503d613437565b513d86823e3d90fd5b61346990613b72565b3861339d565b8381939492503d83116134a3575b613487818361226b565b8101031261163457829161349c60019261235e565b9091613390565b503d61347d565b85513d89823e3d90fd5b82911617600655848851809481937f1a33757d00000000000000000000000000000000000000000000000000000000835260028d8401525af1801561352e57908491613505575b9081808a93613364565b813d8311613527575b613518818361226b565b81010312610a9e5782386134fb565b503d61350e565b86513d8a823e3d90fd5b8395508193919294501617600555856002868b8d8c5195869485937f1a33757d0000000000000000000000000000000000000000000000000000000085528401525af180156135c357918a93918793613592575b80613351565b9092809294503d83116135bc575b6135aa818361226b565b81010312610a9e57889185913861358c565b503d6135a0565b88513d8c823e3d90fd5b92945092905081813d8311613603575b6135e7818361226b565b81010312611f5a579185916135fc8b9461235e565b903861333f565b503d6135dd565b90508581813d8311613638575b613621818361226b565b81010312611f5a576136329061235e565b38613316565b503d613617565b8184821617600255866011878c8c5196879384928b84528301525afa92831561373257908b9182946136f4575b50888b54161561367d575b506132ef565b161790813b15611f5a57908987868b94838c5195869485937f36b91f2b0000000000000000000000000000000000000000000000000000000085521680988401525af180156135c357908a916136e0575b50508188541617875538808981613677565b6136e99061221f565b611aca5788386136ce565b88809295508193503d831161372b575b61370e818361226b565b81010312613727576137208b9161235e565b923861366c565b8a80fd5b503d613704565b89513d8d823e3d90fd5b90508581813d831161376a575b613753818361226b565b81010312611f5a576137649061235e565b386132dd565b503d613749565b16811760015587517ffe9fbb80000000000000000000000000000000000000000000000000000000008152308a82015286818781855afa908115613732578b91613866575b506137c3575b80836132b5565b803b15611f5a578980918a8a51809481937f4e606c470000000000000000000000000000000000000000000000000000000083525af180156135c357613853575b50888091885182888201917ff098767a0000000000000000000000000000000000000000000000000000000083528c815261383e81612233565b51925af15061384b613a97565b5038806137bc565b61385f9099919961221f565b9738613804565b61387d9150873d891161345057613441818361226b565b386137b6565b90508481813d83116138b1575b61389a818361226b565b81010312611aca576138ab9061235e565b386132a2565b503d613890565b87513d8b823e3d90fd5b93929450505081813d83116138f7575b6138dc818361226b565b81010312611f5a578491876138f1889361235e565b38613278565b503d6138d2565b90508581813d8311613924575b613915818361226b565b81010312611f5a57513861324e565b503d61390b565b6139429150863d8811610a2757610a19818361226b565b386131c7565b809250878092503d8311613979575b613961818361226b565b81010312611f5a57613973879161235e565b3861317c565b503d613957565b809250878092503d83116139b1575b613999818361226b565b81010312611f5a576139ab879161235e565b3861314e565b503d61398f565b809250878092503d83116139e9575b6139d1818361226b565b81010312611f5a576139e3879161235e565b38613121565b503d6139c7565b809250878092503d8311613a21575b613a09818361226b565b81010312611f5a57613a1b879161235e565b386130f3565b503d6139ff565b809250878092503d8311613a59575b613a41818361226b565b81010312611f5a57613a53879161235e565b386130c5565b503d613a37565b9091508481813d8311613a90575b613a78818361226b565b81010312611aca57613a899061235e565b903861307f565b503d613a6e565b3d15613ac2573d90613aa88261228e565b91613ab6604051938461226b565b82523d6000602084013e565b606090565b90816020910312610a9e57518015158103610a9e5790565b90613b1e5750805115613af457805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580613b69575b613b2f575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15613b27565b6001600160a01b03809116908115613c8557806001541615613c8157600354811680613c205750308203613bc3575b505b73ffffffffffffffffffffffffffffffffffffffff196003541617600355565b60015416803b15610a9e57600080916024604051809481937feb8646980000000000000000000000000000000000000000000000000000000083528760048401525af1801561273b5715613ba157613c1a9061221f565b38613ba1565b8091503b15610a9e57600080916024604051809481937f3a74bfd70000000000000000000000000000000000000000000000000000000083528760048401525af1801561273b57613c72575b50613ba3565b613c7b9061221f565b38613c6c565b5050565b60046040517f05d8ce3d000000000000000000000000000000000000000000000000000000008152fdfea264697066735822122059dee25398ad78919513b18a6c0cbd1e752cb075de67f27580cdfa8107f8148a64736f6c63430008190033
Deployed Bytecode
0x608080604052600436101561001357600080fd5b600090813560e01c90816305225a8f146120be575080630ba3aae3146120765780630f3dfd4a146120385780631739cfcd14611cc35780631f374c7d14611b475780632cd4dbe914611b0c5780632f2ab364146117c857806331a0edec146117a157806334d573f3146117465780633cdad82c1461171f5780634030bdb9146116fd578063443b1786146116d75780634641257d1461164a5780634b03eeb41461148a5780634f1ef2861461113457806352d1902d1461109f5780635c12cd4b14610fe15780635ec78cad14610fa95780638c93caec14610f745780638da52e9314610f20578063984b930f14610da35780639d138fb814610b65578063ad3cb1cc14610ace578063ad5c464814610aa7578063b27a69dc146107af578063b52e1eff1461055f578063b930b48214610538578063c4d66de81461025e578063d1c4573a1461022e578063dda39047146101cf578063fce14af6146101a85763fe0b77701461018157600080fd5b346101a55760206003193601126101a5576101a261019d6121bf565b612b2c565b80f35b80fd5b50346101a557806003193601126101a55760206001600160a01b0360015416604051908152f35b50346101a55760406003193601126101a55767ffffffffffffffff60043581811161022a5761020290369060040161232d565b906024359283116102265761021e6101a293369060040161232d565b929091612777565b8380fd5b8280fd5b50346101a55760206003193601126101a557604061025261024d6121bf565b612664565b82519182526020820152f35b50346101a55760206003193601126101a5576102786121bf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0080549160ff8360401c16159067ffffffffffffffff80851694851580610531575b60018097149081610527575b15908161051e575b506104805767ffffffffffffffff199084878383161787556104ff575b5084549260ff8460401c16159383811688811591826104f7575b1490816104ed575b1590816104e4575b506104805784888483161788556104c5575b50855460ff8160401c161593811688811591826104bd575b1490816104b3575b1590816104aa575b506104805783886001600160a01b03948316178855610461575b501673ffffffffffffffffffffffffffffffffffffffff19875416178655610422575b6103e3575b61039961303b565b6103a1578280f35b7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2918168ff00000000000000001960209354169055604051908152a138808280f35b68ff00000000000000001982541682557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26020604051858152a1610391565b68ff00000000000000001983541683557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26020604051868152a161038c565b68ffffffffffffffffff19166801000000000000000117865538610369565b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b9050153861034f565b303b159150610347565b86925061033f565b68ffffffffffffffffff19166801000000000000000117865538610327565b90501538610315565b303b15915061030d565b879250610305565b68ffffffffffffffffff191668010000000000000001178555386102eb565b905015386102ce565b303b1591506102c6565b50836102ba565b50346101a557806003193601126101a55760206001600160a01b0360025416604051908152f35b50346101a557602090816003193601126101a55761057b6121bf565b9060ff815460a01c16610785573381526007835263ffffffff91826040832054161561075b576001600160a01b0391828216938482526007865260408220541661073157338152600994600981526005604083205410156107075784825260088152836040832054166106465784825260088152604082203373ffffffffffffffffffffffffffffffffffffffff19825416179055815b33835286825260408320805482101561067057856106318389936121d5565b90549060031b1c161461064657600101610612565b60046040517fbe592acd000000000000000000000000000000000000000000000000000000008152fd5b838784873384526009825260408420805490680100000000000000008210156106f357916106c8827e861aca61348df7264e2219d6502d9be3b0faea03b4649e08aa285403ef644e959460016106e7950181556121d5565b9091906001600160a01b038084549260031b9316831b921b1916179055565b6040519283523392a280f35b602486634e487b7160e01b81526041600452fd5b60046040517fb7337cab000000000000000000000000000000000000000000000000000000008152fd5b60046040517f11ac60b6000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3e3a049f000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8f85eea2000000000000000000000000000000000000000000000000000000008152fd5b50346101a55760406003193601126101a5576004356006811015610aa3576024356001600160a01b03808216809203610a9e573384526020906007825263ffffffff908160408720541661073157338652600883528060408720541680610a8d575b506005851015610a6357338414610a395761082a612607565b82421690818152602485848a5416604051928380927fc4c379cd000000000000000000000000000000000000000000000000000000008252601960048301525afa908115610a2e578991610a01575b506080820161ffff8092168152606083016108948a8261263f565b61092660c08501978a895260408601968752338d52600b8a5260408d2054806109e9575b5060078a5260408d2086518154888d015167ffffffff00000000199285169290921667ffffffffffffffff199091161790831660201b67ffffffff0000000016178155965187546bffffffff00000000000000001916911660401b6bffffffff000000000000000016178655565b5160068110156109d55760a092859492610945610975936002986123c6565b5185546effff000000000000000000000000001916911660681b6effff0000000000000000000000000016178455565b015160018201550191511673ffffffffffffffffffffffffffffffffffffffff198254161790556109a960405180946122aa565b8201527f129d52cd18b556e6c499d4f4d5633e26b4efe19850879f849e20cd70b795770160403392a280f35b60248b634e487b7160e01b81526021600452fd5b60a0870152338d52600b8a528c6040812055386108b8565b610a219150863d8811610a27575b610a19818361226b565b81019061264b565b38610879565b503d610a0f565b6040513d8b823e3d90fd5b60046040517f3d5062bb000000000000000000000000000000000000000000000000000000008152fd5b60046040517f937fc9f7000000000000000000000000000000000000000000000000000000008152fd5b610a98903390612d78565b38610811565b600080fd5b5080fd5b50346101a557806003193601126101a55760206001600160a01b0360065416604051908152f35b50346101a557806003193601126101a557604051610aeb81612233565b6005815260207f352e302e30000000000000000000000000000000000000000000000000000000602083015260405192839160208352835193846020850152825b858110610b4e57505050601f83601f1992604080968601015201168101030190f35b818101830151878201604001528694508201610b2c565b50346101a557602080600319360112610aa3576024610b9f91610b866121bf565b610b8e612607565b506001600160a01b03938491613012565b169081855260078152604085209460405194610bba86612203565b8187549161ffff63ffffffff938481168a528481881c16878b0152848160401c1660408b0152610bf360ff8260601c1660608c0161263f565b60681c16608089015280600260019a600181015460a08c015201541660c089015260125416604051968780927f0659b1880000000000000000000000000000000000000000000000000000000082528860048301525afa948515610d98578295610cbf575b506040969496939291935195610c7a61012091828901958952848901906122cd565b6101008701528651809352816101408701970193905b838210610c9d5786880387f35b8451805189528301518116838901526040909701969382019390850190610c90565b9094503d8083833e610cd1818361226b565b810190838183031261022a57805167ffffffffffffffff91828211610d9457019082601f83011215610226578151908111610d805760405192610d19868360051b018561226b565b818452858085019260061b84010192818411610d7c578601915b838310610d4557505050509338610c58565b604083830312610d7c57866040918251610d5e81612233565b85518152610d6d8387016123a8565b83820152815201920191610d33565b8580fd5b602484634e487b7160e01b81526041600452fd5b8480fd5b6040513d84823e3d90fd5b50346101a55760406003193601126101a557610dbd6121bf565b6040516024803592610280830167ffffffffffffffff811184821017610f0a576040526102803684376001600160a01b0381168552600960205260408520548590855b601487018711610ef75760148701811015610eeb5781811015610e77576001600160a01b03841688526009602052610e3b8160408a206121d5565b9054906014831015610e645760031b1c6001600160a01b0316600582901b870152600101610e00565b868a634e487b7160e01b81526032600452fd5b509250949390505b601481018111610ed75760140110610ece575b60405191825b60148210610eaf576102a084861515610280820152f35b6020806001926001600160a01b03865116815201930191019091610e98565b60019250610e92565b602484634e487b7160e01b81526011600452fd5b50925094939050610e7f565b8488634e487b7160e01b81526011600452fd5b634e487b7160e01b600052604160045260246000fd5b50346101a557806003193601126101a5576001600160a01b038154163303610f4a576101a261303b565b60046040517fe1010280000000000000000000000000000000000000000000000000000000008152fd5b50346101a55760206003193601126101a5576020610f98610f936121bf565b613012565b6001600160a01b0360405191168152f35b50346101a55760206003193601126101a55760406020916001600160a01b03610fd06121bf565b168152600b83522054604051908152f35b50346101a55760206003193601126101a5576101009061109d61102060406110076121bf565b61100f612607565b506001600160a01b03928391613012565b16938481526007602052209060026040519261103b84612203565b61ffff815463ffffffff8082168752808260201c1660208801528160401c16604087015261107260ff8260601c166060880161263f565b60681c166080850152600181015460a085015201541660c082015260405192835260208301906122cd565bf35b50346101a557806003193601126101a5576001600160a01b037f000000000000000000000000567906b97df10a5313feb654a682826928cc781d16300361110a5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b60046040517fe07c8dba000000000000000000000000000000000000000000000000000000008152fd5b5060406003193601126101a5576111496121bf565b602491823567ffffffffffffffff8111610aa35736602382011215610aa3578060040135926111778461228e565b611184604051918261226b565b8481526020948582019336888383010111610d7c57818692898993018737830101526001600160a01b03807f000000000000000000000000567906b97df10a5313feb654a682826928cc781d1680301490811561145c575b5061110a57868682875416604051928380927fd91078070000000000000000000000000000000000000000000000000000000082528a60048301525afa908115611451579082918791611419575b5016330361140857821694604051907f52d1902d00000000000000000000000000000000000000000000000000000000825280826004818a5afa91829187936113d8575b50506112a4578686604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b8590877f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc918281036113aa5750843b1561137b5750805473ffffffffffffffffffffffffffffffffffffffff1916821790556040518592917fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a281511561134557506113419382915190845af461133b613a97565b91613adf565b5080f35b935050505034611353575080f35b807fb398979f0000000000000000000000000000000000000000000000000000000060049252fd5b82604051907f4c9c8ce30000000000000000000000000000000000000000000000000000000082526004820152fd5b604051907faa1d49a40000000000000000000000000000000000000000000000000000000082526004820152fd5b9080929350813d8311611401575b6113f0818361226b565b81010312610d7c575190388061126e565b503d6113e6565b6004604051637bd5018b60e11b8152fd5b809250888092503d831161144a575b611432818361226b565b81010312610d7c57611444829161235e565b3861122a565b503d611428565b6040513d88823e3d90fd5b9050817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54161415386111dc565b50346101a55760406003193601126101a5576114a46121bf565b6001600160a01b0390818354166040519263bcaa0c5560e01b808552600c60048601526020948581602481875afa801561163f5783918891611603575b501680156115eb5733036115da5784906024604051809581938252600c60048301525afa80156115cf5784928691611597575b50604490868360405196879586947faad3ec960000000000000000000000000000000000000000000000000000000086521660048501526024356024850152165af1801561158c57611564578280f35b813d8311611585575b611577818361226b565b81010312610a9e5738808280f35b503d61156d565b6040513d85823e3d90fd5b83819492503d83116115c8575b6115ae818361226b565b81010312610d945760446115c2859361235e565b90611514565b503d6115a4565b6040513d87823e3d90fd5b60046040516306923c2160e21b8152fd5b602460405163092bc2cd60e41b8152600c6004820152fd5b809250878092503d8311611638575b61161c818361226b565b810103126116345761162e839161235e565b386114e1565b8680fd5b503d611612565b6040513d89823e3d90fd5b50346101a557806003193601126101a55760ff815460a01c166107855761167033613012565b9063ffffffff60406001600160a01b038416928381526007602052205416156116a657602061169e83612f2d565b604051908152f35b602490604051907f96e543170000000000000000000000000000000000000000000000000000000082526004820152fd5b50346101a557806003193601126101a5576001600160a01b036020915416604051908152f35b50346101a55760206003193601126101a5576101a261171a6121bf565b61243f565b50346101a557806003193601126101a55760206001600160a01b0360035416604051908152f35b50346101a55760406003193601126101a5576117606121bf565b602435916001600160a01b0380921681526009602052604081209081548410156101a55750602092611791916121d5565b9190546040519260031b1c168152f35b50346101a557806003193601126101a55760206001600160a01b0360055416604051908152f35b50346101a5576101006003193601126101a5576117e36121bf565b60e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610aa3576001600160a01b039081835416906040519063bcaa0c5560e01b918281526007600482015260209260249484838781845afa928315611a8e578893611ad5575b506040518281526008600482015285818881855afa928315610a2e5786918a94611a99575b5087604051809481938252601f60048301525afa928315611a8e5787918994611a50575b50163381141580611a44575b80611a38575b6119cc5750505090836007921685525260408320903563ffffffff90818116809103610a9e5763ffffffff198354161782556044358181168103610a9e57825467ffffffff00000000191660209190911b67ffffffff00000000161782556064359081168103610a9e5781546bffffffff0000000000000000191660409190911b6bffffffff00000000000000001617815560843560068110156102265761195090826123c6565b60a4359061ffff821682036102265761199660029282906effff00000000000000000000000000196effff0000000000000000000000000083549260681b169116179055565b60c4356001820155019060e435908116809103610a9e5773ffffffffffffffffffffffffffffffffffffffff1982541617905580f35b918691869315611a2157821615611a0a5716156119f45760046040516306923c2160e21b8152fd5b60405163092bc2cd60e41b8152601f6004820152fd5b8260405163092bc2cd60e41b815260086004820152fd5b8360405163092bc2cd60e41b815260076004820152fd5b503387841614156118a8565b503387831614156118a2565b86809295508193503d8311611a87575b611a6a818361226b565b81010312611a8357611a7c879161235e565b9238611896565b8780fd5b503d611a60565b6040513d8a823e3d90fd5b8281939295503d8311611ace575b611ab1818361226b565b81010312611aca57611ac3869161235e565b9238611872565b8880fd5b503d611aa7565b9092508481813d8311611b05575b611aed818361226b565b81010312611a8357611afe9061235e565b913861184d565b503d611ae3565b50346101a55760206003193601126101a55760209060406001600160a01b039182611b356121bf565b16815260088452205416604051908152f35b50346101a55760406003193601126101a557611b616121bf565b602435906001600160a01b03808454169060405180926318a2299b60e21b82526014600483015281602460209586935afa80156114515782918791611c8b575b50163303611408578315611c61576013548411611c37577fdefb2b4d25401a6c19a6dc5d6b883d2432f7dd04b16b5089efb5053e9099860c9216928385526007825263ffffffff6040862054161515600014611c1d578385526007825260016040862001611c108282546123b9565b90555b604051908152a280f35b600b825260408520611c308282546123b9565b9055611c13565b60046040517f7ee36909000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3908655a000000000000000000000000000000000000000000000000000000008152fd5b809250848092503d8311611cbc575b611ca4818361226b565b81010312610d7c57611cb6829161235e565b38611ba1565b503d611c9a565b50346101a557602090816003193601126101a557611cdf6121bf565b906001600160a01b039182825416926040519463bcaa0c5560e01b8087526002600488015260249582888881845afa9081156114515787988798969792611ff6575b508390604051998a8092868252600960048301525afa97881561163f578798611fbe575b508316963388141580611fb2575b611f655750859650604051947f5c12cd4b0000000000000000000000000000000000000000000000000000000086528381169586600482015261010080828881305afa908115610a2e578991611e9a575b5063ffffffff91505116611db6578680f35b611dbf90612f2d565b5081838754169185604051809481938252600260048301525afa9081156114515783928792611e62575b5050163314611dfa575b8080808680f35b6014541690813b15611e5d57839260405194859384927fcd67953200000000000000000000000000000000000000000000000000000000845260048401525af18015610d9857611e4e575b80808392611df3565b611e579061221f565b38611e45565b505050fd5b8193508092503d8311611e93575b611e7a818361226b565b81010312610d9457611e8c829161235e565b3880611de9565b503d611e70565b905081813d8311611f5e575b611eb0818361226b565b810103908112611aca57601f1960e091611ec98461235e565b500112611a8357604051611edc81612203565b611ee78583016123a8565b8152611ef5604083016123a8565b85820152611f05606083016123a8565b604082015260808201516006811015611f5a57606082015260a08201519161ffff83168303611f5a5760e0611f4f9163ffffffff94608085015260c081015160a08501520161235e565b60c082015238611da4565b8980fd5b503d611ea6565b9150508515611f9b571615611f855760046040516306923c2160e21b8152fd5b60405163092bc2cd60e41b815260096004820152fd5b8260405163092bc2cd60e41b815260026004820152fd5b50338482161415611d53565b9097508281813d8311611fef575b611fd6818361226b565b8101031261163457611fe8849161235e565b9790611d45565b503d611fcc565b84809697508193509193943d8311612031575b612013818361226b565b8101031261163457908361202a899695949361235e565b9190611d21565b503d612009565b50346101a55760206003193601126101a55763ffffffff60406020926001600160a01b036120646121bf565b168152600a8452205416604051908152f35b50346101a55760206003193601126101a5576120906121bf565b60ff825460a01c1661078557338252600760205263ffffffff6040832054161561075b576101a29033612d78565b905034610aa3576040600319360112610aa3576120d96121bf565b602435916001600160a01b0381818654166318a2299b60e21b82526014600483015281602460209586935afa80156114515782918791612187575b50163303611408577fd7ca3fb901656d5fcc7ae06d950d190a8dedb0bee0cd205d699ef5ecb4ae83749216928385526007825263ffffffff6040862054161515600014612174578385526007825260016040862001611c10828254612385565b600b825260408520611c30828254612385565b809250848092503d83116121b8575b6121a0818361226b565b81010312610d7c576121b2829161235e565b38612114565b503d612196565b600435906001600160a01b0382168203610a9e57565b80548210156121ed5760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b60e0810190811067ffffffffffffffff821117610f0a57604052565b67ffffffffffffffff8111610f0a57604052565b6040810190811067ffffffffffffffff821117610f0a57604052565b60a0810190811067ffffffffffffffff821117610f0a57604052565b90601f601f19910116810190811067ffffffffffffffff821117610f0a57604052565b67ffffffffffffffff8111610f0a57601f01601f191660200190565b9060068210156122b75752565b634e487b7160e01b600052602160045260246000fd5b6001600160a01b0360c0809263ffffffff808251168652806020830151166020870152604082015116604086015261230d606082015160608701906122aa565b61ffff608082015116608086015260a081015160a0860152015116910152565b9181601f84011215610a9e5782359167ffffffffffffffff8311610a9e576020808501948460051b010111610a9e57565b51906001600160a01b0382168203610a9e57565b9190602083019260158210156122b75752565b9190820391821161239257565b634e487b7160e01b600052601160045260246000fd5b519063ffffffff82168203610a9e57565b9190820180921161239257565b9060068110156122b7577fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff6cff00000000000000000000000083549260601b169116179055565b356001600160a01b0381168103610a9e5790565b9060058110156121ed5760051b0190565b5160158110156122b75790565b604090815161244d8161224f565b6006815260209060078282015260088482015260096060820152600a90600a608082015260005b6005811061248c5760048651637bd5018b60e11b8152fd5b6001600160a01b036124ca8582600054166124af6124aa8688612421565b612432565b908a5180809581946318a2299b60e21b835260048301612372565b03915afa90811561256857908291600091612531575b501633146124f15750600101612474565b91505061250a9394929416938460005252600020612586565b7faf42be50f1f5f8787a4bbed666dd5a7a36611d2e60a55e6db3f81dc56c9a5711600080a2565b91508682813d8311612561575b612548818361226b565b810103126101a5575061255b829161235e565b386124e0565b503d61253e565b88513d6000823e3d90fd5b8181029291811591840414171561239257565b6000808255600180920191825492828155836125a3575b50505050565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841684036125f3578252602082209260011b8301925b8381101561259d578083600292558383820155016125da565b602483634e487b7160e01b81526011600452fd5b6040519061261482612203565b8160c06000918281528260208201528260408201528260608201528260808201528260a08201520152565b60068210156122b75752565b90816020910312610a9e57518060010b8103610a9e5790565b906001600160a01b03809281600d5416906040519283917fdf0fbb65000000000000000000000000000000000000000000000000000000008352169182600483015281602460209586935afa801561273b578391600091612747575b50600a90049460115416916024604051809481937f679c93b900000000000000000000000000000000000000000000000000000000835260048301525afa91821561273b5760009261271157505090565b90809250813d8311612734575b612728818361226b565b81010312610a9e575190565b503d61271e565b6040513d6000823e3d90fd5b82819392503d8311612770575b61275e818361226b565b81010312610a9e57518290600a6126c0565b503d612754565b939290919260409384519261278b8461224f565b6001948585526020906002828701526003888701526004988960608801526005966005608082015260005b8881106127cc578b8b51637bd5018b60e11b8152fd5b8b612807868d6001600160a01b03938460005416906127ee6124aa888a612421565b92518095819482936318a2299b60e21b84528301612372565b03915afa908115612af557908291600091612abe575b5016331461282d575089016127b6565b95979b92939496999a985050508215612a9757858303612a705761ffff60065460a01c168311612a495733600052600a9586865263ffffffff80896000205416612a21573360005287875261288489600020612586565b8a60005b8681106129f6575050336000528787528860002090421663ffffffff1982541617905560005b8481106128c2575050505050505050505050565b856128d66128d1838888612b00565b61240d565b16600052600c80885260ff8a60002054166129ce57336000528888528a8c8b60002001612912848661290c6128d1838d8d612b00565b94612b00565b3590898d519361292185612233565b1683528a8301918252805490680100000000000000008210156129b9578f8201815561294d9190612b10565b9290926129a55751825473ffffffffffffffffffffffffffffffffffffffff1916908a1617825551908d01558b91908761298b6128d1848a8a612b00565b166000528852896000208260ff19825416179055016128ae565b600087634e487b7160e01b82525260246000fd5b604188634e487b7160e01b6000525260246000fd5b838a517f256fe3c7000000000000000000000000000000000000000000000000000000008152fd5b87612a056128d1838a8a612b00565b16600052600c89528a60002060ff198154169055018b90612888565b8289517f33cfa0ab000000000000000000000000000000000000000000000000000000008152fd5b86517f2c72acee000000000000000000000000000000000000000000000000000000008152fd5b86517f46d3a1b6000000000000000000000000000000000000000000000000000000008152fd5b86517f6dcdca7a000000000000000000000000000000000000000000000000000000008152fd5b91508782813d8311612aee575b612ad5818361226b565b810103126101a55750612ae8829161235e565b3861281d565b503d612acb565b8d513d6000823e3d90fd5b91908110156121ed5760051b0190565b80548210156121ed5760005260206000209060011b0190600090565b90604090815190612b3c8261224f565b600682526007602060078185015260088585015260096060850152600a9081608086015260005b60058110612b7b5760048751637bd5018b60e11b8152fd5b6001600160a01b038060005416612b956124aa848a612421565b90848a5180926318a2299b60e21b82528180612bb5600497888301612372565b03915afa908115612d6d57908391600091612d36575b50163314612bdd575050600101612b63565b92939496509788915096949616948560005283835263ffffffff918286600020541615612d0f575085600052838352600191828660002001549160005b838110612c6157505050505083612c3a9495965060005252600020612586565b7f2bd78aa6599e4965a32ba17ee89f34e7dfc8df640c4984ca2875c7905d0f4dbf600080a2565b80897fdefb2b4d25401a6c19a6dc5d6b883d2432f7dd04b16b5089efb5053e9099860c88888f8196828f8f612cb89489600052818852612ca685858560002001612b10565b50541698600052865260002001612b10565b50015460008481528783528d902054881615612cf55783600052868252858d60002001612ce68282546123b9565b90555b8c51908152a201612c1a565b600b82528c600020612d088282546123b9565b9055612ce9565b85517f3908655a000000000000000000000000000000000000000000000000000000008152fd5b91508582813d8311612d66575b612d4d818361226b565b810103126101a55750612d60839161235e565b38612bcb565b503d612d43565b8a513d6000823e3d90fd5b916001600160a01b0380921691600092808452602090600882528260409687872073ffffffffffffffffffffffffffffffffffffffff1981541690551692838652600980845287872054908792885b8a848210612e34575b505050505090919293945015612e0b577ff5558e1d420cd51517634d283f7c62af1bdc4bda1ad3ddf9ca08ebe983b4723f92939451908152a2565b600485517fa7c04edf000000000000000000000000000000000000000000000000000000008152fd5b86612e488385938e8d8152888d52206121d5565b929054600393841b1c1614612e605750600101612dc7565b9450878a949293969798999a528588528a8420917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92838201918211612f195784612eb2612ec894936106c8936121d5565b905490891b1c16918b8752888b528d87206121d5565b6001948884528752898320928354908115612f0557500192612eea84846121d5565b81939154921b1b19169055559084939291388080808a612dd0565b80634e487b7160e01b602492526031600452fd5b602486634e487b7160e01b81526011600452fd5b906001600160a01b03612f5d612f56670de0b6b3a7640000612f4e86612664565b819391612573565b04906123b9565b9216612fe560008281526007602052604062015180612f9263ffffffff97612f8c8985872054861c1642612385565b90612573565b04958483526007602052600182842001612fad8882546123b9565b9055848352600760205242169120906bffffffff0000000000000000196bffffffff000000000000000083549260401b169116179055565b7f121c5042302bae5fc561fbc64368f297ca60a880878e1e3a7f7e9380377260bf602084604051908152a2565b906001600160a01b038083166000526008602052604060002054169182156130375750565b9150565b60006001600160a01b038082541690604090815163bcaa0c5560e01b9384825260049460028684015260209260249284828581845afa9182156138b8578992613a60575b508573ffffffffffffffffffffffffffffffffffffffff19921682600d541617600d55865183815260058982015285818681855afa9081156135c3579087918b91613a28575b501682600e541617600e55865183815260148982015285818681855afa9081156135c3579087918b916139f0575b5016826010541617601055865183815260158982015285818681855afa9081156135c3579087918b916139b8575b501682600f541617600f558651838152858982015285818681855afa9081156135c3579087918b91613980575b5016826011541617601155865183815260078982015285818681855afa9081156135c3579087918b91613948575b501682601254161760125586517fc4c379cd000000000000000000000000000000000000000000000000000000008152601c8982015285818681855afa9081156135c3578a9161392b575b507fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000006006549260a01b1691161760065586517f42ab14dd000000000000000000000000000000000000000000000000000000008152602f8982015285818681855afa9081156135c3578a916138fe575b506013558651838152601f8982015285818681855afa9081156135c3579187879287948d916138c2575b5016846014541617601455885192838092878252600f8d8301525afa9081156138b8578991613883575b5060015486821690838882168303613771575b5050505084885416865183815260108982015285818681855afa9081156135c3578a9161373c575b5086600254911690878116820361363f575b50505084885416865183815260128982015285818681855afa9081156135c3578a9161360a575b50856013868b8b5195869384928a84528301525afa9182156135c357918a9391879385926135cd575b50886005549116908981168203613538575b50508760065491169188821683036134b4575b505050908590541691838751809481938252600c8b8301525afa9081156134aa5790839291889161346f575b5090818560019316613460575b50875496865195869384927ff2aac76c00000000000000000000000000000000000000000000000000000000845283015288165afa92831561345757509174ff0000000000000000000000000000000000000000917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff93869261342a575b5050151560a01b169116179055565b6134499250803d10613450575b613441818361226b565b810190613ac7565b388061341b565b503d613437565b513d86823e3d90fd5b61346990613b72565b3861339d565b8381939492503d83116134a3575b613487818361226b565b8101031261163457829161349c60019261235e565b9091613390565b503d61347d565b85513d89823e3d90fd5b82911617600655848851809481937f1a33757d00000000000000000000000000000000000000000000000000000000835260028d8401525af1801561352e57908491613505575b9081808a93613364565b813d8311613527575b613518818361226b565b81010312610a9e5782386134fb565b503d61350e565b86513d8a823e3d90fd5b8395508193919294501617600555856002868b8d8c5195869485937f1a33757d0000000000000000000000000000000000000000000000000000000085528401525af180156135c357918a93918793613592575b80613351565b9092809294503d83116135bc575b6135aa818361226b565b81010312610a9e57889185913861358c565b503d6135a0565b88513d8c823e3d90fd5b92945092905081813d8311613603575b6135e7818361226b565b81010312611f5a579185916135fc8b9461235e565b903861333f565b503d6135dd565b90508581813d8311613638575b613621818361226b565b81010312611f5a576136329061235e565b38613316565b503d613617565b8184821617600255866011878c8c5196879384928b84528301525afa92831561373257908b9182946136f4575b50888b54161561367d575b506132ef565b161790813b15611f5a57908987868b94838c5195869485937f36b91f2b0000000000000000000000000000000000000000000000000000000085521680988401525af180156135c357908a916136e0575b50508188541617875538808981613677565b6136e99061221f565b611aca5788386136ce565b88809295508193503d831161372b575b61370e818361226b565b81010312613727576137208b9161235e565b923861366c565b8a80fd5b503d613704565b89513d8d823e3d90fd5b90508581813d831161376a575b613753818361226b565b81010312611f5a576137649061235e565b386132dd565b503d613749565b16811760015587517ffe9fbb80000000000000000000000000000000000000000000000000000000008152308a82015286818781855afa908115613732578b91613866575b506137c3575b80836132b5565b803b15611f5a578980918a8a51809481937f4e606c470000000000000000000000000000000000000000000000000000000083525af180156135c357613853575b50888091885182888201917ff098767a0000000000000000000000000000000000000000000000000000000083528c815261383e81612233565b51925af15061384b613a97565b5038806137bc565b61385f9099919961221f565b9738613804565b61387d9150873d891161345057613441818361226b565b386137b6565b90508481813d83116138b1575b61389a818361226b565b81010312611aca576138ab9061235e565b386132a2565b503d613890565b87513d8b823e3d90fd5b93929450505081813d83116138f7575b6138dc818361226b565b81010312611f5a578491876138f1889361235e565b38613278565b503d6138d2565b90508581813d8311613924575b613915818361226b565b81010312611f5a57513861324e565b503d61390b565b6139429150863d8811610a2757610a19818361226b565b386131c7565b809250878092503d8311613979575b613961818361226b565b81010312611f5a57613973879161235e565b3861317c565b503d613957565b809250878092503d83116139b1575b613999818361226b565b81010312611f5a576139ab879161235e565b3861314e565b503d61398f565b809250878092503d83116139e9575b6139d1818361226b565b81010312611f5a576139e3879161235e565b38613121565b503d6139c7565b809250878092503d8311613a21575b613a09818361226b565b81010312611f5a57613a1b879161235e565b386130f3565b503d6139ff565b809250878092503d8311613a59575b613a41818361226b565b81010312611f5a57613a53879161235e565b386130c5565b503d613a37565b9091508481813d8311613a90575b613a78818361226b565b81010312611aca57613a899061235e565b903861307f565b503d613a6e565b3d15613ac2573d90613aa88261228e565b91613ab6604051938461226b565b82523d6000602084013e565b606090565b90816020910312610a9e57518015158103610a9e5790565b90613b1e5750805115613af457805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580613b69575b613b2f575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15613b27565b6001600160a01b03809116908115613c8557806001541615613c8157600354811680613c205750308203613bc3575b505b73ffffffffffffffffffffffffffffffffffffffff196003541617600355565b60015416803b15610a9e57600080916024604051809481937feb8646980000000000000000000000000000000000000000000000000000000083528760048401525af1801561273b5715613ba157613c1a9061221f565b38613ba1565b8091503b15610a9e57600080916024604051809481937f3a74bfd70000000000000000000000000000000000000000000000000000000083528760048401525af1801561273b57613c72575b50613ba3565b613c7b9061221f565b38613c6c565b5050565b60046040517f05d8ce3d000000000000000000000000000000000000000000000000000000008152fdfea264697066735822122059dee25398ad78919513b18a6c0cbd1e752cb075de67f27580cdfa8107f8148a64736f6c63430008190033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.