Overview
S Balance
0 S
S Value
-More Info
Private Name Tags
ContractCreator
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 Source Code Verified (Exact Match)
Contract Name:
Shop
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 9999999 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {Treasury} from "./Treasury.sol"; import {IBrushToken} from "./interfaces/external/IBrushToken.sol"; import {IItemNFT} from "./interfaces/IItemNFT.sol"; // The contract allows items to be bought/sold contract Shop is UUPSUpgradeable, OwnableUpgradeable { event AddShopItems(ShopItem[] shopItems); event EditShopItems(ShopItem[] shopItems); event RemoveShopItems(uint16[] tokenIds); event Buy(address buyer, address to, uint256 tokenId, uint256 quantity, uint256 price); event BuyBatch(address buyer, address to, uint256[] tokenIds, uint256[] quantities, uint256[] prices); event Sell(address seller, uint256 tokenId, uint256 quantity, uint256 price); event SellBatch(address seller, uint256[] tokenIds, uint256[] quantities, uint256[] prices); event NewAllocation(uint16 tokenId, uint256 allocation); event AddUnsellableItems(uint16[] tokenIds); event RemoveUnsellableItems(uint16[] tokenIds); event SetMinItemQuantityBeforeSellsAllowed(uint256 minItemQuantityBeforeSellsAllowed); event SetBrushDistributionPercentages( uint256 brushBurntPercentage, uint256 brushTreasuryPercentage, uint256 brushDevPercentage ); event SetSellingCutoffDuration(uint256 duration); error LengthMismatch(); error LengthEmpty(); error ItemCannotBeBought(); error ItemDoesNotExist(); error ShopItemDoesNotExist(); error ShopItemAlreadyExists(); error PriceCannotBeZero(); error NotEnoughBrush(uint256 brushNeeded, uint256 brushAvailable); error MinExpectedBrushNotReached(uint256 totalBrush, uint256 minExpectedBrush); error LiquidatePriceIsHigherThanShop(uint256 tokenId); error SellingTooQuicklyAfterItemIntroduction(); error NotEnoughAllocationRemaining(uint256 tokenId, uint256 totalSold, uint256 allocationRemaining); error AlreadyUnsellable(); error AlreadySellable(); error ItemNotSellable(uint256 tokenId); error PercentNotTotal100(); struct ShopItem { uint16 tokenId; uint128 price; } struct TokenInfo { uint80 allocationRemaining; uint80 price; uint40 checkpointTimestamp; // 00:00 UTC bool unsellable; } mapping(uint256 tokenId => TokenInfo tokenInfo) private _tokenInfos; IBrushToken private _brush; Treasury private _treasury; IItemNFT private _itemNFT; uint16 private _numUnsellableItems; uint24 private _minItemQuantityBeforeSellsAllowed; address private _dev; uint8 private _brushBurntPercentage; uint8 private _brushTreasuryPercentage; uint8 private _brushDevPercentage; uint24 private _sellingCutoffDuration; mapping(uint256 itemId => uint256 price) private _shopItems; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize( IBrushToken brush, Treasury treasury, address dev, uint24 minItemQuantityBeforeSellsAllowed, uint24 sellingCutoffDuration ) external initializer { __Ownable_init(_msgSender()); __UUPSUpgradeable_init(); _brush = brush; _treasury = treasury; _dev = dev; setMinItemQuantityBeforeSellsAllowed(minItemQuantityBeforeSellsAllowed); setSellingCutoffDuration(sellingCutoffDuration); } function getMinItemQuantityBeforeSellsAllowed() external view returns (uint24) { return _minItemQuantityBeforeSellsAllowed; } function liquidatePrice(uint16 tokenId) external view returns (uint256) { return _liquidatePrice(tokenId, _totalBrushForItem()); } function liquidatePrices(uint16[] calldata tokenIds) external view returns (uint256[] memory prices) { uint256 length = tokenIds.length; if (length != 0) { uint256 totalBrushForItem = _totalBrushForItem(); prices = new uint256[](length); for (uint256 i; i < length; ++i) { prices[i] = _liquidatePrice(tokenIds[i], totalBrushForItem); } } } // Buy simple items and XP boosts using brush function buy(address to, uint16 tokenId, uint256 quantity) external { uint256 price = _shopItems[tokenId]; require(price != 0, ItemCannotBeBought()); uint256 tokenCost = price * quantity; // Pay (address[] memory accounts, uint256[] memory amounts) = _buyDistribution(tokenCost); address sender = _msgSender(); _brush.transferFromBulk(sender, accounts, amounts); _itemNFT.mint(to, tokenId, quantity); emit Buy(sender, to, tokenId, quantity, price); } function buyBatch(address to, uint256[] calldata tokenIds, uint256[] calldata quantities) external { require(tokenIds.length != 0, LengthEmpty()); require(tokenIds.length == quantities.length, LengthMismatch()); uint256 tokenCost; uint256[] memory prices = new uint256[](tokenIds.length); for (uint256 i = 0; i < tokenIds.length; ++i) { uint256 price = _shopItems[uint16(tokenIds[i])]; require(price != 0, ItemCannotBeBought()); tokenCost += price * quantities[i]; prices[i] = price; } // Pay (address[] memory accounts, uint256[] memory amounts) = _buyDistribution(tokenCost); address sender = _msgSender(); _brush.transferFromBulk(sender, accounts, amounts); _itemNFT.mintBatch(to, tokenIds, quantities); emit BuyBatch(sender, to, tokenIds, quantities, prices); } function sell(uint16 tokenId, uint256 quantity, uint256 minExpectedBrush) external { uint256 price = _liquidatePrice(tokenId, _totalBrushForItem()); uint256 totalBrush = price * quantity; _sell(tokenId, quantity, price); require(totalBrush >= minExpectedBrush, MinExpectedBrushNotReached(totalBrush, minExpectedBrush)); address sender = _msgSender(); _treasury.spend(sender, totalBrush); _itemNFT.burn(sender, tokenId, quantity); emit Sell(sender, tokenId, quantity, price); } function sellBatch(uint256[] calldata tokenIds, uint256[] calldata quantities, uint256 minExpectedBrush) external { // check array lengths require(tokenIds.length != 0, LengthEmpty()); require(tokenIds.length == quantities.length, LengthMismatch()); uint256 totalBrush; uint256[] memory prices = new uint256[](tokenIds.length); uint256 totalBrushForItem = _totalBrushForItem(); for (uint256 i = 0; i < tokenIds.length; ++i) { uint256 sellPrice = _liquidatePrice(uint16(tokenIds[i]), totalBrushForItem); totalBrush += sellPrice * quantities[i]; prices[i] = sellPrice; _sell(tokenIds[i], quantities[i], prices[i]); } require(totalBrush >= minExpectedBrush, MinExpectedBrushNotReached(totalBrush, minExpectedBrush)); address sender = _msgSender(); _treasury.spend(sender, totalBrush); _itemNFT.burnBatch(sender, tokenIds, quantities); emit SellBatch(sender, tokenIds, quantities, prices); } // Does not burn! function _sell(uint256 tokenId, uint256 quantity, uint256 sellPrice) private { uint256 price = _shopItems[tokenId]; require(price == 0 || price >= sellPrice, LiquidatePriceIsHigherThanShop(tokenId)); // A period of no selling allowed for a newly minted item require( _itemNFT.getTimestampFirstMint(tokenId) + _sellingCutoffDuration <= block.timestamp, SellingTooQuicklyAfterItemIntroduction() ); // Check if tokenInfo checkpoint is older than 24 hours TokenInfo storage tokenInfo = _tokenInfos[tokenId]; require(!tokenInfo.unsellable, ItemNotSellable(tokenId)); uint256 allocationRemaining; if (_hasNewDailyData(tokenInfo.checkpointTimestamp)) { uint256 numItems = _getNumItems(); // New day, reset max allocation can be sold allocationRemaining = _treasury.totalClaimable(address(this)) / numItems; tokenInfo.checkpointTimestamp = uint40((block.timestamp / 1 days) * 1 days); tokenInfo.price = uint80(sellPrice); emit NewAllocation(uint16(tokenId), allocationRemaining); } else { allocationRemaining = tokenInfo.allocationRemaining; } uint256 totalSold = quantity * sellPrice; require(allocationRemaining >= totalSold, NotEnoughAllocationRemaining(tokenId, totalSold, allocationRemaining)); tokenInfo.allocationRemaining = uint80(allocationRemaining - totalSold); } function _liquidatePrice(uint16 tokenId, uint256 totalBrushPerItem) private view returns (uint80 price) { TokenInfo storage tokenInfo = _tokenInfos[tokenId]; uint256 totalOfThisItem = _itemNFT.totalSupply(tokenId); if (_hasNewDailyData(tokenInfo.checkpointTimestamp)) { if (totalOfThisItem != 0) { price = uint80(totalBrushPerItem / totalOfThisItem); } } else { price = uint80(tokenInfo.price); } if (totalOfThisItem < _minItemQuantityBeforeSellsAllowed || tokenInfo.unsellable) { // Needs to have a minimum of an item before any can be sold, and the item must be sellable price = 0; } } function _buyDistribution( uint256 tokenCost ) private view returns (address[] memory accounts, uint256[] memory amounts) { accounts = new address[](3); amounts = new uint256[](3); amounts[0] = (tokenCost * _brushBurntPercentage) / 100; amounts[1] = (tokenCost * _brushTreasuryPercentage) / 100; amounts[2] = (tokenCost * _brushDevPercentage) / 100; accounts[0] = address(0); accounts[1] = address(_treasury); accounts[2] = _dev; } function _getNumItems() private view returns (uint256) { uint256 totalSupply = _itemNFT.totalSupply(); uint16 numUnsellableItems = _numUnsellableItems; return (numUnsellableItems >= totalSupply) ? totalSupply : totalSupply - numUnsellableItems; } function _totalBrushForItem() private view returns (uint256) { return _treasury.totalClaimable(address(this)) / _getNumItems(); } function _addBuyableItem(ShopItem calldata buyableItem) private { // Check item exists require(_itemNFT.exists(buyableItem.tokenId), ItemDoesNotExist()); require(_shopItems[buyableItem.tokenId] == 0, ShopItemAlreadyExists()); require(buyableItem.price != 0, PriceCannotBeZero()); _shopItems[buyableItem.tokenId] = buyableItem.price; } function _hasNewDailyData(uint256 checkpointTimestamp) private view returns (bool) { return (block.timestamp / 1 days) * 1 days >= checkpointTimestamp + 1 days; } function tokenInfos(uint16 tokenId) external view returns (TokenInfo memory tokenInfo) { return _tokenInfos[tokenId]; } function shopItems(uint16 tokenId) external view returns (uint256 price) { return _shopItems[tokenId]; } function addBuyableItems(ShopItem[] calldata buyableItems) external onlyOwner { for (uint256 i; i < buyableItems.length; ++i) { _addBuyableItem(buyableItems[i]); } emit AddShopItems(buyableItems); } function editItems(ShopItem[] calldata itemsToEdit) external onlyOwner { for (uint256 i; i < itemsToEdit.length; ++i) { require(_shopItems[itemsToEdit[i].tokenId] != 0, ShopItemDoesNotExist()); _shopItems[itemsToEdit[i].tokenId] = itemsToEdit[i].price; } emit EditShopItems(itemsToEdit); } function removeItems(uint16[] calldata tokenIds) external onlyOwner { for (uint256 i; i < tokenIds.length; ++i) { uint16 tokenId = tokenIds[i]; require(_shopItems[tokenId] != 0, ShopItemDoesNotExist()); delete _shopItems[tokenId]; } emit RemoveShopItems(tokenIds); } function addUnsellableItems(uint16[] calldata itemTokenIds) external onlyOwner { for (uint256 i; i < itemTokenIds.length; ++i) { uint16 tokenId = itemTokenIds[i]; TokenInfo storage tokenInfo = _tokenInfos[tokenId]; require(!tokenInfo.unsellable, AlreadyUnsellable()); require(_itemNFT.exists(tokenId), ItemDoesNotExist()); tokenInfo.unsellable = true; } _numUnsellableItems += uint16(itemTokenIds.length); emit AddUnsellableItems(itemTokenIds); } function removeUnsellableItems(uint16[] calldata itemTokenIds) external onlyOwner { for (uint256 i; i < itemTokenIds.length; ++i) { TokenInfo storage tokenInfo = _tokenInfos[itemTokenIds[i]]; require(tokenInfo.unsellable, AlreadySellable()); tokenInfo.unsellable = false; } _numUnsellableItems -= uint16(itemTokenIds.length); emit RemoveUnsellableItems(itemTokenIds); } function setItemNFT(IItemNFT itemNFT) external onlyOwner { _itemNFT = itemNFT; } function setMinItemQuantityBeforeSellsAllowed(uint24 minItemQuantityBeforeSellsAllowed) public onlyOwner { _minItemQuantityBeforeSellsAllowed = minItemQuantityBeforeSellsAllowed; emit SetMinItemQuantityBeforeSellsAllowed(minItemQuantityBeforeSellsAllowed); } function setBrushDistributionPercentages( uint8 brushBurntPercentage, uint8 brushTreasuryPercentage, uint8 brushDevPercentage ) external onlyOwner { require(brushBurntPercentage + brushTreasuryPercentage + brushDevPercentage == 100, PercentNotTotal100()); _brushBurntPercentage = brushBurntPercentage; _brushTreasuryPercentage = brushTreasuryPercentage; _brushDevPercentage = brushDevPercentage; emit SetBrushDistributionPercentages(brushBurntPercentage, brushTreasuryPercentage, brushDevPercentage); } function setSellingCutoffDuration(uint24 duration) public onlyOwner { _sellingCutoffDuration = duration; emit SetSellingCutoffDuration(duration); } // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly ("memory-safe") { $.slot := OwnableStorageLocation } } /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { OwnableStorage storage $ = _getOwnableStorage(); return $._owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// 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 ("memory-safe") { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol) pragma solidity ^0.8.20; import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {Initializable} from "./Initializable.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 Initializable, 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 ERC-1967) 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 ERC-1167 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(); _; } function __UUPSUpgradeable_init() internal onlyInitializing { } function __UUPSUpgradeable_init_unchained() internal onlyInitializing { } /** * @dev Implementation of the ERC-1822 {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 ERC-1967 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 ERC-1967. * * 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.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } 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.1.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.20; /** * @dev ERC-1822: 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) (interfaces/IERC1967.sol) pragma solidity ^0.8.20; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. */ interface IERC1967 { /** * @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); }
// 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.1.0) (proxy/ERC1967/ERC1967Utils.sol) pragma solidity ^0.8.21; import {IBeacon} from "../beacon/IBeacon.sol"; import {IERC1967} from "../../interfaces/IERC1967.sol"; import {Address} from "../../utils/Address.sol"; import {StorageSlot} from "../../utils/StorageSlot.sol"; /** * @dev This library provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots. */ library ERC1967Utils { /** * @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 ERC-1967 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 IERC1967.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 ERC-1967) 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 ERC-1967 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 IERC1967.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 ERC-1967 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 IERC1967.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.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ 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 // OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @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 Errors.InsufficientBalance(address(this).balance, amount); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert Errors.FailedCall(); } } /** * @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 * {Errors.FailedCall} 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 Errors.InsufficientBalance(address(this).balance, value); } (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 {Errors.FailedCall}) 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 {Errors.FailedCall} 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 {Errors.FailedCall}. */ 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 assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.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 ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * 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; * } * } * ``` * * TIP: Consider using this library along with {SlotDerivation}. */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct Int256Slot { int256 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) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Int256Slot` with member `value` located at `slot`. */ function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { 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) { assembly ("memory-safe") { r.slot := store.slot } } /** * @dev Returns a `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { 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) { assembly ("memory-safe") { r.slot := store.slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableMap.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableMap.js. pragma solidity ^0.8.20; import {EnumerableSet} from "./EnumerableSet.sol"; /** * @dev Library for managing an enumerable variant of Solidity's * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] * type. * * Maps have the following properties: * * - Entries are added, removed, and checked for existence in constant time * (O(1)). * - Entries are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableMap for EnumerableMap.UintToAddressMap; * * // Declare a set state variable * EnumerableMap.UintToAddressMap private myMap; * } * ``` * * The following map types are supported: * * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0 * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0 * - `uint256 -> bytes32` (`UintToBytes32Map`) since v5.1.0 * - `address -> address` (`AddressToAddressMap`) since v5.1.0 * - `address -> bytes32` (`AddressToBytes32Map`) since v5.1.0 * - `bytes32 -> address` (`Bytes32ToAddressMap`) since v5.1.0 * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableMap. * ==== */ library EnumerableMap { using EnumerableSet for EnumerableSet.Bytes32Set; // To implement this library for multiple types with as little code repetition as possible, we write it in // terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions, // and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map. // This means that we can only create new EnumerableMaps for types that fit in bytes32. /** * @dev Query for a nonexistent map key. */ error EnumerableMapNonexistentKey(bytes32 key); struct Bytes32ToBytes32Map { // Storage of keys EnumerableSet.Bytes32Set _keys; mapping(bytes32 key => bytes32) _values; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) { map._values[key] = value; return map._keys.add(key); } /** * @dev Removes a key-value pair from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) { delete map._values[key]; return map._keys.remove(key); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) { return map._keys.contains(key); } /** * @dev Returns the number of key-value pairs in the map. O(1). */ function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) { return map._keys.length(); } /** * @dev Returns the key-value pair stored at position `index` in the map. O(1). * * Note that there are no guarantees on the ordering of entries inside the * array, and it may change when more entries are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32 key, bytes32 value) { bytes32 atKey = map._keys.at(index); return (atKey, map._values[atKey]); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool exists, bytes32 value) { bytes32 val = map._values[key]; if (val == bytes32(0)) { return (contains(map, key), bytes32(0)); } else { return (true, val); } } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) { bytes32 value = map._values[key]; if (value == 0 && !contains(map, key)) { revert EnumerableMapNonexistentKey(key); } return value; } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) { return map._keys.values(); } // UintToUintMap struct UintToUintMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) { return set(map._inner, bytes32(key), bytes32(value)); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToUintMap storage map, uint256 key) internal returns (bool) { return remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) { return contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToUintMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintToUintMap storage map, uint256 index) internal view returns (uint256 key, uint256 value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (uint256(atKey), uint256(val)); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool exists, uint256 value) { (bool success, bytes32 val) = tryGet(map._inner, bytes32(key)); return (success, uint256(val)); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) { return uint256(get(map._inner, bytes32(key))); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(UintToUintMap storage map) internal view returns (uint256[] memory) { bytes32[] memory store = keys(map._inner); uint256[] memory result; assembly ("memory-safe") { result := store } return result; } // UintToAddressMap struct UintToAddressMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) { return set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { return remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { return contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToAddressMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256 key, address value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (uint256(atKey), address(uint160(uint256(val)))); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool exists, address value) { (bool success, bytes32 val) = tryGet(map._inner, bytes32(key)); return (success, address(uint160(uint256(val)))); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { return address(uint160(uint256(get(map._inner, bytes32(key))))); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) { bytes32[] memory store = keys(map._inner); uint256[] memory result; assembly ("memory-safe") { result := store } return result; } // UintToBytes32Map struct UintToBytes32Map { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(UintToBytes32Map storage map, uint256 key, bytes32 value) internal returns (bool) { return set(map._inner, bytes32(key), value); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToBytes32Map storage map, uint256 key) internal returns (bool) { return remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToBytes32Map storage map, uint256 key) internal view returns (bool) { return contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToBytes32Map storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintToBytes32Map storage map, uint256 index) internal view returns (uint256 key, bytes32 value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (uint256(atKey), val); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(UintToBytes32Map storage map, uint256 key) internal view returns (bool exists, bytes32 value) { (bool success, bytes32 val) = tryGet(map._inner, bytes32(key)); return (success, val); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToBytes32Map storage map, uint256 key) internal view returns (bytes32) { return get(map._inner, bytes32(key)); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(UintToBytes32Map storage map) internal view returns (uint256[] memory) { bytes32[] memory store = keys(map._inner); uint256[] memory result; assembly ("memory-safe") { result := store } return result; } // AddressToUintMap struct AddressToUintMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) { return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value)); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(AddressToUintMap storage map, address key) internal returns (bool) { return remove(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(AddressToUintMap storage map, address key) internal view returns (bool) { return contains(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns the number of elements in the map. O(1). */ function length(AddressToUintMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressToUintMap storage map, uint256 index) internal view returns (address key, uint256 value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (address(uint160(uint256(atKey))), uint256(val)); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(AddressToUintMap storage map, address key) internal view returns (bool exists, uint256 value) { (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key)))); return (success, uint256(val)); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(AddressToUintMap storage map, address key) internal view returns (uint256) { return uint256(get(map._inner, bytes32(uint256(uint160(key))))); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(AddressToUintMap storage map) internal view returns (address[] memory) { bytes32[] memory store = keys(map._inner); address[] memory result; assembly ("memory-safe") { result := store } return result; } // AddressToAddressMap struct AddressToAddressMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) { return set(map._inner, bytes32(uint256(uint160(key))), bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(AddressToAddressMap storage map, address key) internal returns (bool) { return remove(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(AddressToAddressMap storage map, address key) internal view returns (bool) { return contains(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns the number of elements in the map. O(1). */ function length(AddressToAddressMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressToAddressMap storage map, uint256 index) internal view returns (address key, address value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (address(uint160(uint256(atKey))), address(uint160(uint256(val)))); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool exists, address value) { (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key)))); return (success, address(uint160(uint256(val)))); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(AddressToAddressMap storage map, address key) internal view returns (address) { return address(uint160(uint256(get(map._inner, bytes32(uint256(uint160(key))))))); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(AddressToAddressMap storage map) internal view returns (address[] memory) { bytes32[] memory store = keys(map._inner); address[] memory result; assembly ("memory-safe") { result := store } return result; } // AddressToBytes32Map struct AddressToBytes32Map { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(AddressToBytes32Map storage map, address key, bytes32 value) internal returns (bool) { return set(map._inner, bytes32(uint256(uint160(key))), value); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(AddressToBytes32Map storage map, address key) internal returns (bool) { return remove(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(AddressToBytes32Map storage map, address key) internal view returns (bool) { return contains(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns the number of elements in the map. O(1). */ function length(AddressToBytes32Map storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressToBytes32Map storage map, uint256 index) internal view returns (address key, bytes32 value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (address(uint160(uint256(atKey))), val); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(AddressToBytes32Map storage map, address key) internal view returns (bool exists, bytes32 value) { (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key)))); return (success, val); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) { return get(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(AddressToBytes32Map storage map) internal view returns (address[] memory) { bytes32[] memory store = keys(map._inner); address[] memory result; assembly ("memory-safe") { result := store } return result; } // Bytes32ToUintMap struct Bytes32ToUintMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) { return set(map._inner, key, bytes32(value)); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) { return remove(map._inner, key); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) { return contains(map._inner, key); } /** * @dev Returns the number of elements in the map. O(1). */ function length(Bytes32ToUintMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32 key, uint256 value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (atKey, uint256(val)); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool exists, uint256 value) { (bool success, bytes32 val) = tryGet(map._inner, key); return (success, uint256(val)); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) { return uint256(get(map._inner, key)); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) { bytes32[] memory store = keys(map._inner); bytes32[] memory result; assembly ("memory-safe") { result := store } return result; } // Bytes32ToAddressMap struct Bytes32ToAddressMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(Bytes32ToAddressMap storage map, bytes32 key, address value) internal returns (bool) { return set(map._inner, key, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(Bytes32ToAddressMap storage map, bytes32 key) internal returns (bool) { return remove(map._inner, key); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool) { return contains(map._inner, key); } /** * @dev Returns the number of elements in the map. O(1). */ function length(Bytes32ToAddressMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the map. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32ToAddressMap storage map, uint256 index) internal view returns (bytes32 key, address value) { (bytes32 atKey, bytes32 val) = at(map._inner, index); return (atKey, address(uint160(uint256(val)))); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool exists, address value) { (bool success, bytes32 val) = tryGet(map._inner, key); return (success, address(uint160(uint256(val)))); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (address) { return address(uint160(uint256(get(map._inner, key)))); } /** * @dev Return the an array containing all the keys * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. */ function keys(Bytes32ToAddressMap storage map) internal view returns (bytes32[] memory) { bytes32[] memory store = keys(map._inner); bytes32[] memory result; assembly ("memory-safe") { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.20; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position is the index of the value in the `values` array plus 1. // Position 0 is used to mean a value is not in the set. mapping(bytes32 value => uint256) _positions; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._positions[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We cache the value's position to prevent multiple reads from the same storage slot uint256 position = set._positions[value]; if (position != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 valueIndex = position - 1; uint256 lastIndex = set._values.length - 1; if (valueIndex != lastIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the lastValue to the index where the value to delete is set._values[valueIndex] = lastValue; // Update the tracked position of the lastValue (that was just moved) set._positions[lastValue] = position; } // Delete the slot where the moved value was stored set._values.pop(); // Delete the tracked position for the deleted slot delete set._positions[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._positions[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; assembly ("memory-safe") { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; assembly ("memory-safe") { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; assembly ("memory-safe") { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Skill, Attire, CombatStyle, CombatStats} from "./misc.sol"; import {GuaranteedReward, RandomReward} from "./rewards.sol"; enum ActionQueueStrategy { OVERWRITE, APPEND, KEEP_LAST_IN_PROGRESS } struct QueuedActionInput { Attire attire; uint16 actionId; uint16 regenerateId; // Food (combat), maybe something for non-combat later uint16 choiceId; // Melee/Ranged/Magic (combat), logs, ore (non-combat) uint16 rightHandEquipmentTokenId; // Axe/Sword/bow, can be empty uint16 leftHandEquipmentTokenId; // Shield, can be empty uint24 timespan; // How long to queue the action for uint8 combatStyle; // CombatStyle specific style of combat uint40 petId; // id of the pet (can be empty) } struct QueuedAction { uint16 actionId; uint16 regenerateId; // Food (combat), maybe something for non-combat later uint16 choiceId; // Melee/Ranged/Magic (combat), logs, ore (non-combat) uint16 rightHandEquipmentTokenId; // Axe/Sword/bow, can be empty uint16 leftHandEquipmentTokenId; // Shield, can be empty uint24 timespan; // How long to queue the action for uint24 prevProcessedTime; // How long the action has been processed for previously uint24 prevProcessedXPTime; // How much XP has been gained for this action so far uint64 queueId; // id of this queued action bytes1 packed; // 1st bit is isValid (not used yet), 2nd bit is for hasPet (decides if the 2nd storage slot is read) uint8 combatStyle; uint24 reserved; // Next storage slot uint40 petId; // id of the pet (can be empty) } // This is only used as an input arg (and events) struct ActionInput { uint16 actionId; ActionInfo info; GuaranteedReward[] guaranteedRewards; RandomReward[] randomRewards; CombatStats combatStats; } struct ActionInfo { uint8 skill; bool actionChoiceRequired; // If true, then the user must choose an action choice uint24 xpPerHour; uint32 minXP; uint24 numSpawned; // Mostly for combat, capped respawn rate for xp/drops. Per hour, base 10000 uint16 handItemTokenIdRangeMin; // Inclusive uint16 handItemTokenIdRangeMax; // Inclusive uint8 successPercent; // 0-100 uint8 worldLocation; // 0 is the main starting world bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; } uint16 constant ACTIONCHOICE_MELEE_BASIC_SWORD = 1500; uint16 constant ACTIONCHOICE_MAGIC_SHADOW_BLAST = 2000; uint16 constant ACTIONCHOICE_RANGED_BASIC_BOW = 3000; // Allows for 2, 4 or 8 hour respawn time uint256 constant SPAWN_MUL = 1000; uint256 constant RATE_MUL = 1000; uint256 constant GUAR_MUL = 10; // Guaranteeded reward multiplier (1 decimal, allows for 2 hour action times) uint256 constant MAX_QUEUEABLE_ACTIONS = 3; // Available slots to queue actions
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; enum BoostType { NONE, ANY_XP, COMBAT_XP, NON_COMBAT_XP, GATHERING, ABSENCE, PASSIVE_SKIP_CHANCE, // Clan wars PVP_BLOCK, PVP_REATTACK, PVP_SUPER_ATTACK, // Combat stats COMBAT_FIXED } struct Equipment { uint16 itemTokenId; uint24 amount; } enum Skill { NONE, COMBAT, // This is a helper which incorporates all combat skills, attack <-> magic, defence, health etc MELEE, RANGED, MAGIC, DEFENCE, HEALTH, RESERVED_COMBAT, MINING, WOODCUTTING, FISHING, SMITHING, THIEVING, CRAFTING, COOKING, FIREMAKING, FARMING, ALCHEMY, FLETCHING, FORGING, RESERVED2, RESERVED3, RESERVED4, RESERVED5, RESERVED6, RESERVED7, RESERVED8, RESERVED9, RESERVED10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15, RESERVED16, RESERVED17, RESERVED18, RESERVED19, RESERVED20, TRAVELING // Helper Skill for travelling } struct Attire { uint16 head; uint16 neck; uint16 body; uint16 arms; uint16 legs; uint16 feet; uint16 ring; uint16 reserved1; } struct CombatStats { // From skill points int16 meleeAttack; int16 magicAttack; int16 rangedAttack; int16 health; // These include equipment int16 meleeDefence; int16 magicDefence; int16 rangedDefence; } enum CombatStyle { NONE, ATTACK, DEFENCE }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {QueuedAction} from "./actions.sol"; import {Skill, BoostType, CombatStats, Equipment} from "./misc.sol"; import {PlayerQuest} from "./quests.sol"; // 4 bytes for each level. 0x00000000 is the first level, 0x00000054 is the second, etc. bytes constant XP_BYTES = hex"0000000000000054000000AE0000010E00000176000001E60000025E000002DE00000368000003FD0000049B00000546000005FC000006C000000792000008730000096400000A6600000B7B00000CA400000DE100000F36000010A200001229000013CB0000158B0000176B0000196E00001B9400001DE20000205A000022FF000025D5000028DD00002C1E00002F99000033540000375200003B9A000040300000451900004A5C00004FFF0000560900005C810000637000006ADD000072D100007B570000847900008E42000098BE0000A3F90000B0020000BCE70000CAB80000D9860000E9630000FA6200010C990001201D0001350600014B6F0001637300017D2E000198C10001B64E0001D5F80001F7E600021C430002433B00026CFD000299BE0002C9B30002FD180003342B00036F320003AE730003F23D00043AE3000488BE0004DC2F0005359B000595700005FC2400066A360006E02D00075E990007E6160008774C000912EB0009B9B4000A6C74000B2C06000BF956000CD561000DC134000EBDF3000FCCD40010EF2400122648001373BF0014D9230016582C0017F2B00019AAA9001B8234001D7B95001F99390021DDBC00244BE60026E6B60029B15F002CAF51002FE43A0033540D00370303003AF5A4003F30CC0043B9B0004895E3004DCB600053609100595C53005FC6030066A585006E034D0075E86C007E5E980087703B0091287D009B935300A6BD8F00B2B4EE00BF882800CD470500DC026F00EBCC8500FCB8B7010EDBD5"; uint256 constant MAX_LEVEL = 140; // Original max level uint256 constant MAX_LEVEL_1 = 160; // TODO: Update later uint256 constant MAX_LEVEL_2 = 190; // TODO: Update later enum EquipPosition { NONE, HEAD, NECK, BODY, ARMS, LEGS, FEET, RING, SPARE2, LEFT_HAND, RIGHT_HAND, BOTH_HANDS, QUIVER, MAGIC_BAG, FOOD, AUX, // wood, seeds etc.. BOOST_VIAL, EXTRA_BOOST_VIAL, GLOBAL_BOOST_VIAL, CLAN_BOOST_VIAL, PASSIVE_BOOST_VIAL, LOCKED_VAULT, TERRITORY } struct Player { uint40 currentActionStartTimestamp; // The in-progress start time of the first queued action Skill currentActionProcessedSkill1; // The skill that the queued action has already gained XP in uint24 currentActionProcessedXPGained1; // The amount of XP that the queued action has already gained Skill currentActionProcessedSkill2; uint24 currentActionProcessedXPGained2; Skill currentActionProcessedSkill3; uint24 currentActionProcessedXPGained3; uint16 currentActionProcessedFoodConsumed; uint16 currentActionProcessedBaseInputItemsConsumedNum; // e.g scrolls, crafting materials etc Skill skillBoosted1; // The first skill that is boosted Skill skillBoosted2; // The second skill that is boosted (if applicable) uint48 totalXP; uint16 totalLevel; // Doesn't not automatically add new skills to it bytes1 packedData; // Contains worldLocation in first 6 bits (0 is the main starting randomnessBeacon), and full mode unlocked in the upper most bit // TODO: Can be up to 7 QueuedAction[] actionQueue; string name; // Raw name } struct Item { EquipPosition equipPosition; bytes1 packedData; // 0x1 exists, upper most bit is full mode uint16 questPrerequisiteId; // Can it be transferred? bool isTransferable; // TODO: Move into packedData // Food uint16 healthRestored; // Boost vial BoostType boostType; uint16 boostValue; // Varies, could be the % increase uint24 boostDuration; // How long the effect of the boost last // Combat stats int16 meleeAttack; int16 magicAttack; int16 rangedAttack; int16 meleeDefence; int16 magicDefence; int16 rangedDefence; int16 health; // Minimum requirements in this skill to use this item (can be NONE) Skill skill; uint32 minXP; } // Used for events struct BoostInfo { uint40 startTime; uint24 duration; uint16 value; uint16 itemTokenId; // Get the effect of it BoostType boostType; } struct PlayerBoostInfo { uint40 startTime; uint24 duration; uint16 value; uint16 itemTokenId; // Get the effect of it BoostType boostType; // Another boost slot (for global/clan boosts this is the "last", for users it is the "extra") uint40 extraOrLastStartTime; uint24 extraOrLastDuration; uint16 extraOrLastValue; uint16 extraOrLastItemTokenId; BoostType extraOrLastBoostType; uint40 cooldown; // Just put here for packing } // This is effectively a ratio to produce 1 of outputTokenId. // Available choices that can be undertaken for an action struct ActionChoiceInput { uint8 skill; // Skill that this action choice is related to uint24 rate; // Rate of output produced per hour (base 1000) 3 decimals uint24 xpPerHour; uint16[] inputTokenIds; uint24[] inputAmounts; uint16 outputTokenId; uint8 outputAmount; uint8 successPercent; // 0-100 uint16 handItemTokenIdRangeMin; // Inclusive uint16 handItemTokenIdRangeMax; // Inclusive bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; uint8[] skills; // Skills required to do this action choice uint32[] skillMinXPs; // Min XP in the corresponding skills to be able to do this action choice int16[] skillDiffs; // How much the skill is increased/decreased by this action choice } struct ActionChoice { uint8 skill; // Skill that this action choice is related to uint24 rate; // Rate of output produced per hour (base 1000) 3 decimals uint24 xpPerHour; uint16 inputTokenId1; uint24 inputAmount1; uint16 inputTokenId2; uint24 inputAmount2; uint16 inputTokenId3; uint24 inputAmount3; uint16 outputTokenId; uint8 outputAmount; uint8 successPercent; // 0-100 uint8 skill1; // Skills required to do this action choice, commonly the same as skill uint32 skillMinXP1; // Min XP in the skill to be able to do this action choice int16 skillDiff1; // How much the skill is increased/decreased by this action choice uint8 skill2; uint32 skillMinXP2; int16 skillDiff2; uint8 skill3; uint32 skillMinXP3; int16 skillDiff3; uint16 handItemTokenIdRangeMin; // Inclusive uint16 handItemTokenIdRangeMax; // Inclusive uint16 questPrerequisiteId; // FullMode is last bit, first 6 bits is worldLocation, // 2nd last bit is if there are other skills in next storage slot to check, // 3rd last bit if the input amounts should be used bytes1 packedData; } // Must be in the same order as Skill enum struct PackedXP { uint40 melee; uint40 ranged; uint40 magic; uint40 defence; uint40 health; uint40 reservedCombat; bytes2 packedDataIsMaxed; // 2 bits per skill to indicate whether the maxed skill is reached. I think this was added in case we added a new max level which a user had already passed so old & new levels are the same and it would not trigger a level up event. // Next slot uint40 mining; uint40 woodcutting; uint40 fishing; uint40 smithing; uint40 thieving; uint40 crafting; bytes2 packedDataIsMaxed1; // 2 bits per skill to indicate whether the maxed skill is reached // Next slot uint40 cooking; uint40 firemaking; uint40 farming; uint40 alchemy; uint40 fletching; uint40 forging; bytes2 packedDataIsMaxed2; // 2 bits per skill to indicate whether the maxed skill is reached } struct AvatarInfo { string name; string description; string imageURI; Skill[2] startSkills; // Can be NONE } struct PastRandomRewardInfo { uint16 itemTokenId; uint24 amount; uint64 queueId; } struct PendingQueuedActionEquipmentState { uint256[] consumedItemTokenIds; uint256[] consumedAmounts; uint256[] producedItemTokenIds; uint256[] producedAmounts; } struct PendingQueuedActionMetadata { uint32 xpGained; // total xp gained uint32 rolls; bool died; uint16 actionId; uint64 queueId; uint24 elapsedTime; uint24 xpElapsedTime; uint8 checkpoint; } struct PendingQueuedActionData { // The amount of XP that the queued action has already gained Skill skill1; uint24 xpGained1; Skill skill2; // Most likely health uint24 xpGained2; Skill skill3; // Could come uint24 xpGained3; // How much food is consumed in the current action so far uint16 foodConsumed; // How many base consumables are consumed in the current action so far uint16 baseInputItemsConsumedNum; } struct PendingQueuedActionProcessed { // XP gained during this session Skill[] skills; uint32[] xpGainedSkills; // Data for the current action which has been previously processed, this is used to store on the Player PendingQueuedActionData currentAction; } struct QuestState { uint256[] consumedItemTokenIds; uint256[] consumedAmounts; uint256[] rewardItemTokenIds; uint256[] rewardAmounts; PlayerQuest[] activeQuestInfo; uint256[] questsCompleted; Skill[] skills; // Skills gained XP in uint32[] xpGainedSkills; // XP gained in these skills } struct LotteryWinnerInfo { uint16 lotteryId; uint24 raffleId; uint16 itemTokenId; uint16 amount; bool instantConsume; uint64 playerId; } struct PendingQueuedActionState { // These 2 are in sync. Separated to reduce gas/deployment costs as these are passed down many layers. PendingQueuedActionEquipmentState[] equipmentStates; PendingQueuedActionMetadata[] actionMetadatas; QueuedAction[] remainingQueuedActions; PastRandomRewardInfo[] producedPastRandomRewards; uint256[] xpRewardItemTokenIds; uint256[] xpRewardAmounts; uint256[] dailyRewardItemTokenIds; uint256[] dailyRewardAmounts; PendingQueuedActionProcessed processedData; bytes32 dailyRewardMask; QuestState quests; uint256 numPastRandomRewardInstancesToRemove; uint8 worldLocation; LotteryWinnerInfo lotteryWinner; } struct FullAttireBonusInput { Skill skill; uint8 bonusXPPercent; uint8 bonusRewardsPercent; // 3 = 3% uint16[5] itemTokenIds; // 0 = head, 1 = body, 2 arms, 3 body, 4 = feet } // Contains everything you need to create an item struct ItemInput { CombatStats combatStats; uint16 tokenId; EquipPosition equipPosition; bool isTransferable; bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; // Minimum requirements in this skill Skill skill; uint32 minXP; // Food uint16 healthRestored; // Boost BoostType boostType; uint16 boostValue; // Varies, could be the % increase uint24 boostDuration; // How long the effect of the boost vial last // uri string metadataURI; string name; } /* Order head, neck, body, arms, legs, feet, ring, reserved1, leftHandEquipment, rightHandEquipment, Not used yet: input1, input2,input3, regenerate, reserved2, reserved3 */ struct CheckpointEquipments { uint16[16] itemTokenIds; uint16[16] balances; } struct ActivePlayerInfo { uint64 playerId; uint40 checkpoint; uint24 timespan; uint24 timespan1; uint24 timespan2; } uint8 constant START_LEVEL = 17; // Needs updating when there is a new skill. Only useful for new heroes. uint256 constant MAX_UNIQUE_TICKETS = 64; // Used in a bunch of places uint256 constant IS_FULL_MODE_BIT = 7; // Passive/Instant/InstantVRF/Actions/ActionChoices/Item action uint256 constant IS_AVAILABLE_BIT = 6; // Passive actions uint256 constant HAS_RANDOM_REWARDS_BIT = 5; // The rest use world location for first 4 bits // Queued action uint256 constant HAS_PET_BIT = 2; uint256 constant IS_VALID_BIT = 1;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Skill} from "./misc.sol"; struct QuestInput { uint16 dependentQuestId; // The quest that must be completed before this one can be started uint16 actionId1; // action to do uint16 actionNum1; // how many (up to 65535) uint16 actionId2; // another action to do uint16 actionNum2; // how many (up to 65535) uint16 actionChoiceId; // actionChoice to perform uint16 actionChoiceNum; // how many to do (base number), (up to 65535) Skill skillReward; // The skill to reward XP to uint24 skillXPGained; // The amount of XP to give (up to 65535) uint16 rewardItemTokenId1; // Reward an item uint16 rewardAmount1; // amount of the reward (up to 65535) uint16 rewardItemTokenId2; // Reward another item uint16 rewardAmount2; // amount of the reward (up to 65535) uint16 burnItemTokenId; // Burn an item uint16 burnAmount; // amount of the burn (up to 65535) uint16 questId; // Unique id for this quest bool isFullModeOnly; // If true this quest requires the user be evolved uint8 worldLocation; // 0 is the main starting world } struct Quest { uint16 dependentQuestId; // The quest that must be completed before this one can be started uint16 actionId1; // action to do uint16 actionNum1; // how many (up to 65535) uint16 actionId2; // another action to do uint16 actionNum2; // how many (up to 65535) uint16 actionChoiceId; // actionChoice to perform uint16 actionChoiceNum; // how many to do (base number), (up to 65535) Skill skillReward; // The skill to reward XP to uint24 skillXPGained; // The amount of XP to give (up to 65535) uint16 rewardItemTokenId1; // Reward an item uint16 rewardAmount1; // amount of the reward (up to 65535) uint16 rewardItemTokenId2; // Reward another item uint16 rewardAmount2; // amount of the reward (up to 65535) uint16 burnItemTokenId; // Burn an item uint16 burnAmount; // amount of the burn (up to 65535) uint16 reserved; // Reserved for future use (previously was questId and cleared) bytes1 packedData; // FullMode is last bit, first 6 bits is worldLocation } struct PlayerQuest { uint32 questId; uint16 actionCompletedNum1; uint16 actionCompletedNum2; uint16 actionChoiceCompletedNum; uint16 burnCompletedAmount; } uint256 constant QUEST_PURSE_STRINGS = 5; // MAKE SURE THIS MATCHES definitions
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {BoostType, Equipment} from "./misc.sol"; struct GuaranteedReward { uint16 itemTokenId; uint16 rate; // num per hour (base 10, 1 decimal) for actions and num per duration for passive actions } struct RandomReward { uint16 itemTokenId; uint16 chance; // out of 65535 uint8 amount; // out of 255 } struct PendingRandomReward { uint16 actionId; uint40 startTime; uint24 xpElapsedTime; uint16 boostItemTokenId; uint24 elapsedTime; uint40 boostStartTime; // When the boost was started uint24 sentinelElapsedTime; // Full equipment at the time this was generated uint8 fullAttireBonusRewardsPercent; uint64 queueId; // TODO: Could reduce this if more stuff is needed } struct ActionRewards { uint16 guaranteedRewardTokenId1; uint16 guaranteedRewardRate1; // Num per hour base 10 (1 decimal) for actions (Max 6553.5 per hour), num per duration for passive actions uint16 guaranteedRewardTokenId2; uint16 guaranteedRewardRate2; uint16 guaranteedRewardTokenId3; uint16 guaranteedRewardRate3; // Random chance rewards uint16 randomRewardTokenId1; uint16 randomRewardChance1; // out of 65535 uint8 randomRewardAmount1; // out of 255 uint16 randomRewardTokenId2; uint16 randomRewardChance2; uint8 randomRewardAmount2; uint16 randomRewardTokenId3; uint16 randomRewardChance3; uint8 randomRewardAmount3; uint16 randomRewardTokenId4; uint16 randomRewardChance4; uint8 randomRewardAmount4; // No more room in this storage slot! } struct XPThresholdReward { uint32 xpThreshold; Equipment[] rewards; } enum InstantVRFActionType { NONE, GENERIC, FORGING, EGG } struct InstantVRFActionInput { uint16 actionId; uint16[] inputTokenIds; uint24[] inputAmounts; bytes data; InstantVRFActionType actionType; bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; } struct InstantVRFRandomReward { uint16 itemTokenId; uint16 chance; // out of 65535 uint16 amount; // out of 65535 } uint256 constant MAX_GUARANTEED_REWARDS_PER_ACTION = 3; uint256 constant MAX_RANDOM_REWARDS_PER_ACTION = 4; uint256 constant MAX_REWARDS_PER_ACTION = MAX_GUARANTEED_REWARDS_PER_ACTION + MAX_RANDOM_REWARDS_PER_ACTION; uint256 constant MAX_CONSUMED_PER_ACTION = 3; uint256 constant MAX_QUEST_REWARDS = 2; uint256 constant TIER_1_DAILY_REWARD_START_XP = 0; uint256 constant TIER_2_DAILY_REWARD_START_XP = 7_650; uint256 constant TIER_3_DAILY_REWARD_START_XP = 33_913; uint256 constant TIER_4_DAILY_REWARD_START_XP = 195_864; uint256 constant TIER_5_DAILY_REWARD_START_XP = 784_726; uint256 constant TIER_6_DAILY_REWARD_START_XP = 2_219_451; // 4 bytes for each threshold, starts at 500 xp in decimal bytes constant XP_THRESHOLD_REWARDS = hex"00000000000001F4000003E8000009C40000138800002710000075300000C350000186A00001D4C0000493E0000557300007A120000927C0000B71B0000DBBA0000F424000124F800016E360001B7740001E8480002625A0002932E0002DC6C0003567E0003D0900004C4B40005B8D80006ACFC0007A1200008954400098968000A7D8C000B71B0000C65D4000D59F8000E4E1C0";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IBrushToken is IERC20 { function burn(uint256 amount) external; function burnFrom(address account, uint256 amount) external; function transferFromBulk(address from, address[] calldata tos, uint256[] calldata amounts) external; function transferOwnership(address newOwner) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Item} from "../globals/players.sol"; interface IItemNFT { function balanceOfs(address account, uint16[] memory ids) external view returns (uint256[] memory); function balanceOfs10(address account, uint16[10] memory ids) external view returns (uint256[] memory); function balanceOf(address account, uint256 id) external view returns (uint256); function getItem(uint16 tokenId) external view returns (Item memory); function getItems(uint16[] calldata tokenIds) external view returns (Item[] memory); function totalSupply(uint256 id) external view returns (uint256); // ERC1155Supply function totalSupply() external view returns (uint256); // ERC1155Supply function mint(address to, uint256 id, uint256 quantity) external; function mintBatch(address to, uint256[] calldata ids, uint256[] calldata quantities) external; function burn(address account, uint256 id, uint256 value) external; function burnBatch(address account, uint256[] calldata ids, uint256[] calldata values) external; function getTimestampFirstMint(uint256 id) external view returns (uint256); function exists(uint256 id) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import {IBrushToken} from "./interfaces/external/IBrushToken.sol"; contract Treasury is UUPSUpgradeable, OwnableUpgradeable { using EnumerableMap for EnumerableMap.AddressToUintMap; event SetFundAllocationPercentages(address[] accounts, uint256[] percentages); error OnlyTerritories(); error OnlySpenders(); error LengthMismatch(); error TotalPercentageNot100(uint256 percent); EnumerableMap.AddressToUintMap private _accountsToPercentage; IBrushToken private _brush; address private _territories; mapping(address spender => bool) private _spenders; modifier onlySpenders() { require(_isSpender(_msgSender()), OnlySpenders()); _; } /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize(IBrushToken brush) external initializer { __Ownable_init(_msgSender()); __UUPSUpgradeable_init(); _brush = brush; } function _isSpender(address operator) private view returns (bool) { return _spenders[operator]; } function totalClaimable(address account) public view returns (uint256 total) { (bool success, uint256 percent) = _accountsToPercentage.tryGet(account); if (!success) { return 0; } return (_brush.balanceOf(address(this)) * percent) / 100; } function spend(address to, uint256 amountBrush) external onlySpenders { _brush.transfer(to, amountBrush); } // What accounts are given access to what funds of the treasury function setFundAllocationPercentages( address[] calldata accounts, uint256[] calldata percentages ) external onlyOwner { require(accounts.length == percentages.length, LengthMismatch()); uint256 totalPercentage; // Clear the old accounts uint256 length = _accountsToPercentage.length(); for (uint256 i = 0; i < length; ++i) { (address account, ) = _accountsToPercentage.at(0); _accountsToPercentage.remove(account); } for (uint256 i = 0; i < accounts.length; ++i) { _accountsToPercentage.set(accounts[i], percentages[i]); totalPercentage += percentages[i]; } require(totalPercentage == 100, TotalPercentageNot100(totalPercentage)); emit SetFundAllocationPercentages(accounts, percentages); } function setSpenders(address[] calldata spenders, bool isSpender) external onlyOwner { for (uint256 i; i < spenders.length; ++i) { _spenders[spenders[i]] = isSpender; } } // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} }
{ "evmVersion": "cancun", "optimizer": { "enabled": true, "runs": 9999999, "details": { "yul": true } }, "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AlreadySellable","type":"error"},{"inputs":[],"name":"AlreadyUnsellable","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"ItemCannotBeBought","type":"error"},{"inputs":[],"name":"ItemDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ItemNotSellable","type":"error"},{"inputs":[],"name":"LengthEmpty","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"LiquidatePriceIsHigherThanShop","type":"error"},{"inputs":[{"internalType":"uint256","name":"totalBrush","type":"uint256"},{"internalType":"uint256","name":"minExpectedBrush","type":"uint256"}],"name":"MinExpectedBrushNotReached","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"totalSold","type":"uint256"},{"internalType":"uint256","name":"allocationRemaining","type":"uint256"}],"name":"NotEnoughAllocationRemaining","type":"error"},{"inputs":[{"internalType":"uint256","name":"brushNeeded","type":"uint256"},{"internalType":"uint256","name":"brushAvailable","type":"uint256"}],"name":"NotEnoughBrush","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"PercentNotTotal100","type":"error"},{"inputs":[],"name":"PriceCannotBeZero","type":"error"},{"inputs":[],"name":"SellingTooQuicklyAfterItemIntroduction","type":"error"},{"inputs":[],"name":"ShopItemAlreadyExists","type":"error"},{"inputs":[],"name":"ShopItemDoesNotExist","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"tokenId","type":"uint16"},{"internalType":"uint128","name":"price","type":"uint128"}],"indexed":false,"internalType":"struct Shop.ShopItem[]","name":"shopItems","type":"tuple[]"}],"name":"AddShopItems","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16[]","name":"tokenIds","type":"uint16[]"}],"name":"AddUnsellableItems","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"quantities","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"name":"BuyBatch","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"tokenId","type":"uint16"},{"internalType":"uint128","name":"price","type":"uint128"}],"indexed":false,"internalType":"struct Shop.ShopItem[]","name":"shopItems","type":"tuple[]"}],"name":"EditShopItems","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"allocation","type":"uint256"}],"name":"NewAllocation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16[]","name":"tokenIds","type":"uint16[]"}],"name":"RemoveShopItems","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16[]","name":"tokenIds","type":"uint16[]"}],"name":"RemoveUnsellableItems","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"Sell","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"quantities","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"name":"SellBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"brushBurntPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"brushTreasuryPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"brushDevPercentage","type":"uint256"}],"name":"SetBrushDistributionPercentages","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minItemQuantityBeforeSellsAllowed","type":"uint256"}],"name":"SetMinItemQuantityBeforeSellsAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"SetSellingCutoffDuration","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":[{"components":[{"internalType":"uint16","name":"tokenId","type":"uint16"},{"internalType":"uint128","name":"price","type":"uint128"}],"internalType":"struct Shop.ShopItem[]","name":"buyableItems","type":"tuple[]"}],"name":"addBuyableItems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"itemTokenIds","type":"uint16[]"}],"name":"addUnsellableItems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint16","name":"tokenId","type":"uint16"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"quantities","type":"uint256[]"}],"name":"buyBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"tokenId","type":"uint16"},{"internalType":"uint128","name":"price","type":"uint128"}],"internalType":"struct Shop.ShopItem[]","name":"itemsToEdit","type":"tuple[]"}],"name":"editItems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getMinItemQuantityBeforeSellsAllowed","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IBrushToken","name":"brush","type":"address"},{"internalType":"contract Treasury","name":"treasury","type":"address"},{"internalType":"address","name":"dev","type":"address"},{"internalType":"uint24","name":"minItemQuantityBeforeSellsAllowed","type":"uint24"},{"internalType":"uint24","name":"sellingCutoffDuration","type":"uint24"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"tokenId","type":"uint16"}],"name":"liquidatePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"tokenIds","type":"uint16[]"}],"name":"liquidatePrices","outputs":[{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","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":"uint16[]","name":"tokenIds","type":"uint16[]"}],"name":"removeItems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"itemTokenIds","type":"uint16[]"}],"name":"removeUnsellableItems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"tokenId","type":"uint16"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"minExpectedBrush","type":"uint256"}],"name":"sell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"quantities","type":"uint256[]"},{"internalType":"uint256","name":"minExpectedBrush","type":"uint256"}],"name":"sellBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"brushBurntPercentage","type":"uint8"},{"internalType":"uint8","name":"brushTreasuryPercentage","type":"uint8"},{"internalType":"uint8","name":"brushDevPercentage","type":"uint8"}],"name":"setBrushDistributionPercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IItemNFT","name":"itemNFT","type":"address"}],"name":"setItemNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"minItemQuantityBeforeSellsAllowed","type":"uint24"}],"name":"setMinItemQuantityBeforeSellsAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"duration","type":"uint24"}],"name":"setSellingCutoffDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"tokenId","type":"uint16"}],"name":"shopItems","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"tokenId","type":"uint16"}],"name":"tokenInfos","outputs":[{"components":[{"internalType":"uint80","name":"allocationRemaining","type":"uint80"},{"internalType":"uint80","name":"price","type":"uint80"},{"internalType":"uint40","name":"checkpointTimestamp","type":"uint40"},{"internalType":"bool","name":"unsellable","type":"bool"}],"internalType":"struct Shop.TokenInfo","name":"tokenInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","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
60a080604052346100c257306080525f5160206133645f395f51905f525460ff8160401c166100b3576002600160401b03196001600160401b03821601610060575b60405161329d90816100c78239608051818181611a250152611b440152f35b6001600160401b0319166001600160401b039081175f5160206133645f395f51905f525581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80610041565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816312016f841461233b57508063128dd84c146121d85780632ea0e51c1461211f578063355c25f81461205c5780633750556c14611f805780633d72f2c714611e8657806347e25eff14611e015780634f1ef28614611a9d57806352d1902d146119e05780635f5a0b4014611926578063715018a61461184c5780637c71df4914611428578063853930ad1461111e5780638da5cb5b146110ad5780638e68e18d14610d955780639a92726914610c22578063a12fd4bb14610a46578063ad3cb1cc14610996578063b17d998b1461074b578063b34c555e146106fa578063b6617e5d146104bb578063ca7ae6a5146103e0578063d9b438be14610381578063ee727ec21461017e5763f2fde38b14610131575f80fd5b3461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5761017861016b612450565b6101736129d3565b6128e6565b80f35b80fd5b503461017b5761018d3661238d565b6101956129d3565b825b8181106101d657507fd3b4ffac9fa92781fa3c42c21bfac7e9f9dc9942f2033aa4f460605c3a96563b916101d060405192839283612614565b0390a180f35b6101e18183856125ab565b73ffffffffffffffffffffffffffffffffffffffff60035416602061ffff602461020a856125e8565b60405194859384927f4f558e790000000000000000000000000000000000000000000000000000000084521660048301525afa908115610376578691610348575b50156103205761ffff61025d826125e8565b168552600560205260408520546102f857602081016fffffffffffffffffffffffffffffffff61028c826125f7565b16156102d0579061ffff6102bd6fffffffffffffffffffffffffffffffff6102b6600196956125f7565b16926125e8565b1686526005602052604086205501610197565b6004867f2c669f0a000000000000000000000000000000000000000000000000000000008152fd5b6004857f044a9661000000000000000000000000000000000000000000000000000000008152fd5b6004857f9a749d81000000000000000000000000000000000000000000000000000000008152fd5b610369915060203d811161036f575b61036181836124bc565b8101906128ce565b5f61024b565b503d610357565b6040513d88823e3d90fd5b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57602069ffffffffffffffffffff6103d76103c96123ff565b6103d1612a76565b90612b19565b16604051908152f35b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5760043567ffffffffffffffff81116104b75761043090369060040161241f565b9060609180610454575b6040516020808252819061045090820186612537565b0390f35b90915061045f612a76565b90610469816126e3565b935b81811061047b575083925061043a565b8069ffffffffffffffffffff6104a5856104a061049b600196888b61267b565b6125e8565b612b19565b166104b0828861277f565b520161046b565b5080fd5b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5760043567ffffffffffffffff81116104b75761050b90369060040161241f565b6105169291926129d3565b815b8181106105dd57506003549261ffff821661ffff8560a01c16019361ffff85116105b0577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000007f94f6b0382d8a2d71d45ba1209f7577a146485e813cd876e83e94355e2846d3b4959660a01b169116176003556101d06040519283928361268b565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61ffff6105ee61049b83858861267b565b1680845283602052604084209081549060ff8260c81c166106d257602073ffffffffffffffffffffffffffffffffffffffff60035416916024604051809481937f4f558e7900000000000000000000000000000000000000000000000000000000835260048301525afa9081156103765786916106b4575b5015610320577fffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffff16790100000000000000000000000000000000000000000000000000179055600101610518565b6106cc915060203d811161036f5761036181836124bc565b5f610666565b6004867f410ad840000000000000000000000000000000000000000000000000000000008152fd5b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57604060209161ffff61073a6123ff565b168152600583522054604051908152f35b503461017b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57610783612450565b6024359061ffff82169182810361099257604435928085526005602052604085205491821561096a5785906107c06107bb8786612732565b612fd0565b9073ffffffffffffffffffffffffffffffffffffffff6001541691823b156109375761081f928592836040518096819582947f307b8a02000000000000000000000000000000000000000000000000000000008452336004850161284c565b03925af190811561095f57839161094a575b505073ffffffffffffffffffffffffffffffffffffffff60035416803b15610946576040517f156e29f600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716600482015261ffff9290921660248301526044820187905282908290606490829084905af1801561093b5761091c575b50927ef93dbdb72854b6b6fb35433086556f2635fc83c37080c667496fecfa650fb4928560a09373ffffffffffffffffffffffffffffffffffffffff60405194338652166020850152604084015260608301526080820152a180f35b8161092a91959493956124bc565b610937579091845f6108c0565b8480fd5b6040513d84823e3d90fd5b8280fd5b81610954916124bc565b6104b757815f610831565b6040513d85823e3d90fd5b6004867f2cd04007000000000000000000000000000000000000000000000000000000008152fd5b8380fd5b503461017b57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57604080516109d382826124bc565b600581527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602083017f352e302e3000000000000000000000000000000000000000000000000000000081528451958694602086525180928160208801528787015e85828601015201168101030190f35b503461017b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57610a7e6123ff565b6044356024358369ffffffffffffffffffff610aa1610a9b612a76565b86612b19565b1693610aad8386612732565b610acb61ffff831695610ac1888789612c5a565b8281811015612793565b73ffffffffffffffffffffffffffffffffffffffff60025416803b15610992576040517faf7d6ca3000000000000000000000000000000000000000000000000000000008152336004820152602481019290925283908290604490829084905af190811561095f578391610c0d575b505073ffffffffffffffffffffffffffffffffffffffff60035416803b15610946576040517ff5298aca00000000000000000000000000000000000000000000000000000000815233600482015261ffff9290921660248301526044820184905282908290606490829084905af1801561093b57610bf4575b507f846c37eef631e0943682d87352ec117c20008eb7f425c9b85ac011a6d4774cc0608084868560405192338452602084015260408301526060820152a180f35b81610c01919493946124bc565b6109925790835f610bb3565b81610c17916124bc565b6104b757815f610b3a565b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5760043567ffffffffffffffff81116104b757610c7290369060040161241f565b610c7d9291926129d3565b815b818110610d1757506003549261ffff821661ffff8560a01c16039361ffff85116105b0577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000007f1861b42bcf45563723332dc6e4f28b72999ce5769a0a2b3323037a6b2ced8511959660a01b169116176003556101d06040519283928361268b565b61ffff610d2861049b83858861267b565b1683528260205260408320805460ff8160c81c1615610d6d577fffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffff169055600101610c7f565b6004857ffa32579c000000000000000000000000000000000000000000000000000000008152fd5b503461017b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57610dcd612450565b60243567ffffffffffffffff811161094657610ded90369060040161241f565b60449291923567ffffffffffffffff811161093757610e1090369060040161241f565b909382156110855781830361105d57859286610e2b826126e3565b94815b838110610fd85750610e3f90612fd0565b9073ffffffffffffffffffffffffffffffffffffffff6001541691823b1561099257610e9e928492836040518096819582947f307b8a02000000000000000000000000000000000000000000000000000000008452336004850161284c565b03925af1801561093b57610fc3575b5073ffffffffffffffffffffffffffffffffffffffff60035416803b156104b7578183899286838b610f0f8b604051988997889687957fd81d0a1500000000000000000000000000000000000000000000000000000000875260048701612807565b03925af1801561093b57610fa0575b507f6c41018d91a69c94652370bd8b720eb5d5e02cf87baefdaadfb7d7171780e53173ffffffffffffffffffffffffffffffffffffffff876101d088610f928c8a610f848b8b604051998a99338b521660208a015260a060408a015260a08901916127ca565b9186830360608801526127ca565b908382036080850152612537565b81610fb0919796949395976124bc565b610fbf5790929391865f610f1e565b8680fd5b81610fcd916124bc565b610fbf57865f610ead565b90915061ffff610fe982858761267b565b3516895260056020526040892054918215611035579061102060019261101a611013848a8e61267b565b3586612732565b90612745565b9261102b828961277f565b5201908891610e2e565b60048a7f2cd04007000000000000000000000000000000000000000000000000000000008152fd5b6004867fff633a38000000000000000000000000000000000000000000000000000000008152fd5b6004867f91a5942c000000000000000000000000000000000000000000000000000000008152fd5b503461017b57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57602073ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416604051908152f35b503461134c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043567ffffffffffffffff811161134c5761116e90369060040161241f565b9060243567ffffffffffffffff811161134c5761118f90369060040161241f565b9290604435938215611400578083036113d8575f6111ac846126e3565b956111b5612a76565b5f5b86811061135057505080826111ce92811015612793565b73ffffffffffffffffffffffffffffffffffffffff60025416803b1561134c576040517faf7d6ca300000000000000000000000000000000000000000000000000000000815233600482015260248101929092525f908290604490829084905af180156113415761132c575b508573ffffffffffffffffffffffffffffffffffffffff60035416803b156104b7576040517f6b20c454000000000000000000000000000000000000000000000000000000008152908290829081838161129b898b8d8f3360048701612807565b03925af1801561093b5761130f575b5050936113016101d0927fe014e54bfef8f3f83ecfa41d5c78aebe96ece67b9778477c6da4b53900133d0e966112f3604051978897338952608060208a015260808901916127ca565b9186830360408801526127ca565b908382036060850152612537565b8161131c919793976124bc565b6113285793855f6112aa565b8580fd5b6113399196505f906124bc565b5f945f61123a565b6040513d5f823e3d90fd5b5f80fd5b8161135c82898b61267b565b3561ffff169061136b91612b19565b69ffffffffffffffffffff169361138382878961267b565b3561138e9086612732565b61139791612745565b936113a2828b61277f565b526113ae81888a61267b565b356113ba82878961267b565b356113c5838c61277f565b51906113d092612c5a565b6001016111b7565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f91a5942c000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461134c5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043573ffffffffffffffffffffffffffffffffffffffff811680910361134c576024359073ffffffffffffffffffffffffffffffffffffffff821680920361134c576044359173ffffffffffffffffffffffffffffffffffffffff831680930361134c5760643562ffffff81169081810361134c576084359462ffffff86169283870361134c577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549460ff8660401c16159767ffffffffffffffff871680159081611844575b600114908161183a575b159081611831575b50611809577fdeeec7d2d22b446e217440535fb1375e98be303a3260190c2d1e1b28a3c57f6560207fffffffffffff000000ffffff00000000000000000000000000000000000000009479ffffff0000000000000000000000000000000000000000000000947f2d36e1826c285c959501ff9750ab5c3202a197bd16d557e1251811bacfb0b5549b8b8e60017fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000879f16177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00556117b4575b5061161261309f565b61161a61309f565b611623336128e6565b61162b61309f565b7fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001557fffffffffffffffffffffffff0000000000000000000000000000000000000000600254161760025560045497877fffffffffffffffffffffffff00000000000000000000000000000000000000008a16176004556116b16129d3565b7fffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffff78ffffff000000000000000000000000000000000000000000006003549260b01b16911617600355604051908152a16117096129d3565b60b81b1692161717600455604051908152a161172157005b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b7fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00558e611609565b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050158a611531565b303b159150611529565b8a915061151f565b3461134c575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c576118826129d3565b5f73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300547fffffffffffffffffffffffff000000000000000000000000000000000000000081167f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c577f2d36e1826c285c959501ff9750ab5c3202a197bd16d557e1251811bacfb0b554602061198061237b565b6119886129d3565b6004547fffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffff79ffffff00000000000000000000000000000000000000000000008360b81b1691161760045562ffffff60405191168152a1005b3461134c575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003611a755760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b7fe07c8dba000000000000000000000000000000000000000000000000000000005f5260045ffd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c57611acf612450565b6024359067ffffffffffffffff821161134c573660238301121561134c57816004013590611afc826124fd565b91611b0a60405193846124bc565b8083526020830193366024838301011161134c57815f9260246020930187378401015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803014908115611dbf575b50611a7557611b7c6129d3565b73ffffffffffffffffffffffffffffffffffffffff8116926040517f52d1902d000000000000000000000000000000000000000000000000000000008152602081600481885afa5f9181611d8b575b50611bfc57847f4c9c8ce3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc869203611d605750823b15611d3557807fffffffffffffffffffffffff00000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2825115611d03575f8091611cf9945190845af43d15611cfb573d91611cdd836124fd565b92611ceb60405194856124bc565b83523d5f602085013e6131ce565b005b6060916131ce565b50505034611d0d57005b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4c9c8ce3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7faa1d49a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011611db7575b81611da7602093836124bc565b8101031261134c57519086611bcb565b3d9150611d9a565b905073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141584611b6f565b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043573ffffffffffffffffffffffffffffffffffffffff811680910361134c57611e596129d3565b7fffffffffffffffffffffffff000000000000000000000000000000000000000060035416176003555f80f35b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043567ffffffffffffffff811161134c57611ed590369060040161241f565b611edd6129d3565b5f5b818110611f1d57507ff189929725290201c3e718cac3495edb1e4cbac62f7b71482bef2fd58c27195d91611f186040519283928361268b565b0390a1005b61ffff611f2e61049b83858761267b565b1690815f52600560205260405f205415611f58576001915f5260056020525f604081205501611edf565b7f10e067e0000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5761ffff611fba6123ff565b5f6060604051611fc981612473565b8281528260208201528260408201520152165f525f602052608060405f2060405190611ff482612473565b5464ffffffffff69ffffffffffffffffffff82169283815269ffffffffffffffffffff60208201818560501c16815260ff60606040850194868860a01c168652019560c81c161515855260405195865251166020850152511660408301525115156060820152f35b3461134c5761206a3661238d565b906120736129d3565b5f5b8281106120ad576040517ff29d3629e86628beaba81304b186def19003218376a3f0bcde417deb22a9a57a9080611f18868683612614565b61ffff6120be61049b8386866125ab565b165f52600560205260405f205415611f5857806fffffffffffffffffffffffffffffffff6120fa60206120f460019588886125ab565b016125f7565b1661ffff61210c61049b8488886125ab565b165f52600560205260405f205501612075565b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c577fdeeec7d2d22b446e217440535fb1375e98be303a3260190c2d1e1b28a3c57f65602061217961237b565b6121816129d3565b6003547fffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffff78ffffff000000000000000000000000000000000000000000008360b01b1691161760035562ffffff60405191168152a1005b3461134c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043560ff81169081810361134c576024359160ff83169182840361134c576044359260ff84169182850361134c5761223e6129d3565b606460ff612255876122508a8661256a565b61256a565b1603612313577fb4ae64ee51c0d84410a16847b3f8f3d7ac7bbbeaf2abae8671beba816f0279e39575ff0000000000000000000000000000000000000000006060967fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000076ff000000000000000000000000000000000000000000006004549360b01b169560a01b169116179160a81b16171760045560405192835260208301526040820152a1005b7f32de0eb6000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461134c575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760209062ffffff60035460b01c168152f35b6004359062ffffff8216820361134c57565b9060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261134c5760043567ffffffffffffffff811161134c578260238201121561134c5780600401359267ffffffffffffffff841161134c5760248460061b8301011161134c576024019190565b6004359061ffff8216820361134c57565b359061ffff8216820361134c57565b9181601f8401121561134c5782359167ffffffffffffffff831161134c576020808501948460051b01011161134c57565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361134c57565b6080810190811067ffffffffffffffff82111761248f57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761248f57604052565b67ffffffffffffffff811161248f57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b90602080835192838152019201905f5b8181106125545750505090565b8251845260209384019390920191600101612547565b9060ff8091169116019060ff821161257e57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908110156125bb5760061b0190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b3561ffff8116810361134c5790565b356fffffffffffffffffffffffffffffffff8116810361134c5790565b60208082528101839052604001915f905b8082106126325750505090565b90919261ffff61264185612410565b1681526020840135906fffffffffffffffffffffffffffffffff821680920361134c57604081600193602083940152019401920190612625565b91908110156125bb5760051b0190565b60208082528101839052604001915f5b8181106126a85750505090565b90919260208060019261ffff6126bd88612410565b16815201940192910161269b565b67ffffffffffffffff811161248f5760051b60200190565b906126ed826126cb565b6126fa60405191826124bc565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061272882946126cb565b0190602036910137565b8181029291811591840414171561257e57565b9190820180921161257e57565b8051156125bb5760200190565b8051600110156125bb5760400190565b8051600210156125bb5760600190565b80518210156125bb5760209160051b010190565b1561279c575050565b7f85d8dc79000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b90918281527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831161134c5760209260051b809284830137010190565b9391612849959373ffffffffffffffffffffffffffffffffffffffff61283b931686526060602087015260608601916127ca565b9260408185039101526127ca565b90565b9392909373ffffffffffffffffffffffffffffffffffffffff606082019516815260606020820152825180955260206080820193015f955b8087106128a05750506128499394506040818403910152612537565b909360208060019273ffffffffffffffffffffffffffffffffffffffff885116815201950196019590612884565b9081602091031261134c5751801515810361134c5790565b73ffffffffffffffffffffffffffffffffffffffff1680156129a75773ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054827fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b7f1e4fbdf7000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054163303612a1357565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b8115612a49570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b6024602073ffffffffffffffffffffffffffffffffffffffff60025416604051928380927f1dfce5c60000000000000000000000000000000000000000000000000000000082523060048301525afa8015611341575f90612ae5575b6128499150612adf6130f6565b90612a3f565b506020813d602011612b11575b81612aff602093836124bc565b8101031261134c576128499051612ad2565b3d9150612af2565b919061ffff5f9316805f525f60205260405f2060035490604051927fbd85b039000000000000000000000000000000000000000000000000000000008452600484015260208360248173ffffffffffffffffffffffffffffffffffffffff86165afa928315611341575f93612c19575b505492612b9f64ffffffffff8560a01c1661319f565b15612bfc5782612bd8575b5062ffffff905b60b01c1611908115612bca575b50612bc557565b5f9150565b60ff915060c81c165f612bbe565b62ffffff919550612bf48369ffffffffffffffffffff92612a3f565b169490612baa565b50935062ffffff69ffffffffffffffffffff8360501c1694612bb1565b9092506020813d602011612c45575b81612c35602093836124bc565b8101031261134c5751915f612b89565b3d9150612c28565b9190820391821161257e57565b90815f52600560205260405f2054838115918215612fc5575b505015612f99576024602073ffffffffffffffffffffffffffffffffffffffff60035416604051928380927f19e7f0460000000000000000000000000000000000000000000000000000000082528760048301525afa8015611341575f90612f65575b612cec915062ffffff60045460b81c1690612745565b4210612f3d57815f525f60205260405f209283549060ff8260c81c16612f1157612d1f64ffffffffff8360a01c1661319f565b15612efb576024612d2e6130f6565b602073ffffffffffffffffffffffffffffffffffffffff60025416604051938480927f1dfce5c60000000000000000000000000000000000000000000000000000000082523060048301525afa8015611341575f90612ec7575b612d929250612a3f565b926201518042046201518081029080820462015180149015171561257e57612e4c937fffffffffffffff000000000000000000000000000000ffffffffffffffffffff78ffffffffff000000000000000000000000000000000000000073ffffffffffffffffffff000000000000000000008660501b169360a01b169116171786557fa86a2ea2409eb9a5a366acc2656446a90f9a8f4b7694ac183718898663bb292a6040805161ffff88168152866020820152a1612732565b91828210612e94575069ffffffffffffffffffff91612e6a91612c4d565b167fffffffffffffffffffffffffffffffffffffffffffff00000000000000000000825416179055565b82907f4fb574cf000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b506020823d602011612ef3575b81612ee1602093836124bc565b8101031261134c57612d929151612d88565b3d9150612ed4565b69ffffffffffffffffffff612e4c921692612732565b837f3fb251d7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f22cfd44d000000000000000000000000000000000000000000000000000000005f5260045ffd5b506020813d602011612f91575b81612f7f602093836124bc565b8101031261134c57612cec9051612cd6565b3d9150612f72565b507f58a2d6dc000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b10159050835f612c73565b9060405190612fe06080836124bc565b600382526060918236602083013761309c73ffffffffffffffffffffffffffffffffffffffff8295604051956130176080886124bc565b600387523660208801378561307a6064613073600454948261303f60ff8860a01c1683612732565b0461304986612752565b528261305b60ff8860a81c1683612732565b046130658661275f565b5260ff8660b01c1690612732565b049161276f565b525f61308585612752565b5281600254166130948561275f565b52169161276f565b52565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c16156130ce57565b7fd7e6bcf8000000000000000000000000000000000000000000000000000000005f5260045ffd5b600354604051907f18160ddd00000000000000000000000000000000000000000000000000000000825260208260048173ffffffffffffffffffffffffffffffffffffffff85165afa918215611341575f92613169575b5060a01c61ffff16818110613160575090565b61284991612c4d565b9091506020813d602011613197575b81613185602093836124bc565b8101031261134c57519061ffff61314d565b3d9150613178565b6201518042046201518081029080820462015180149015171561257e5762015180820180921161257e57101590565b9061320b57508051156131e357805190602001fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b8151158061325e575b61321c575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b1561321456fea264697066735822122043daf9266a348737d6ff0b6acebbb0e92dd2e78d08c9aa3842b4f02a93cb5b7e64736f6c634300081c0033f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816312016f841461233b57508063128dd84c146121d85780632ea0e51c1461211f578063355c25f81461205c5780633750556c14611f805780633d72f2c714611e8657806347e25eff14611e015780634f1ef28614611a9d57806352d1902d146119e05780635f5a0b4014611926578063715018a61461184c5780637c71df4914611428578063853930ad1461111e5780638da5cb5b146110ad5780638e68e18d14610d955780639a92726914610c22578063a12fd4bb14610a46578063ad3cb1cc14610996578063b17d998b1461074b578063b34c555e146106fa578063b6617e5d146104bb578063ca7ae6a5146103e0578063d9b438be14610381578063ee727ec21461017e5763f2fde38b14610131575f80fd5b3461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5761017861016b612450565b6101736129d3565b6128e6565b80f35b80fd5b503461017b5761018d3661238d565b6101956129d3565b825b8181106101d657507fd3b4ffac9fa92781fa3c42c21bfac7e9f9dc9942f2033aa4f460605c3a96563b916101d060405192839283612614565b0390a180f35b6101e18183856125ab565b73ffffffffffffffffffffffffffffffffffffffff60035416602061ffff602461020a856125e8565b60405194859384927f4f558e790000000000000000000000000000000000000000000000000000000084521660048301525afa908115610376578691610348575b50156103205761ffff61025d826125e8565b168552600560205260408520546102f857602081016fffffffffffffffffffffffffffffffff61028c826125f7565b16156102d0579061ffff6102bd6fffffffffffffffffffffffffffffffff6102b6600196956125f7565b16926125e8565b1686526005602052604086205501610197565b6004867f2c669f0a000000000000000000000000000000000000000000000000000000008152fd5b6004857f044a9661000000000000000000000000000000000000000000000000000000008152fd5b6004857f9a749d81000000000000000000000000000000000000000000000000000000008152fd5b610369915060203d811161036f575b61036181836124bc565b8101906128ce565b5f61024b565b503d610357565b6040513d88823e3d90fd5b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57602069ffffffffffffffffffff6103d76103c96123ff565b6103d1612a76565b90612b19565b16604051908152f35b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5760043567ffffffffffffffff81116104b75761043090369060040161241f565b9060609180610454575b6040516020808252819061045090820186612537565b0390f35b90915061045f612a76565b90610469816126e3565b935b81811061047b575083925061043a565b8069ffffffffffffffffffff6104a5856104a061049b600196888b61267b565b6125e8565b612b19565b166104b0828861277f565b520161046b565b5080fd5b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5760043567ffffffffffffffff81116104b75761050b90369060040161241f565b6105169291926129d3565b815b8181106105dd57506003549261ffff821661ffff8560a01c16019361ffff85116105b0577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000007f94f6b0382d8a2d71d45ba1209f7577a146485e813cd876e83e94355e2846d3b4959660a01b169116176003556101d06040519283928361268b565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61ffff6105ee61049b83858861267b565b1680845283602052604084209081549060ff8260c81c166106d257602073ffffffffffffffffffffffffffffffffffffffff60035416916024604051809481937f4f558e7900000000000000000000000000000000000000000000000000000000835260048301525afa9081156103765786916106b4575b5015610320577fffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffff16790100000000000000000000000000000000000000000000000000179055600101610518565b6106cc915060203d811161036f5761036181836124bc565b5f610666565b6004867f410ad840000000000000000000000000000000000000000000000000000000008152fd5b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57604060209161ffff61073a6123ff565b168152600583522054604051908152f35b503461017b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57610783612450565b6024359061ffff82169182810361099257604435928085526005602052604085205491821561096a5785906107c06107bb8786612732565b612fd0565b9073ffffffffffffffffffffffffffffffffffffffff6001541691823b156109375761081f928592836040518096819582947f307b8a02000000000000000000000000000000000000000000000000000000008452336004850161284c565b03925af190811561095f57839161094a575b505073ffffffffffffffffffffffffffffffffffffffff60035416803b15610946576040517f156e29f600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716600482015261ffff9290921660248301526044820187905282908290606490829084905af1801561093b5761091c575b50927ef93dbdb72854b6b6fb35433086556f2635fc83c37080c667496fecfa650fb4928560a09373ffffffffffffffffffffffffffffffffffffffff60405194338652166020850152604084015260608301526080820152a180f35b8161092a91959493956124bc565b610937579091845f6108c0565b8480fd5b6040513d84823e3d90fd5b8280fd5b81610954916124bc565b6104b757815f610831565b6040513d85823e3d90fd5b6004867f2cd04007000000000000000000000000000000000000000000000000000000008152fd5b8380fd5b503461017b57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57604080516109d382826124bc565b600581527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602083017f352e302e3000000000000000000000000000000000000000000000000000000081528451958694602086525180928160208801528787015e85828601015201168101030190f35b503461017b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57610a7e6123ff565b6044356024358369ffffffffffffffffffff610aa1610a9b612a76565b86612b19565b1693610aad8386612732565b610acb61ffff831695610ac1888789612c5a565b8281811015612793565b73ffffffffffffffffffffffffffffffffffffffff60025416803b15610992576040517faf7d6ca3000000000000000000000000000000000000000000000000000000008152336004820152602481019290925283908290604490829084905af190811561095f578391610c0d575b505073ffffffffffffffffffffffffffffffffffffffff60035416803b15610946576040517ff5298aca00000000000000000000000000000000000000000000000000000000815233600482015261ffff9290921660248301526044820184905282908290606490829084905af1801561093b57610bf4575b507f846c37eef631e0943682d87352ec117c20008eb7f425c9b85ac011a6d4774cc0608084868560405192338452602084015260408301526060820152a180f35b81610c01919493946124bc565b6109925790835f610bb3565b81610c17916124bc565b6104b757815f610b3a565b503461017b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b5760043567ffffffffffffffff81116104b757610c7290369060040161241f565b610c7d9291926129d3565b815b818110610d1757506003549261ffff821661ffff8560a01c16039361ffff85116105b0577fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff75ffff00000000000000000000000000000000000000007f1861b42bcf45563723332dc6e4f28b72999ce5769a0a2b3323037a6b2ced8511959660a01b169116176003556101d06040519283928361268b565b61ffff610d2861049b83858861267b565b1683528260205260408320805460ff8160c81c1615610d6d577fffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffff169055600101610c7f565b6004857ffa32579c000000000000000000000000000000000000000000000000000000008152fd5b503461017b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57610dcd612450565b60243567ffffffffffffffff811161094657610ded90369060040161241f565b60449291923567ffffffffffffffff811161093757610e1090369060040161241f565b909382156110855781830361105d57859286610e2b826126e3565b94815b838110610fd85750610e3f90612fd0565b9073ffffffffffffffffffffffffffffffffffffffff6001541691823b1561099257610e9e928492836040518096819582947f307b8a02000000000000000000000000000000000000000000000000000000008452336004850161284c565b03925af1801561093b57610fc3575b5073ffffffffffffffffffffffffffffffffffffffff60035416803b156104b7578183899286838b610f0f8b604051988997889687957fd81d0a1500000000000000000000000000000000000000000000000000000000875260048701612807565b03925af1801561093b57610fa0575b507f6c41018d91a69c94652370bd8b720eb5d5e02cf87baefdaadfb7d7171780e53173ffffffffffffffffffffffffffffffffffffffff876101d088610f928c8a610f848b8b604051998a99338b521660208a015260a060408a015260a08901916127ca565b9186830360608801526127ca565b908382036080850152612537565b81610fb0919796949395976124bc565b610fbf5790929391865f610f1e565b8680fd5b81610fcd916124bc565b610fbf57865f610ead565b90915061ffff610fe982858761267b565b3516895260056020526040892054918215611035579061102060019261101a611013848a8e61267b565b3586612732565b90612745565b9261102b828961277f565b5201908891610e2e565b60048a7f2cd04007000000000000000000000000000000000000000000000000000000008152fd5b6004867fff633a38000000000000000000000000000000000000000000000000000000008152fd5b6004867f91a5942c000000000000000000000000000000000000000000000000000000008152fd5b503461017b57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017b57602073ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416604051908152f35b503461134c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043567ffffffffffffffff811161134c5761116e90369060040161241f565b9060243567ffffffffffffffff811161134c5761118f90369060040161241f565b9290604435938215611400578083036113d8575f6111ac846126e3565b956111b5612a76565b5f5b86811061135057505080826111ce92811015612793565b73ffffffffffffffffffffffffffffffffffffffff60025416803b1561134c576040517faf7d6ca300000000000000000000000000000000000000000000000000000000815233600482015260248101929092525f908290604490829084905af180156113415761132c575b508573ffffffffffffffffffffffffffffffffffffffff60035416803b156104b7576040517f6b20c454000000000000000000000000000000000000000000000000000000008152908290829081838161129b898b8d8f3360048701612807565b03925af1801561093b5761130f575b5050936113016101d0927fe014e54bfef8f3f83ecfa41d5c78aebe96ece67b9778477c6da4b53900133d0e966112f3604051978897338952608060208a015260808901916127ca565b9186830360408801526127ca565b908382036060850152612537565b8161131c919793976124bc565b6113285793855f6112aa565b8580fd5b6113399196505f906124bc565b5f945f61123a565b6040513d5f823e3d90fd5b5f80fd5b8161135c82898b61267b565b3561ffff169061136b91612b19565b69ffffffffffffffffffff169361138382878961267b565b3561138e9086612732565b61139791612745565b936113a2828b61277f565b526113ae81888a61267b565b356113ba82878961267b565b356113c5838c61277f565b51906113d092612c5a565b6001016111b7565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f91a5942c000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461134c5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043573ffffffffffffffffffffffffffffffffffffffff811680910361134c576024359073ffffffffffffffffffffffffffffffffffffffff821680920361134c576044359173ffffffffffffffffffffffffffffffffffffffff831680930361134c5760643562ffffff81169081810361134c576084359462ffffff86169283870361134c577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549460ff8660401c16159767ffffffffffffffff871680159081611844575b600114908161183a575b159081611831575b50611809577fdeeec7d2d22b446e217440535fb1375e98be303a3260190c2d1e1b28a3c57f6560207fffffffffffff000000ffffff00000000000000000000000000000000000000009479ffffff0000000000000000000000000000000000000000000000947f2d36e1826c285c959501ff9750ab5c3202a197bd16d557e1251811bacfb0b5549b8b8e60017fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000879f16177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00556117b4575b5061161261309f565b61161a61309f565b611623336128e6565b61162b61309f565b7fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001557fffffffffffffffffffffffff0000000000000000000000000000000000000000600254161760025560045497877fffffffffffffffffffffffff00000000000000000000000000000000000000008a16176004556116b16129d3565b7fffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffff78ffffff000000000000000000000000000000000000000000006003549260b01b16911617600355604051908152a16117096129d3565b60b81b1692161717600455604051908152a161172157005b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b7fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00558e611609565b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050158a611531565b303b159150611529565b8a915061151f565b3461134c575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c576118826129d3565b5f73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300547fffffffffffffffffffffffff000000000000000000000000000000000000000081167f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c577f2d36e1826c285c959501ff9750ab5c3202a197bd16d557e1251811bacfb0b554602061198061237b565b6119886129d3565b6004547fffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffff79ffffff00000000000000000000000000000000000000000000008360b81b1691161760045562ffffff60405191168152a1005b3461134c575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003e596ed277dd2aef069380e6021da59f4bfddc13163003611a755760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b7fe07c8dba000000000000000000000000000000000000000000000000000000005f5260045ffd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c57611acf612450565b6024359067ffffffffffffffff821161134c573660238301121561134c57816004013590611afc826124fd565b91611b0a60405193846124bc565b8083526020830193366024838301011161134c57815f9260246020930187378401015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003e596ed277dd2aef069380e6021da59f4bfddc1316803014908115611dbf575b50611a7557611b7c6129d3565b73ffffffffffffffffffffffffffffffffffffffff8116926040517f52d1902d000000000000000000000000000000000000000000000000000000008152602081600481885afa5f9181611d8b575b50611bfc57847f4c9c8ce3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc869203611d605750823b15611d3557807fffffffffffffffffffffffff00000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2825115611d03575f8091611cf9945190845af43d15611cfb573d91611cdd836124fd565b92611ceb60405194856124bc565b83523d5f602085013e6131ce565b005b6060916131ce565b50505034611d0d57005b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4c9c8ce3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7faa1d49a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011611db7575b81611da7602093836124bc565b8101031261134c57519086611bcb565b3d9150611d9a565b905073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141584611b6f565b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043573ffffffffffffffffffffffffffffffffffffffff811680910361134c57611e596129d3565b7fffffffffffffffffffffffff000000000000000000000000000000000000000060035416176003555f80f35b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043567ffffffffffffffff811161134c57611ed590369060040161241f565b611edd6129d3565b5f5b818110611f1d57507ff189929725290201c3e718cac3495edb1e4cbac62f7b71482bef2fd58c27195d91611f186040519283928361268b565b0390a1005b61ffff611f2e61049b83858761267b565b1690815f52600560205260405f205415611f58576001915f5260056020525f604081205501611edf565b7f10e067e0000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5761ffff611fba6123ff565b5f6060604051611fc981612473565b8281528260208201528260408201520152165f525f602052608060405f2060405190611ff482612473565b5464ffffffffff69ffffffffffffffffffff82169283815269ffffffffffffffffffff60208201818560501c16815260ff60606040850194868860a01c168652019560c81c161515855260405195865251166020850152511660408301525115156060820152f35b3461134c5761206a3661238d565b906120736129d3565b5f5b8281106120ad576040517ff29d3629e86628beaba81304b186def19003218376a3f0bcde417deb22a9a57a9080611f18868683612614565b61ffff6120be61049b8386866125ab565b165f52600560205260405f205415611f5857806fffffffffffffffffffffffffffffffff6120fa60206120f460019588886125ab565b016125f7565b1661ffff61210c61049b8488886125ab565b165f52600560205260405f205501612075565b3461134c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c577fdeeec7d2d22b446e217440535fb1375e98be303a3260190c2d1e1b28a3c57f65602061217961237b565b6121816129d3565b6003547fffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffff78ffffff000000000000000000000000000000000000000000008360b01b1691161760035562ffffff60405191168152a1005b3461134c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760043560ff81169081810361134c576024359160ff83169182840361134c576044359260ff84169182850361134c5761223e6129d3565b606460ff612255876122508a8661256a565b61256a565b1603612313577fb4ae64ee51c0d84410a16847b3f8f3d7ac7bbbeaf2abae8671beba816f0279e39575ff0000000000000000000000000000000000000000006060967fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000076ff000000000000000000000000000000000000000000006004549360b01b169560a01b169116179160a81b16171760045560405192835260208301526040820152a1005b7f32de0eb6000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461134c575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261134c5760209062ffffff60035460b01c168152f35b6004359062ffffff8216820361134c57565b9060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261134c5760043567ffffffffffffffff811161134c578260238201121561134c5780600401359267ffffffffffffffff841161134c5760248460061b8301011161134c576024019190565b6004359061ffff8216820361134c57565b359061ffff8216820361134c57565b9181601f8401121561134c5782359167ffffffffffffffff831161134c576020808501948460051b01011161134c57565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361134c57565b6080810190811067ffffffffffffffff82111761248f57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761248f57604052565b67ffffffffffffffff811161248f57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b90602080835192838152019201905f5b8181106125545750505090565b8251845260209384019390920191600101612547565b9060ff8091169116019060ff821161257e57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908110156125bb5760061b0190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b3561ffff8116810361134c5790565b356fffffffffffffffffffffffffffffffff8116810361134c5790565b60208082528101839052604001915f905b8082106126325750505090565b90919261ffff61264185612410565b1681526020840135906fffffffffffffffffffffffffffffffff821680920361134c57604081600193602083940152019401920190612625565b91908110156125bb5760051b0190565b60208082528101839052604001915f5b8181106126a85750505090565b90919260208060019261ffff6126bd88612410565b16815201940192910161269b565b67ffffffffffffffff811161248f5760051b60200190565b906126ed826126cb565b6126fa60405191826124bc565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061272882946126cb565b0190602036910137565b8181029291811591840414171561257e57565b9190820180921161257e57565b8051156125bb5760200190565b8051600110156125bb5760400190565b8051600210156125bb5760600190565b80518210156125bb5760209160051b010190565b1561279c575050565b7f85d8dc79000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b90918281527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831161134c5760209260051b809284830137010190565b9391612849959373ffffffffffffffffffffffffffffffffffffffff61283b931686526060602087015260608601916127ca565b9260408185039101526127ca565b90565b9392909373ffffffffffffffffffffffffffffffffffffffff606082019516815260606020820152825180955260206080820193015f955b8087106128a05750506128499394506040818403910152612537565b909360208060019273ffffffffffffffffffffffffffffffffffffffff885116815201950196019590612884565b9081602091031261134c5751801515810361134c5790565b73ffffffffffffffffffffffffffffffffffffffff1680156129a75773ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054827fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b7f1e4fbdf7000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054163303612a1357565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b8115612a49570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b6024602073ffffffffffffffffffffffffffffffffffffffff60025416604051928380927f1dfce5c60000000000000000000000000000000000000000000000000000000082523060048301525afa8015611341575f90612ae5575b6128499150612adf6130f6565b90612a3f565b506020813d602011612b11575b81612aff602093836124bc565b8101031261134c576128499051612ad2565b3d9150612af2565b919061ffff5f9316805f525f60205260405f2060035490604051927fbd85b039000000000000000000000000000000000000000000000000000000008452600484015260208360248173ffffffffffffffffffffffffffffffffffffffff86165afa928315611341575f93612c19575b505492612b9f64ffffffffff8560a01c1661319f565b15612bfc5782612bd8575b5062ffffff905b60b01c1611908115612bca575b50612bc557565b5f9150565b60ff915060c81c165f612bbe565b62ffffff919550612bf48369ffffffffffffffffffff92612a3f565b169490612baa565b50935062ffffff69ffffffffffffffffffff8360501c1694612bb1565b9092506020813d602011612c45575b81612c35602093836124bc565b8101031261134c5751915f612b89565b3d9150612c28565b9190820391821161257e57565b90815f52600560205260405f2054838115918215612fc5575b505015612f99576024602073ffffffffffffffffffffffffffffffffffffffff60035416604051928380927f19e7f0460000000000000000000000000000000000000000000000000000000082528760048301525afa8015611341575f90612f65575b612cec915062ffffff60045460b81c1690612745565b4210612f3d57815f525f60205260405f209283549060ff8260c81c16612f1157612d1f64ffffffffff8360a01c1661319f565b15612efb576024612d2e6130f6565b602073ffffffffffffffffffffffffffffffffffffffff60025416604051938480927f1dfce5c60000000000000000000000000000000000000000000000000000000082523060048301525afa8015611341575f90612ec7575b612d929250612a3f565b926201518042046201518081029080820462015180149015171561257e57612e4c937fffffffffffffff000000000000000000000000000000ffffffffffffffffffff78ffffffffff000000000000000000000000000000000000000073ffffffffffffffffffff000000000000000000008660501b169360a01b169116171786557fa86a2ea2409eb9a5a366acc2656446a90f9a8f4b7694ac183718898663bb292a6040805161ffff88168152866020820152a1612732565b91828210612e94575069ffffffffffffffffffff91612e6a91612c4d565b167fffffffffffffffffffffffffffffffffffffffffffff00000000000000000000825416179055565b82907f4fb574cf000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b506020823d602011612ef3575b81612ee1602093836124bc565b8101031261134c57612d929151612d88565b3d9150612ed4565b69ffffffffffffffffffff612e4c921692612732565b837f3fb251d7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f22cfd44d000000000000000000000000000000000000000000000000000000005f5260045ffd5b506020813d602011612f91575b81612f7f602093836124bc565b8101031261134c57612cec9051612cd6565b3d9150612f72565b507f58a2d6dc000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b10159050835f612c73565b9060405190612fe06080836124bc565b600382526060918236602083013761309c73ffffffffffffffffffffffffffffffffffffffff8295604051956130176080886124bc565b600387523660208801378561307a6064613073600454948261303f60ff8860a01c1683612732565b0461304986612752565b528261305b60ff8860a81c1683612732565b046130658661275f565b5260ff8660b01c1690612732565b049161276f565b525f61308585612752565b5281600254166130948561275f565b52169161276f565b52565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c16156130ce57565b7fd7e6bcf8000000000000000000000000000000000000000000000000000000005f5260045ffd5b600354604051907f18160ddd00000000000000000000000000000000000000000000000000000000825260208260048173ffffffffffffffffffffffffffffffffffffffff85165afa918215611341575f92613169575b5060a01c61ffff16818110613160575090565b61284991612c4d565b9091506020813d602011613197575b81613185602093836124bc565b8101031261134c57519061ffff61314d565b3d9150613178565b6201518042046201518081029080820462015180149015171561257e5762015180820180921161257e57101590565b9061320b57508051156131e357805190602001fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b8151158061325e575b61321c575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b1561321456fea264697066735822122043daf9266a348737d6ff0b6acebbb0e92dd2e78d08c9aa3842b4f02a93cb5b7e64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.