Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
CollectionLib
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 800 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "openzeppelin-contracts/contracts/utils/math/Math.sol"; import "openzeppelin-contracts/contracts/utils/math/SafeCast.sol"; import "../library/RollingBuckets.sol"; import "../library/ERC721Transfer.sol"; import "../Errors.sol"; import "../Constants.sol"; import "./User.sol"; import "./Helper.sol"; import {SafeBox, CollectionState, AuctionInfo, CollectionAccount, UserFloorAccount, LockParam} from "./Structs.sol"; import {SafeBoxLib} from "./SafeBox.sol"; import "../interface/IFlooring.sol"; library CollectionLib { using SafeBoxLib for SafeBox; using SafeCast for uint256; using RollingBuckets for mapping(uint256 => uint256); using UserLib for CollectionAccount; using UserLib for UserFloorAccount; event LockNft( address indexed sender, address indexed onBehalfOf, address indexed collection, uint256[] tokenIds, uint256[] safeBoxKeys, uint256 safeBoxExpiryTs, uint256 minMaintCredit, address proxyCollection ); event ExtendKey( address indexed operator, address indexed collection, uint256[] tokenIds, uint256[] safeBoxKeys, uint256 safeBoxExpiryTs, uint256 minMaintCredit ); event UnlockNft( address indexed operator, address indexed receiver, address indexed collection, uint256[] tokenIds, address proxyCollection ); event RemoveExpiredKey( address indexed operator, address indexed onBehalfOf, address indexed collection, uint256[] tokenIds, uint256[] safeBoxKeys ); event ExpiredNftToVault(address indexed operator, address indexed collection, uint256[] tokenIds); event FragmentNft( address indexed operator, address indexed onBehalfOf, address indexed collection, uint256[] tokenIds ); event ClaimRandomNft( address indexed operator, address indexed receiver, address indexed collection, uint256[] tokenIds, uint256 creditCost ); function fragmentNFTs( CollectionState storage collectionState, address collection, uint256[] memory nftIds, address onBehalfOf ) public { uint256 nftLen = nftIds.length; unchecked { for (uint256 i; i < nftLen; ++i) { collectionState.freeTokenIds.push(nftIds[i]); } } collectionState.floorToken.mint(onBehalfOf, Constants.FLOOR_TOKEN_AMOUNT * nftLen); ERC721Transfer.safeBatchTransferFrom(collection, msg.sender, address(this), nftIds); emit FragmentNft(msg.sender, onBehalfOf, collection, nftIds); } struct LockInfo { bool isInfinite; uint256 currentBucket; uint256 newExpiryBucket; uint256 totalManaged; uint256 newRequireLockCredit; uint64 infiniteCnt; } function lockNfts( CollectionState storage collection, UserFloorAccount storage account, LockParam memory param, address onBehalfOf ) public returns (uint256 totalCreditCost) { if (onBehalfOf == address(this)) revert Errors.InvalidParam(); /// proxy collection only enabled when infinity lock if (param.collection != param.proxyCollection && param.expiryTs != 0) revert Errors.InvalidParam(); uint8 vipLevel = uint8(param.vipLevel); uint256 totalCredit = account.ensureVipCredit(vipLevel, param.creditToken); Helper.ensureMaxLocking(collection, vipLevel, param.expiryTs, param.nftIds.length, false); { uint8 maxVipLevel = Constants.getVipLevel(totalCredit); uint256 newLocked = param.nftIds.length; Helper.ensureProxyVipLevel(maxVipLevel, param.collection != param.proxyCollection); Helper.checkAndUpdateSafeboxPeriodQuota(account, maxVipLevel, newLocked.toUint16()); Helper.checkSafeboxUserQuota(account, vipLevel, newLocked); } /// cache value to avoid multi-reads uint256 minMaintCredit = account.minMaintCredit; uint256[] memory nftIds = param.nftIds; uint256[] memory newKeys; { CollectionAccount storage userCollectionAccount = account.getOrAddCollection(param.collection); (totalCreditCost, newKeys) = _lockNfts(collection, userCollectionAccount, nftIds, param.expiryTs, vipLevel); // compute max credit for locking cost uint96 totalLockingCredit = userCollectionAccount.totalLockingCredit; { uint256 creditBuffer; unchecked { creditBuffer = totalCredit - totalLockingCredit; } if (totalCreditCost > creditBuffer || totalCreditCost > param.maxCreditCost) { revert Errors.InsufficientCredit(); } } totalLockingCredit += totalCreditCost.toUint96(); userCollectionAccount.totalLockingCredit = totalLockingCredit; if (totalLockingCredit > minMaintCredit) { account.minMaintCredit = totalLockingCredit; minMaintCredit = totalLockingCredit; } } account.updateVipKeyCount(vipLevel, int256(nftIds.length)); /// mint for `onBehalfOf`, transfer from msg.sender collection.floorToken.mint(onBehalfOf, Constants.FLOOR_TOKEN_AMOUNT * nftIds.length); ERC721Transfer.safeBatchTransferFrom(param.proxyCollection, msg.sender, address(this), nftIds); emit LockNft( msg.sender, onBehalfOf, param.collection, nftIds, newKeys, param.expiryTs, minMaintCredit, param.proxyCollection ); } function _lockNfts( CollectionState storage collectionState, CollectionAccount storage account, uint256[] memory nftIds, uint256 expiryTs, // treat 0 as infinite lock. uint8 vipLevel ) private returns (uint256, uint256[] memory) { LockInfo memory info = LockInfo({ isInfinite: expiryTs == 0, currentBucket: Helper.counterStamp(block.timestamp), newExpiryBucket: Helper.counterStamp(expiryTs), totalManaged: collectionState.activeSafeBoxCnt + collectionState.freeTokenIds.length, newRequireLockCredit: 0, infiniteCnt: collectionState.infiniteCnt }); if (info.isInfinite) { /// if it is infinite lock, we need load all buckets to calculate the staking cost info.newExpiryBucket = Helper.counterStamp(block.timestamp + Constants.MAX_LOCKING_PERIOD); } uint256[] memory buckets = Helper.prepareBucketUpdate(collectionState, info.currentBucket, info.newExpiryBucket); /// @dev `keys` used to log info, we just compact its fields into one 256 bits number uint256[] memory keys = new uint256[](nftIds.length); for (uint256 idx; idx < nftIds.length;) { uint256 lockedCredit = updateCountersAndGetSafeboxCredit(buckets, info, vipLevel); if (info.isInfinite) ++info.infiniteCnt; SafeBoxKey memory key = SafeBoxKey({ keyId: Helper.generateNextKeyId(collectionState), lockingCredit: lockedCredit.toUint96(), vipLevel: vipLevel }); account.addSafeboxKey(nftIds[idx], key); addSafeBox( collectionState, nftIds[idx], SafeBox({keyId: key.keyId, expiryTs: uint32(expiryTs), owner: msg.sender}) ); keys[idx] = SafeBoxLib.encodeSafeBoxKey(key); info.newRequireLockCredit += lockedCredit; unchecked { ++info.totalManaged; ++idx; } } if (info.isInfinite) { collectionState.infiniteCnt = info.infiniteCnt; } else { collectionState.countingBuckets.batchSet(info.currentBucket, buckets); if (info.newExpiryBucket > collectionState.lastUpdatedBucket) { collectionState.lastUpdatedBucket = uint64(info.newExpiryBucket); } } return (info.newRequireLockCredit, keys); } function unlockNfts( CollectionState storage collection, UserFloorAccount storage userAccount, address proxyCollection, address collectionId, uint256[] memory nftIds, uint256 maxExpiryTs, address receiver ) public { CollectionAccount storage userCollectionAccount = userAccount.getByKey(collectionId); SafeBoxKey[] memory releasedKeys = _unlockNfts(collection, maxExpiryTs, nftIds, userCollectionAccount); for (uint256 i = 0; i < releasedKeys.length;) { userAccount.updateVipKeyCount(releasedKeys[i].vipLevel, -1); unchecked { ++i; } } /// @dev if the receiver is the contract self, then unlock the safeboxes and dump the NFTs to the vault if (receiver == address(this)) { uint256 nftLen = nftIds.length; for (uint256 i; i < nftLen;) { collection.freeTokenIds.push(nftIds[i]); unchecked { ++i; } } emit FragmentNft(msg.sender, msg.sender, collectionId, nftIds); } else { collection.floorToken.burn(msg.sender, Constants.FLOOR_TOKEN_AMOUNT * nftIds.length); ERC721Transfer.safeBatchTransferFrom(proxyCollection, address(this), receiver, nftIds); } emit UnlockNft(msg.sender, receiver, collectionId, nftIds, proxyCollection); } function _unlockNfts( CollectionState storage collectionState, uint256 maxExpiryTs, uint256[] memory nftIds, CollectionAccount storage userCollectionAccount ) private returns (SafeBoxKey[] memory) { if (maxExpiryTs > 0 && maxExpiryTs < block.timestamp) revert Errors.SafeBoxHasExpire(); SafeBoxKey[] memory expiredKeys = new SafeBoxKey[](nftIds.length); uint256 currentBucketTime = Helper.counterStamp(block.timestamp); uint256 creditToRelease = 0; uint256[] memory buckets; /// if maxExpiryTs == 0, it means all nftIds in this batch being locked infinitely that we don't need to update countingBuckets if (maxExpiryTs > 0) { uint256 maxExpiryBucketTime = Math.min(Helper.counterStamp(maxExpiryTs), collectionState.lastUpdatedBucket); buckets = collectionState.countingBuckets.batchGet(currentBucketTime, maxExpiryBucketTime); } for (uint256 i; i < nftIds.length;) { uint256 nftId = nftIds[i]; if (Helper.hasActiveActivities(collectionState, nftId)) revert Errors.NftHasActiveActivities(); (SafeBox storage safeBox, SafeBoxKey storage safeBoxKey) = Helper.useSafeBoxAndKey(collectionState, userCollectionAccount, nftId); creditToRelease += safeBoxKey.lockingCredit; if (safeBox.isInfiniteSafeBox()) { --collectionState.infiniteCnt; } else { uint256 limit = Helper.counterStamp(safeBox.expiryTs) - currentBucketTime; if (limit > buckets.length) revert(); for (uint256 idx; idx < limit;) { --buckets[idx]; unchecked { ++idx; } } } expiredKeys[i] = safeBoxKey; removeSafeBox(collectionState, nftId); userCollectionAccount.removeSafeboxKey(nftId); unchecked { ++i; } } userCollectionAccount.totalLockingCredit -= creditToRelease.toUint96(); if (buckets.length > 0) { collectionState.countingBuckets.batchSet(currentBucketTime, buckets); } return expiredKeys; } function extendLockingForKeys( CollectionState storage collection, UserFloorAccount storage userAccount, LockParam memory param ) public returns (uint256 totalCreditCost) { uint8 newVipLevel = uint8(param.vipLevel); uint256 totalCredit = userAccount.ensureVipCredit(newVipLevel, param.creditToken); Helper.ensureMaxLocking(collection, newVipLevel, param.expiryTs, param.nftIds.length, true); uint256 minMaintCredit = userAccount.minMaintCredit; uint256[] memory safeBoxKeys; { CollectionAccount storage collectionAccount = userAccount.getOrAddCollection(param.collection); // extend lock duration int256[] memory vipLevelDiffs; (vipLevelDiffs, totalCreditCost, safeBoxKeys) = _extendLockingForKeys(collection, collectionAccount, param.nftIds, param.expiryTs, uint8(newVipLevel)); // compute max credit for locking cost uint96 totalLockingCredit = collectionAccount.totalLockingCredit; { uint256 creditBuffer; unchecked { creditBuffer = totalCredit - totalLockingCredit; } if (totalCreditCost > creditBuffer || totalCreditCost > param.maxCreditCost) { revert Errors.InsufficientCredit(); } } // update user vip key counts for (uint256 vipLevel = 0; vipLevel < vipLevelDiffs.length;) { userAccount.updateVipKeyCount(uint8(vipLevel), vipLevelDiffs[vipLevel]); unchecked { ++vipLevel; } } totalLockingCredit += totalCreditCost.toUint96(); collectionAccount.totalLockingCredit = totalLockingCredit; if (totalLockingCredit > minMaintCredit) { userAccount.minMaintCredit = totalLockingCredit; minMaintCredit = totalLockingCredit; } } emit ExtendKey(msg.sender, param.collection, param.nftIds, safeBoxKeys, param.expiryTs, minMaintCredit); } function _extendLockingForKeys( CollectionState storage collectionState, CollectionAccount storage userCollectionAccount, uint256[] memory nftIds, uint256 newExpiryTs, // expiryTs of 0 is infinite. uint8 newVipLevel ) private returns (int256[] memory, uint256, uint256[] memory) { LockInfo memory info = LockInfo({ isInfinite: newExpiryTs == 0, currentBucket: Helper.counterStamp(block.timestamp), newExpiryBucket: Helper.counterStamp(newExpiryTs), totalManaged: collectionState.activeSafeBoxCnt + collectionState.freeTokenIds.length, newRequireLockCredit: 0, infiniteCnt: collectionState.infiniteCnt }); if (info.isInfinite) { info.newExpiryBucket = Helper.counterStamp(block.timestamp + Constants.MAX_LOCKING_PERIOD); } uint256[] memory buckets = Helper.prepareBucketUpdate(collectionState, info.currentBucket, info.newExpiryBucket); int256[] memory vipLevelDiffs = new int256[](Constants.VIP_LEVEL_COUNT); /// @dev `keys` used to log info, we just compact its fields into one 256 bits number uint256[] memory keys = new uint256[](nftIds.length); for (uint256 idx; idx < nftIds.length;) { if (Helper.hasActiveActivities(collectionState, nftIds[idx])) revert Errors.NftHasActiveActivities(); (SafeBox storage safeBox, SafeBoxKey storage safeBoxKey) = Helper.useSafeBoxAndKey(collectionState, userCollectionAccount, nftIds[idx]); { uint256 extendOffset = Helper.counterStamp(safeBox.expiryTs) - info.currentBucket; unchecked { for (uint256 i; i < extendOffset; ++i) { if (buckets[i] == 0) revert Errors.InvalidParam(); --buckets[i]; } } } uint256 safeboxQuote = updateCountersAndGetSafeboxCredit(buckets, info, newVipLevel); if (safeboxQuote > safeBoxKey.lockingCredit) { info.newRequireLockCredit += (safeboxQuote - safeBoxKey.lockingCredit); safeBoxKey.lockingCredit = safeboxQuote.toUint96(); } uint8 oldVipLevel = safeBoxKey.vipLevel; if (newVipLevel > oldVipLevel) { safeBoxKey.vipLevel = newVipLevel; --vipLevelDiffs[oldVipLevel]; ++vipLevelDiffs[newVipLevel]; } if (info.isInfinite) { safeBox.expiryTs = 0; ++info.infiniteCnt; } else { safeBox.expiryTs = uint32(newExpiryTs); } keys[idx] = SafeBoxLib.encodeSafeBoxKey(safeBoxKey); unchecked { ++idx; } } if (info.isInfinite) { collectionState.infiniteCnt = info.infiniteCnt; } else { collectionState.countingBuckets.batchSet(info.currentBucket, buckets); if (info.newExpiryBucket > collectionState.lastUpdatedBucket) { collectionState.lastUpdatedBucket = uint64(info.newExpiryBucket); } } return (vipLevelDiffs, info.newRequireLockCredit, keys); } function updateCountersAndGetSafeboxCredit(uint256[] memory counters, LockInfo memory lockInfo, uint8 vipLevel) private pure returns (uint256 result) { unchecked { uint256 infiniteCnt = lockInfo.infiniteCnt; uint256 totalManaged = lockInfo.totalManaged; uint256 counterOffsetEnd = (counters.length + 1) * 0x20; uint256 tmpCount; if (lockInfo.isInfinite) { for (uint256 i = 0x20; i < counterOffsetEnd; i += 0x20) { assembly { tmpCount := mload(add(counters, i)) } result += Constants.getRequiredStakingForLockRatio(infiniteCnt + tmpCount, totalManaged); } } else { for (uint256 i = 0x20; i < counterOffsetEnd; i += 0x20) { assembly { tmpCount := mload(add(counters, i)) } result += Constants.getRequiredStakingForLockRatio(infiniteCnt + tmpCount, totalManaged); assembly { /// increase counters[i] mstore(add(counters, i), add(tmpCount, 1)) } } result = Constants.getVipRequiredStakingWithDiscount(result, vipLevel); } } } function removeExpiredKeysAndRestoreCredits( CollectionState storage collectionState, UserFloorAccount storage userAccount, address collectionId, uint256[] memory nftIds, address onBehalfOf ) public returns (uint256 releasedCredit) { CollectionAccount storage collectionAccount = userAccount.getByKey(collectionId); uint256 removedCnt; uint256[] memory removedIds = new uint256[](nftIds.length); uint256[] memory removedKeys = new uint256[](nftIds.length); for (uint256 i = 0; i < nftIds.length;) { uint256 nftId = nftIds[i]; SafeBoxKey memory safeBoxKey = collectionAccount.getByKey(nftId); SafeBox memory safeBox = collectionState.safeBoxes[nftId]; if (safeBoxKey.keyId == 0) { revert Errors.InvalidParam(); } if (safeBox._isSafeBoxExpired() || !safeBox._isKeyMatchingSafeBox(safeBoxKey)) { removedIds[removedCnt] = nftId; removedKeys[removedCnt] = SafeBoxLib.encodeSafeBoxKey(safeBoxKey); unchecked { ++removedCnt; releasedCredit += safeBoxKey.lockingCredit; } userAccount.updateVipKeyCount(safeBoxKey.vipLevel, -1); collectionAccount.removeSafeboxKey(nftId); } unchecked { ++i; } } if (releasedCredit > 0) { collectionAccount.totalLockingCredit -= releasedCredit.toUint96(); } emit RemoveExpiredKey(msg.sender, onBehalfOf, collectionId, removedIds, removedKeys); } function tidyExpiredNFTs(CollectionState storage collection, uint256[] memory nftIds, address collectionId) public { uint256 nftLen = nftIds.length; for (uint256 i; i < nftLen;) { uint256 nftId = nftIds[i]; SafeBox storage safeBox = Helper.useSafeBox(collection, nftId); if (!safeBox.isSafeBoxExpired()) revert Errors.SafeBoxHasNotExpire(); if (!Helper.isAuctionPeriodOver(safeBox)) revert Errors.AuctionHasNotCompleted(); /// remove expired safebox, and dump it to vault removeSafeBox(collection, nftId); collection.freeTokenIds.push(nftId); unchecked { ++i; } } emit ExpiredNftToVault(msg.sender, collectionId, nftIds); } function claimRandomNFT( CollectionState storage collection, mapping(address => UserFloorAccount) storage userAccounts, address creditToken, address collectionId, uint256 claimCnt, uint256 maxCreditCost, address receiver ) public returns (uint256 totalCreditCost) { if (claimCnt == 0 || collection.freeTokenIds.length < claimCnt) revert Errors.ClaimableNftInsufficient(); uint256 freeAmount = collection.freeTokenIds.length; uint256 totalManaged = collection.activeSafeBoxCnt + freeAmount; /// when locking ratio greater than xx%, stop random redemption if ( Helper.calculateLockingRatioRaw(freeAmount - claimCnt, totalManaged - claimCnt) >= Constants.VAULT_REDEMPTION_MAX_LOKING_RATIO ) { revert Errors.ClaimableNftInsufficient(); } uint256[] memory selectedTokenIds = new uint256[](claimCnt); UserFloorAccount storage userAccount = userAccounts[msg.sender]; while (claimCnt > 0) { totalCreditCost += Constants.getClaimCost(Helper.calculateLockingRatioRaw(freeAmount, totalManaged)); /// just compute a deterministic random number uint256 chosenNftIdx = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, totalManaged))) % collection.freeTokenIds.length; unchecked { --claimCnt; --totalManaged; --freeAmount; } selectedTokenIds[claimCnt] = collection.freeTokenIds[chosenNftIdx]; collection.freeTokenIds[chosenNftIdx] = collection.freeTokenIds[collection.freeTokenIds.length - 1]; collection.freeTokenIds.pop(); } { /// calculate cost with waiver quota uint8 vipLevel = Constants.getVipLevel(userAccount.tokenBalance(creditToken)); uint96 waiverUsed = Helper.updateUserCreditWaiver(userAccount); (totalCreditCost, userAccount.creditWaiverUsed) = Constants.getVipClaimCostWithDiscount(totalCreditCost, vipLevel, waiverUsed); } if (totalCreditCost > maxCreditCost) { revert Errors.InsufficientCredit(); } userAccount.transferToken(userAccounts[address(this)], creditToken, totalCreditCost, true); collection.floorToken.burn(msg.sender, Constants.FLOOR_TOKEN_AMOUNT * selectedTokenIds.length); ERC721Transfer.safeBatchTransferFrom(collectionId, address(this), receiver, selectedTokenIds); emit ClaimRandomNft(msg.sender, receiver, collectionId, selectedTokenIds, totalCreditCost); } function getLockingBuckets(CollectionState storage collection, uint256 startTimestamp, uint256 endTimestamp) public view returns (uint256[] memory) { return Helper.prepareBucketUpdate( collection, Helper.counterStamp(startTimestamp), Math.min(collection.lastUpdatedBucket, Helper.counterStamp(endTimestamp)) ); } function addSafeBox(CollectionState storage collectionState, uint256 nftId, SafeBox memory safebox) internal { if (collectionState.safeBoxes[nftId].keyId > 0) revert Errors.SafeBoxAlreadyExist(); collectionState.safeBoxes[nftId] = safebox; ++collectionState.activeSafeBoxCnt; } function removeSafeBox(CollectionState storage collectionState, uint256 nftId) internal { delete collectionState.safeBoxes[nftId]; --collectionState.activeSafeBoxCnt; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.20; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be * reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.20; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeCast { /** * @dev Value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); /** * @dev An int value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedIntToUint(int256 value); /** * @dev Value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); /** * @dev An uint value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedUintToInt(uint256 value); /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits */ function toUint248(uint256 value) internal pure returns (uint248) { if (value > type(uint248).max) { revert SafeCastOverflowedUintDowncast(248, value); } return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits */ function toUint240(uint256 value) internal pure returns (uint240) { if (value > type(uint240).max) { revert SafeCastOverflowedUintDowncast(240, value); } return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits */ function toUint232(uint256 value) internal pure returns (uint232) { if (value > type(uint232).max) { revert SafeCastOverflowedUintDowncast(232, value); } return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits */ function toUint224(uint256 value) internal pure returns (uint224) { if (value > type(uint224).max) { revert SafeCastOverflowedUintDowncast(224, value); } return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits */ function toUint216(uint256 value) internal pure returns (uint216) { if (value > type(uint216).max) { revert SafeCastOverflowedUintDowncast(216, value); } return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits */ function toUint208(uint256 value) internal pure returns (uint208) { if (value > type(uint208).max) { revert SafeCastOverflowedUintDowncast(208, value); } return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits */ function toUint200(uint256 value) internal pure returns (uint200) { if (value > type(uint200).max) { revert SafeCastOverflowedUintDowncast(200, value); } return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits */ function toUint192(uint256 value) internal pure returns (uint192) { if (value > type(uint192).max) { revert SafeCastOverflowedUintDowncast(192, value); } return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits */ function toUint184(uint256 value) internal pure returns (uint184) { if (value > type(uint184).max) { revert SafeCastOverflowedUintDowncast(184, value); } return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits */ function toUint176(uint256 value) internal pure returns (uint176) { if (value > type(uint176).max) { revert SafeCastOverflowedUintDowncast(176, value); } return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits */ function toUint168(uint256 value) internal pure returns (uint168) { if (value > type(uint168).max) { revert SafeCastOverflowedUintDowncast(168, value); } return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits */ function toUint160(uint256 value) internal pure returns (uint160) { if (value > type(uint160).max) { revert SafeCastOverflowedUintDowncast(160, value); } return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits */ function toUint152(uint256 value) internal pure returns (uint152) { if (value > type(uint152).max) { revert SafeCastOverflowedUintDowncast(152, value); } return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits */ function toUint144(uint256 value) internal pure returns (uint144) { if (value > type(uint144).max) { revert SafeCastOverflowedUintDowncast(144, value); } return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits */ function toUint136(uint256 value) internal pure returns (uint136) { if (value > type(uint136).max) { revert SafeCastOverflowedUintDowncast(136, value); } return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { if (value > type(uint128).max) { revert SafeCastOverflowedUintDowncast(128, value); } return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits */ function toUint120(uint256 value) internal pure returns (uint120) { if (value > type(uint120).max) { revert SafeCastOverflowedUintDowncast(120, value); } return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits */ function toUint112(uint256 value) internal pure returns (uint112) { if (value > type(uint112).max) { revert SafeCastOverflowedUintDowncast(112, value); } return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits */ function toUint104(uint256 value) internal pure returns (uint104) { if (value > type(uint104).max) { revert SafeCastOverflowedUintDowncast(104, value); } return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { if (value > type(uint96).max) { revert SafeCastOverflowedUintDowncast(96, value); } return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits */ function toUint88(uint256 value) internal pure returns (uint88) { if (value > type(uint88).max) { revert SafeCastOverflowedUintDowncast(88, value); } return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits */ function toUint80(uint256 value) internal pure returns (uint80) { if (value > type(uint80).max) { revert SafeCastOverflowedUintDowncast(80, value); } return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits */ function toUint72(uint256 value) internal pure returns (uint72) { if (value > type(uint72).max) { revert SafeCastOverflowedUintDowncast(72, value); } return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { if (value > type(uint64).max) { revert SafeCastOverflowedUintDowncast(64, value); } return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits */ function toUint56(uint256 value) internal pure returns (uint56) { if (value > type(uint56).max) { revert SafeCastOverflowedUintDowncast(56, value); } return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits */ function toUint48(uint256 value) internal pure returns (uint48) { if (value > type(uint48).max) { revert SafeCastOverflowedUintDowncast(48, value); } return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { if (value > type(uint40).max) { revert SafeCastOverflowedUintDowncast(40, value); } return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { if (value > type(uint32).max) { revert SafeCastOverflowedUintDowncast(32, value); } return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits */ function toUint24(uint256 value) internal pure returns (uint24) { if (value > type(uint24).max) { revert SafeCastOverflowedUintDowncast(24, value); } return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { if (value > type(uint16).max) { revert SafeCastOverflowedUintDowncast(16, value); } return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits */ function toUint8(uint256 value) internal pure returns (uint8) { if (value > type(uint8).max) { revert SafeCastOverflowedUintDowncast(8, value); } return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { if (value < 0) { revert SafeCastOverflowedIntToUint(value); } return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(248, value); } } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(240, value); } } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(232, value); } } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(224, value); } } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(216, value); } } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(208, value); } } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(200, value); } } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(192, value); } } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(184, value); } } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(176, value); } } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(168, value); } } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(160, value); } } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(152, value); } } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(144, value); } } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(136, value); } } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(128, value); } } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(120, value); } } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(112, value); } } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(104, value); } } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(96, value); } } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(88, value); } } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(80, value); } } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(72, value); } } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(64, value); } } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(56, value); } } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(48, value); } } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(40, value); } } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(32, value); } } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(24, value); } } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(16, value); } } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(8, value); } } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive if (value > uint256(type(int256).max)) { revert SafeCastOverflowedUintToInt(value); } return int256(value); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; library Constants { /// @notice Flooring protocol /// @dev floor token amount of 1 NFT (with 18 decimals) uint256 public constant FLOOR_TOKEN_AMOUNT = 1_000_000 ether; /// @dev The minimum vip level required to use `proxy collection` uint8 public constant PROXY_COLLECTION_VIP_THRESHOLD = 3; /// @notice Rolling Bucket Constant Conf uint256 public constant BUCKET_SPAN_1 = 259199 seconds; // BUCKET_SPAN minus 1, used for rounding up uint256 public constant BUCKET_SPAN = 3 days; uint256 public constant MAX_LOCKING_BUCKET = 240; uint256 public constant MAX_LOCKING_PERIOD = 720 days; // MAX LOCKING BUCKET * BUCKET_SPAN /// @notice Auction Config uint256 public constant FREE_AUCTION_PERIOD = 24 hours; uint256 public constant AUCTION_INITIAL_PERIODS = 24 hours; uint256 public constant AUCTION_COMPLETE_GRACE_PERIODS = 2 days; /// @dev minimum bid per NFT when someone starts aution on expired safebox uint256 public constant AUCTION_ON_EXPIRED_MINIMUM_BID = 1000 ether; /// @dev minimum bid per NFT when someone starts aution on vault uint256 public constant AUCTION_ON_VAULT_MINIMUM_BID = 10000 ether; /// @dev admin fee charged per NFT when someone starts aution on expired safebox uint256 public constant AUCTION_ON_EXPIRED_SAFEBOX_COST = 0; /// @dev admin fee charged per NFT when owner starts aution on himself safebox uint256 public constant AUCTION_COST = 100 ether; /// @notice Raffle Config uint256 public constant RAFFLE_COST = 500 ether; uint256 public constant RAFFLE_COMPLETE_GRACE_PERIODS = 2 days; /// @notice Private offer Config uint256 public constant PRIVATE_OFFER_DURATION = 24 hours; uint256 public constant PRIVATE_OFFER_COMPLETE_GRACE_DURATION = 2 days; uint256 public constant PRIVATE_OFFER_COST = 0; uint256 public constant ADD_FREE_NFT_REWARD = 0; /// @notice Lock/Unlock config uint256 public constant USER_SAFEBOX_QUOTA_REFRESH_DURATION = 1 days; uint256 public constant USER_REDEMPTION_WAIVER_REFRESH_DURATION = 1 days; uint256 public constant VAULT_REDEMPTION_MAX_LOKING_RATIO = 80; /// @notice Activities Fee Rate /// @notice Fee rate used to distribute funds that collected from Auctions on expired safeboxes. /// these auction would be settled using credit token uint256 public constant FREE_AUCTION_FEE_RATE_BIPS = 2000; // 20% /// @notice Fee rate settled with credit token uint256 public constant CREDIT_FEE_RATE_BIPS = 150; // 2% /// @notice Fee rate settled with specified token uint256 public constant SPEC_FEE_RATE_BIPS = 300; // 3% /// @notice Fee rate settled with all other tokens uint256 public constant COMMON_FEE_RATE_BIPS = 500; // 5% uint256 public constant VIP_LEVEL_COUNT = 8; struct AuctionBidOption { uint256 extendDurationSecs; uint256 minimumRaisePct; uint256 vipLevel; } function getVipLockingBuckets(uint256 vipLevel) internal pure returns (uint256 buckets) { require(vipLevel < VIP_LEVEL_COUNT); assembly { switch vipLevel case 1 { buckets := 1 } case 2 { buckets := 5 } case 3 { buckets := 20 } case 4 { buckets := 60 } case 5 { buckets := 120 } case 6 { buckets := 180 } case 7 { buckets := MAX_LOCKING_BUCKET } } } function getVipLevel(uint256 totalCredit) internal pure returns (uint8) { if (totalCredit < 30_000 ether) { return 0; } else if (totalCredit < 100_000 ether) { return 1; } else if (totalCredit < 300_000 ether) { return 2; } else if (totalCredit < 1_000_000 ether) { return 3; } else if (totalCredit < 3_000_000 ether) { return 4; } else if (totalCredit < 10_000_000 ether) { return 5; } else if (totalCredit < 30_000_000 ether) { return 6; } else { return 7; } } function getVipBalanceRequirements(uint256 vipLevel) internal pure returns (uint256 required) { require(vipLevel < VIP_LEVEL_COUNT); assembly { switch vipLevel case 1 { required := 30000 } case 2 { required := 100000 } case 3 { required := 300000 } case 4 { required := 1000000 } case 5 { required := 3000000 } case 6 { required := 10000000 } case 7 { required := 30000000 } } /// credit token should be scaled with 18 decimals(1 ether == 10**18) unchecked { return required * 1 ether; } } function getBidOption(uint256 idx) internal pure returns (AuctionBidOption memory) { require(idx < 4); AuctionBidOption[4] memory bidOptions = [ AuctionBidOption({extendDurationSecs: 5 minutes, minimumRaisePct: 1, vipLevel: 0}), AuctionBidOption({extendDurationSecs: 8 hours, minimumRaisePct: 10, vipLevel: 3}), AuctionBidOption({extendDurationSecs: 16 hours, minimumRaisePct: 20, vipLevel: 5}), AuctionBidOption({extendDurationSecs: 24 hours, minimumRaisePct: 40, vipLevel: 7}) ]; return bidOptions[idx]; } function raffleDurations(uint256 idx) internal pure returns (uint256 vipLevel, uint256 duration) { require(idx < 6); vipLevel = idx; assembly { switch idx case 1 { duration := 1 } case 2 { duration := 2 } case 3 { duration := 3 } case 4 { duration := 5 } case 5 { duration := 7 } } unchecked { duration *= 1 days; } } /// return locking ratio restrictions indicates that the vipLevel can utility infinite lock NFTs at corresponding ratio function getLockingRatioForInfinite(uint8 vipLevel) internal pure returns (uint256 ratio) { assembly { switch vipLevel case 1 { ratio := 0 } case 2 { ratio := 0 } case 3 { ratio := 20 } case 4 { ratio := 30 } case 5 { ratio := 40 } case 6 { ratio := 50 } case 7 { ratio := 80 } } } /// return locking ratio restrictions indicates that the vipLevel can utility safebox to lock NFTs at corresponding ratio function getLockingRatioForSafebox(uint8 vipLevel) internal pure returns (uint256 ratio) { assembly { switch vipLevel case 1 { ratio := 15 } case 2 { ratio := 25 } case 3 { ratio := 35 } case 4 { ratio := 45 } case 5 { ratio := 55 } case 6 { ratio := 65 } case 7 { ratio := 75 } } } function getVipRequiredStakingWithDiscount(uint256 requiredStaking, uint8 vipLevel) internal pure returns (uint256) { if (vipLevel < 3) { return requiredStaking; } unchecked { /// the higher vip level, more discount for staking /// discount range: 10% - 50% return requiredStaking * (100 - (vipLevel - 2) * 10) / 100; } } function getRequiredStakingForLockRatio(uint256 locked, uint256 totalManaged) internal pure returns (uint256) { if (totalManaged <= 0) { return 1200 ether; } unchecked { uint256 lockingRatioPct = locked * 100 / totalManaged; if (lockingRatioPct <= 40) { return 1200 ether; } else if (lockingRatioPct < 60) { return 1320 ether + ((lockingRatioPct - 40) >> 1) * 120 ether; } else if (lockingRatioPct < 70) { return 2640 ether + ((lockingRatioPct - 60) >> 1) * 240 ether; } else if (lockingRatioPct < 80) { return 4080 ether + ((lockingRatioPct - 70) >> 1) * 480 ether; } else if (lockingRatioPct < 90) { return 6960 ether + ((lockingRatioPct - 80) >> 1) * 960 ether; } else if (lockingRatioPct < 100) { /// 108000 * 2^x return (108000 ether << ((lockingRatioPct - 90) >> 1)) / 5; } else { return 345600 ether; } } } /// @dev returns (costAfterDiscount, quotaUsedAfter) function getVipClaimCostWithDiscount(uint256 cost, uint8 vipLevel, uint96 quotaUsed) internal pure returns (uint256, uint96) { uint96 totalQuota = 1 ether; assembly { switch vipLevel case 0 { totalQuota := mul(0, totalQuota) } case 1 { totalQuota := mul(2000, totalQuota) } case 2 { totalQuota := mul(4000, totalQuota) } case 3 { totalQuota := mul(8000, totalQuota) } case 4 { totalQuota := mul(16000, totalQuota) } case 5 { totalQuota := mul(32000, totalQuota) } case 6 { totalQuota := mul(64000, totalQuota) } case 7 { totalQuota := mul(128000, totalQuota) } } if (totalQuota <= quotaUsed) { return (cost, quotaUsed); } unchecked { totalQuota -= quotaUsed; if (cost < totalQuota) { return (0, uint96(quotaUsed + cost)); } else { return (cost - totalQuota, totalQuota + quotaUsed); } } } function getClaimCost(uint256 lockingRatioPct) internal pure returns (uint256) { if (lockingRatioPct < 40) { return 0; } else { /// 1000 * 2^(0..12) unchecked { return 1000 ether * (2 ** ((lockingRatioPct - 40) / 5)); } } } function getVaultAuctionDurationAtLR(uint256 lockingRatio) internal pure returns (uint256) { if (lockingRatio < 80) return 1 hours; else if (lockingRatio < 85) return 3 hours; else if (lockingRatio < 90) return 6 hours; else if (lockingRatio < 95) return 12 hours; else return 24 hours; } function getSafeboxPeriodQuota(uint8 vipLevel) internal pure returns (uint16 quota) { assembly { switch vipLevel case 0 { quota := 0 } case 1 { quota := 1 } case 2 { quota := 2 } case 3 { quota := 4 } case 4 { quota := 8 } case 5 { quota := 16 } case 6 { quota := 32 } case 7 { quota := 64 } } } function getSafeboxUserQuota(uint8 vipLevel) internal pure returns (uint16 quota) { assembly { switch vipLevel case 0 { quota := 0 } case 1 { quota := 4 } case 2 { quota := 8 } case 3 { quota := 16 } case 4 { quota := 32 } case 5 { quota := 64 } case 6 { quota := 128 } case 7 { quota := 256 } } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; library Errors { /// @notice Safe Box error error SafeBoxHasExpire(); error SafeBoxNotExist(); error SafeBoxHasNotExpire(); error SafeBoxAlreadyExist(); error NoMatchingSafeBoxKey(); error SafeBoxKeyAlreadyExist(); /// @notice Auction error error AuctionHasNotCompleted(); error AuctionHasExpire(); error AuctionBidIsNotHighEnough(); error AuctionBidTokenMismatch(); error AuctionSelfBid(); error AuctionInvalidBidAmount(); error AuctionNotExist(); error SafeBoxAuctionWindowHasPassed(); /// @notice Activity common error error NftHasActiveActivities(); error ActivityHasNotCompleted(); error ActivityHasExpired(); error ActivityNotExist(); /// @notice User account error error InsufficientCredit(); error InsufficientBalanceForVipLevel(); error NoPrivilege(); /// @notice Parameter error error InvalidParam(); error NftCollectionNotSupported(); error NftCollectionAlreadySupported(); error ClaimableNftInsufficient(); error TokenNotSupported(); error PeriodQuotaExhausted(); error UserQuotaExhausted(); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol"; import "./IMulticall.sol"; interface IFlooring is IERC721Receiver, IMulticall { /// Admin Operations /// @notice Add new collection for Flooring Protocol function supportNewCollection(address _originalNFT, address fragmentToken) external; /// @notice Add new token which will be used as settlement token in Flooring Protocol /// @param addOrRemove `true` means add token, `false` means remove token function supportNewToken(address _tokenAddress, bool addOrRemove) external; /// @notice set proxy collection config /// Note. the `tokenId`s of the proxy collection and underlying collection must be correspond one by one /// eg. Paraspace Derivative Token BAYC(nBAYC) -> BAYC function setCollectionProxy(address proxyCollection, address underlyingCollection) external; /// @notice withdraw platform fee accumulated. /// Note. withdraw from `address(this)`'s account. function withdrawPlatformFee(address token, uint256 amount) external; /// @notice Deposit and lock credit token on behalf of receiver /// user can not withdraw these tokens until `unlockCredit` is called. function addAndLockCredit(address receiver, uint256 amount) external; /// @notice Unlock user credit token to allow withdraw /// used to release investors' funds as time goes /// Note. locked credit can be used to operate safeboxes(lock/unlock...) function unlockCredit(address receiver, uint256 amount) external; /// User Operations /// @notice User deposits token to the Floor Contract /// @param onBehalfOf deposit token into `onBehalfOf`'s account.(note. the tokens of msg.sender will be transfered) function addTokens(address onBehalfOf, address token, uint256 amount) external payable; /// @notice User removes token from Floor Contract /// @param receiver who will receive the funds.(note. the token of msg.sender will be transfered) function removeTokens(address token, uint256 amount, address receiver) external; /// @notice Lock specified `nftIds` into Flooring Safeboxes and receive corresponding Fragment Tokens of the `collection` /// @param expiryTs when the safeboxes expired, `0` means infinite lock without expiry /// @param vipLevel vip tier required in this lock operation /// @param maxCredit maximum credit can be locked in this operation, if real cost exceeds this limit, the tx will fail /// @param onBehalfOf who will receive the safebox and fragment tokens.(note. the NFTs of the msg.sender will be transfered) function lockNFTs( address collection, uint256[] memory nftIds, uint256 expiryTs, uint256 vipLevel, uint256 maxCredit, address onBehalfOf ) external returns (uint256); /// @notice Extend the exist safeboxes with longer lock duration with more credit token staked /// @param expiryTs new expiry timestamp, should bigger than previous expiry function extendKeys( address collection, uint256[] memory nftIds, uint256 expiryTs, uint256 vipLevel, uint256 maxCredit ) external returns (uint256); /// @notice Unlock specified `nftIds` which had been locked previously /// sender's wallet should have enough Fragment Tokens of the `collection` which will be burned to redeem the NFTs /// @param expiryTs the latest nft's expiry, we need this to clear locking records /// if the value smaller than the latest nft's expiry, the tx will fail /// if part of `nftIds` were locked infinitely, just skip these expiry /// @param receiver who will receive the NFTs. /// note. - The safeboxes of the msg.sender will be removed. /// - The Fragment Tokens of the msg.sender will be burned. function unlockNFTs(address collection, uint256 expiryTs, uint256[] memory nftIds, address receiver) external; /// @notice Fragment specified `nftIds` into Floor Vault and receive Fragment Tokens without any locking /// after fragmented, any one has enough Fragment Tokens can redeem there `nftIds` /// @param onBehalfOf who will receive the fragment tokens.(note. the NFTs of the msg.sender will be transfered) function fragmentNFTs(address collection, uint256[] memory nftIds, address onBehalfOf) external; /// @notice Kick expired safeboxes to the vault function tidyExpiredNFTs(address collection, uint256[] memory nftIds) external; /// @notice Randomly claim `claimCnt` NFTs from Floor Vault /// sender's wallet should have enough Fragment Tokens of the `collection` which will be burned to redeem the NFTs /// @param maxCredit maximum credit can be costed in this operation, if real cost exceeds this limit, the tx will fail /// @param receiver who will receive the NFTs. /// note. - the msg.sender will pay the redemption cost. /// - The Fragment Tokens of the msg.sender will be burned. function claimRandomNFT(address collection, uint256 claimCnt, uint256 maxCredit, address receiver) external returns (uint256); /// @notice Start auctions on specified `nftIds` with an initial bid price(`bidAmount`) /// This kind of auctions will be settled with Floor Credit Token /// @param bidAmount initial bid price function initAuctionOnExpiredSafeBoxes( address collection, uint256[] memory nftIds, address bidToken, uint256 bidAmount ) external; /// @notice Start auctions on specified `nftIds` index in the vault with an initial bid price(`bidAmount`) /// This kind of auctions will be settled with Fragment Token of the collection /// @param bidAmount initial bid price function initAuctionOnVault(address collection, uint256[] memory vaultIdx, address bidToken, uint96 bidAmount) external; /// @notice Owner starts auctions on his locked Safeboxes /// @param maxExpiry the latest nft's expiry, we need this to clear locking records /// @param token which token should be used to settle auctions(bid, settle) /// @param minimumBid minimum bid price when someone place a bid on the auction function ownerInitAuctions( address collection, uint256[] memory nftIds, uint256 maxExpiry, address token, uint256 minimumBid ) external; /// @notice Place a bid on specified `nftId`'s action /// @param bidAmount bid price /// @param bidOptionIdx which option used to extend auction expiry and bid price function placeBidOnAuction(address collection, uint256 nftId, uint256 bidAmount, uint256 bidOptionIdx) external; /// @notice Place a bid on specified `nftId`'s action /// @param token which token should be transfered to the Flooring for bidding. `0x0` means ETH(native) /// @param amountToTransfer how many `token` should to transfered function placeBidOnAuction( address collection, uint256 nftId, uint256 bidAmount, uint256 bidOptionIdx, address token, uint256 amountToTransfer ) external payable; /// @notice Settle auctions of `nftIds` function settleAuctions(address collection, uint256[] memory nftIds) external; struct RaffleInitParam { address collection; uint256[] nftIds; /// @notice which token used to buy and settle raffle address ticketToken; /// @notice price per ticket uint96 ticketPrice; /// @notice max tickets amount can be sold uint32 maxTickets; /// @notice durationIdx used to get how long does raffles last uint256 duration; /// @notice the largest epxiry of nfts, we need this to clear locking records uint256 maxExpiry; } /// @notice Owner start raffles on locked `nftIds` function ownerInitRaffles(RaffleInitParam memory param) external; /// @notice Buy `nftId`'s raffle tickets /// @param ticketCnt how many tickets should be bought in this operation function buyRaffleTickets(address collectionId, uint256 nftId, uint256 ticketCnt) external; /// @notice Buy `nftId`'s raffle tickets /// @param token which token should be transfered to the Flooring for buying. `0x0` means ETH(native) /// @param amountToTransfer how many `token` should to transfered function buyRaffleTickets( address collectionId, uint256 nftId, uint256 ticketCnt, address token, uint256 amountToTransfer ) external payable; /// @notice Settle raffles of `nftIds` function settleRaffles(address collectionId, uint256[] memory nftIds) external; struct PrivateOfferInitParam { address collection; uint256[] nftIds; /// @notice the largest epxiry of nfts, we need this to clear locking records uint256 maxExpiry; /// @notice who will receive the otc offers address receiver; /// @notice which token used to settle offers address token; /// @notice price of the offers uint96 price; } /// @notice Owner start private offers(otc) on locked `nftIds` function ownerInitPrivateOffers(PrivateOfferInitParam memory param) external; /// @notice Owner or Receiver cancel the private offers of `nftIds` function cancelPrivateOffers(address collectionId, uint256[] memory nftIds) external; /// @notice Receiver accept the private offers of `nftIds` function buyerAcceptPrivateOffers(address collectionId, uint256[] memory nftIds) external; /// @notice Receiver accept the private offers of `nftIds` /// @param token which token should be transfered to the Flooring for buying. `0x0` means ETH(native) /// @param amountToTransfer how many `token` should to transfered function buyerAcceptPrivateOffers( address collectionId, uint256[] memory nftIds, address token, uint256 amountToTransfer ) external payable; /// @notice Clear expired or mismatching safeboxes of `nftIds` in user account /// @param onBehalfOf whose account will be recalculated /// @return credit amount has been released function removeExpiredKeyAndRestoreCredit(address collection, uint256[] memory nftIds, address onBehalfOf) external returns (uint256); /// @notice Update user's staking credit status by iterating all active collections in user account /// @param onBehalfOf whose account will be recalculated /// @return availableCredit how many credit available to use after this opeartion function recalculateAvailableCredit(address onBehalfOf) external returns (uint256 availableCredit); /// Util operations /// @notice Called by external contracts to access granular pool state /// @param slot Key of slot to sload /// @return value The value of the slot as bytes32 function extsload(bytes32 slot) external view returns (bytes32 value); /// @notice Called by external contracts to access granular pool state /// @param slot Key of slot to start sloading from /// @param nSlots Number of slots to load into return value /// @return value The value of the sload-ed slots concatenated as dynamic bytes function extsload(bytes32 slot, uint256 nSlots) external view returns (bytes memory value); function creditToken() external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; interface IFragmentToken { error CallerIsNotTrustedContract(); function mint(address account, uint256 amount) external; function burn(address account, uint256 amount) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; /// @title Multicall interface /// @notice Enables calling multiple methods in a single call to the contract interface IMulticall { /** * @dev A call to an address target failed. The target may have reverted. */ error FailedMulticall(); struct CallData { address target; bytes callData; } /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed /// @param data The encoded function data for each of the calls to make to this contract /// @return results The results from each of the calls passed in via data function multicall(bytes[] calldata data) external returns (bytes[] memory results); /// @notice Allow trusted caller to call specified addresses through the Contract /// @dev The `msg.value` should not be trusted for any method callable from multicall. /// @param calls The encoded function data and target for each of the calls to make to this contract /// @return results The results from each of the calls passed in via calls function extMulticall(CallData[] calldata calls) external returns (bytes[] memory); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; library ERC721Transfer { /// @notice Thrown when an ERC721 transfer fails error ERC721TransferFailed(); function safeTransferFrom(address collection, address from, address to, uint256 tokenId) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let memPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(0, 0x42842e0e00000000000000000000000000000000000000000000000000000000) mstore(4, from) // Append and mask the "from" argument. mstore(36, to) // Append and mask the "to" argument. // Append the "tokenId" argument. Masking not required as it's a full 32 byte type. mstore(68, tokenId) success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), collection, 0, 0, 100, 0, 32) ) mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, memPointer) // Restore the memPointer. } if (!success) revert ERC721TransferFailed(); } function safeBatchTransferFrom(address collection, address from, address to, uint256[] memory tokenIds) internal { unchecked { uint256 len = tokenIds.length; for (uint256 i; i < len; ++i) { safeTransferFrom(collection, from, to, tokenIds[i]); } } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "../Constants.sol"; library RollingBuckets { error BucketValueExceedsLimit(); error BucketLengthExceedsLimit(); /// @dev `MAX_BUCKET_SIZE` must be a multiple of `WORD_ELEMENT_SIZE`, /// otherwise some words may be incomplete which may lead to incorrect bit positioning. uint256 constant MAX_BUCKET_SIZE = Constants.MAX_LOCKING_BUCKET; /// @dev each `ELEMENT_BIT_SIZE` bits stores an element uint256 constant ELEMENT_BIT_SIZE = 24; /// @dev `ELEMENT_BIT_SIZE` bits mask uint256 constant MASK = 0xFFFFFF; /// @dev one word(256 bits) can store (256 // ELEMENT_BIT_SIZE) elements uint256 constant WORD_ELEMENT_SIZE = 10; function position(uint256 tick) private pure returns (uint256 wordPos, uint256 bitPos) { unchecked { wordPos = tick / WORD_ELEMENT_SIZE; bitPos = tick % WORD_ELEMENT_SIZE; } } function get(mapping(uint256 => uint256) storage buckets, uint256 bucketStamp) internal view returns (uint256) { unchecked { (uint256 wordPos, uint256 bitPos) = position(bucketStamp % MAX_BUCKET_SIZE); return (buckets[wordPos] >> (bitPos * ELEMENT_BIT_SIZE)) & MASK; } } /// [first, last) function batchGet(mapping(uint256 => uint256) storage buckets, uint256 firstStamp, uint256 lastStamp) internal view returns (uint256[] memory) { if (firstStamp > lastStamp) revert BucketLengthExceedsLimit(); uint256 len; unchecked { len = lastStamp - firstStamp; } if (len > MAX_BUCKET_SIZE) { revert BucketLengthExceedsLimit(); } uint256[] memory result = new uint256[](len); uint256 resultIndex; unchecked { (uint256 wordPos, uint256 bitPos) = position(firstStamp % MAX_BUCKET_SIZE); uint256 wordVal = buckets[wordPos]; uint256 mask = MASK << (bitPos * ELEMENT_BIT_SIZE); for (uint256 i = firstStamp; i < lastStamp;) { assembly { /// increase idx firstly to skip `array length` resultIndex := add(resultIndex, 0x20) /// wordVal store order starts from lowest bit /// result[i] = ((wordVal & mask) >> (bitPos * ELEMENT_BIT_SIZE)) mstore(add(result, resultIndex), shr(mul(bitPos, ELEMENT_BIT_SIZE), and(wordVal, mask))) mask := shl(ELEMENT_BIT_SIZE, mask) bitPos := add(bitPos, 1) i := add(i, 1) } if (bitPos == WORD_ELEMENT_SIZE) { (wordPos, bitPos) = position(i % MAX_BUCKET_SIZE); wordVal = buckets[wordPos]; mask = MASK; } } } return result; } function set(mapping(uint256 => uint256) storage buckets, uint256 bucketStamp, uint256 value) internal { if (value > MASK) revert BucketValueExceedsLimit(); unchecked { (uint256 wordPos, uint256 bitPos) = position(bucketStamp % MAX_BUCKET_SIZE); uint256 wordValue = buckets[wordPos]; uint256 newValue = value << (bitPos * ELEMENT_BIT_SIZE); uint256 newWord = (wordValue & ~(MASK << (bitPos * ELEMENT_BIT_SIZE))) | newValue; buckets[wordPos] = newWord; } } function batchSet(mapping(uint256 => uint256) storage buckets, uint256 firstStamp, uint256[] memory values) internal { uint256 valLength = values.length; if (valLength > MAX_BUCKET_SIZE) revert BucketLengthExceedsLimit(); if (firstStamp > (type(uint256).max - valLength)) { revert BucketLengthExceedsLimit(); } unchecked { (uint256 wordPos, uint256 bitPos) = position(firstStamp % MAX_BUCKET_SIZE); uint256 wordValue = buckets[wordPos]; uint256 mask = ~(MASK << (bitPos * ELEMENT_BIT_SIZE)); /// reuse val length as End Postion valLength = (valLength + 1) * 0x20; /// start from first element offset for (uint256 i = 0x20; i < valLength; i += 0x20) { uint256 val; assembly { val := mload(add(values, i)) } if (val > MASK) revert BucketValueExceedsLimit(); assembly { /// newVal = val << (bitPos * BIT_SIZE) let newVal := shl(mul(bitPos, ELEMENT_BIT_SIZE), val) /// save newVal to wordVal, clear corresponding bits and set them as newVal /// wordValue = (wordVal & mask) | newVal wordValue := or(and(wordValue, mask), newVal) /// goto next number idx in current word bitPos := add(bitPos, 1) /// mask = ~(MASK << (bitPos, BIT_SIZE)) mask := not(shl(mul(bitPos, ELEMENT_BIT_SIZE), MASK)) } if (bitPos == WORD_ELEMENT_SIZE) { /// store hole word buckets[wordPos] = wordValue; /// get next word' position (wordPos, bitPos) = position((firstStamp + (i / 0x20)) % MAX_BUCKET_SIZE); wordValue = buckets[wordPos]; /// restore mask to make it start from lowest bits mask = ~MASK; } } /// store last word which may incomplete buckets[wordPos] = wordValue; } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "../Constants.sol"; import "../Errors.sol"; import "./SafeBox.sol"; import "./User.sol"; import {SafeBox, CollectionState, AuctionInfo, CollectionAccount, SafeBoxKey} from "./Structs.sol"; import "../library/RollingBuckets.sol"; library Helper { using SafeBoxLib for SafeBox; using UserLib for CollectionAccount; using RollingBuckets for mapping(uint256 => uint256); function counterStamp(uint256 timestamp) internal pure returns (uint96) { unchecked { return uint96((timestamp + Constants.BUCKET_SPAN_1) / Constants.BUCKET_SPAN); } } function checkAndUpdateSafeboxPeriodQuota(UserFloorAccount storage account, uint8 vipLevel, uint16 newLocked) internal { uint16 used = updateUserSafeboxQuota(account); uint16 totalQuota = Constants.getSafeboxPeriodQuota(vipLevel); uint16 nextUsed = used + newLocked; if (nextUsed > totalQuota) revert Errors.PeriodQuotaExhausted(); account.safeboxQuotaUsed = nextUsed; } function checkSafeboxUserQuota(UserFloorAccount storage account, uint8 vipLevel, uint256 newLocked) internal view { uint256 totalQuota = Constants.getSafeboxUserQuota(vipLevel); if (totalQuota < newLocked) { revert Errors.UserQuotaExhausted(); } else { unchecked { totalQuota -= newLocked; } } (, uint256[] memory keyCnts) = UserLib.getMinLevelAndVipKeyCounts(account.vipInfo); for (uint256 i; i < Constants.VIP_LEVEL_COUNT;) { if (totalQuota >= keyCnts[i]) { totalQuota -= keyCnts[i]; } else { revert Errors.UserQuotaExhausted(); } unchecked { ++i; } } } function ensureProxyVipLevel(uint8 vipLevel, bool proxy) internal pure { if (proxy && vipLevel < Constants.PROXY_COLLECTION_VIP_THRESHOLD) { revert Errors.InvalidParam(); } } function ensureMaxLocking( CollectionState storage collection, uint8 vipLevel, uint256 requireExpiryTs, uint256 requireLockCnt, bool extend ) internal view { /// vip level 0 can not use safebox utilities. if (vipLevel >= Constants.VIP_LEVEL_COUNT || vipLevel == 0) { revert Errors.InvalidParam(); } uint256 lockingRatio = calculateLockingRatio(collection, requireLockCnt); uint256 restrictRatio; if (extend) { /// try to extend exist safebox /// only restrict infinity locking, normal safebox with expiry should be skipped restrictRatio = requireExpiryTs == 0 ? Constants.getLockingRatioForInfinite(vipLevel) : 100; } else { /// try to lock(create new safebox) /// restrict maximum locking ratio to use safebox restrictRatio = Constants.getLockingRatioForSafebox(vipLevel); if (requireExpiryTs == 0) { uint256 extraRatio = Constants.getLockingRatioForInfinite(vipLevel); if (restrictRatio > extraRatio) restrictRatio = extraRatio; } } if (lockingRatio > restrictRatio) revert Errors.InvalidParam(); /// only check when it is not infinite lock if (requireExpiryTs > 0) { uint256 deltaBucket; unchecked { deltaBucket = counterStamp(requireExpiryTs) - counterStamp(block.timestamp); } if (deltaBucket == 0 || deltaBucket > Constants.getVipLockingBuckets(vipLevel)) { revert Errors.InvalidParam(); } } } function useSafeBoxAndKey(CollectionState storage collection, CollectionAccount storage userAccount, uint256 nftId) internal view returns (SafeBox storage safeBox, SafeBoxKey storage key) { safeBox = collection.safeBoxes[nftId]; if (safeBox.keyId == 0) revert Errors.SafeBoxNotExist(); if (safeBox.isSafeBoxExpired()) revert Errors.SafeBoxHasExpire(); key = userAccount.getByKey(nftId); if (!safeBox.isKeyMatchingSafeBox(key)) revert Errors.NoMatchingSafeBoxKey(); } function useSafeBox(CollectionState storage collection, uint256 nftId) internal view returns (SafeBox storage safeBox) { safeBox = collection.safeBoxes[nftId]; if (safeBox.keyId == 0) revert Errors.SafeBoxNotExist(); } function generateNextKeyId(CollectionState storage collectionState) internal returns (uint64 nextKeyId) { nextKeyId = collectionState.nextKeyId; ++collectionState.nextKeyId; } function generateNextActivityId(CollectionState storage collection) internal returns (uint64 nextActivityId) { nextActivityId = collection.nextActivityId; ++collection.nextActivityId; } function isAuctionPeriodOver(SafeBox storage safeBox) internal view returns (bool) { return safeBox.expiryTs + Constants.FREE_AUCTION_PERIOD < block.timestamp; } function hasActiveActivities(CollectionState storage collection, uint256 nftId) internal view returns (bool) { return hasActiveAuction(collection, nftId) || hasActiveRaffle(collection, nftId) || hasActivePrivateOffer(collection, nftId); } function hasActiveAuction(CollectionState storage collection, uint256 nftId) internal view returns (bool) { return collection.activeAuctions[nftId].endTime >= block.timestamp; } function hasActiveRaffle(CollectionState storage collection, uint256 nftId) internal view returns (bool) { return collection.activeRaffles[nftId].endTime >= block.timestamp; } function hasActivePrivateOffer(CollectionState storage collection, uint256 nftId) internal view returns (bool) { return collection.activePrivateOffers[nftId].endTime >= block.timestamp; } function getTokenFeeRateBips(address creditToken, address floorToken, address settleToken) internal pure returns (uint256) { uint256 feeRateBips = Constants.COMMON_FEE_RATE_BIPS; if (settleToken == creditToken) { feeRateBips = Constants.CREDIT_FEE_RATE_BIPS; } else if (settleToken == floorToken) { feeRateBips = Constants.SPEC_FEE_RATE_BIPS; } return feeRateBips; } function calculateActivityFee(uint256 settleAmount, uint256 feeRateBips) internal pure returns (uint256 afterFee, uint256 fee) { fee = settleAmount * feeRateBips / 10000; unchecked { afterFee = settleAmount - fee; } } function prepareBucketUpdate(CollectionState storage collection, uint256 startBucket, uint256 endBucket) internal view returns (uint256[] memory buckets) { uint256 validEnd = collection.lastUpdatedBucket; uint256 padding; if (endBucket < validEnd) { validEnd = endBucket; } else { unchecked { padding = endBucket - validEnd; } } if (startBucket < validEnd) { if (padding == 0) { buckets = collection.countingBuckets.batchGet(startBucket, validEnd); } else { uint256 validLen; unchecked { validLen = validEnd - startBucket; } buckets = new uint256[](validLen + padding); uint256[] memory tmp = collection.countingBuckets.batchGet(startBucket, validEnd); for (uint256 i; i < validLen;) { buckets[i] = tmp[i]; unchecked { ++i; } } } } else { buckets = new uint256[](endBucket - startBucket); } } function getActiveSafeBoxes(CollectionState storage collectionState, uint256 timestamp) internal view returns (uint256) { uint256 bucketStamp = counterStamp(timestamp); if (collectionState.lastUpdatedBucket < bucketStamp) { return 0; } return collectionState.countingBuckets.get(bucketStamp); } function calculateLockingRatio(CollectionState storage collection, uint256 newLocked) internal view returns (uint256) { uint256 freeAmount = collection.freeTokenIds.length; uint256 totalManaged = newLocked + collection.activeSafeBoxCnt + freeAmount; return calculateLockingRatioRaw(freeAmount, totalManaged); } function calculateLockingRatioRaw(uint256 freeAmount, uint256 totalManaged) internal pure returns (uint256) { if (totalManaged == 0) { return 0; } else { unchecked { return (100 - freeAmount * 100 / totalManaged); } } } function updateUserCreditWaiver(UserFloorAccount storage account) internal returns (uint96) { if (block.timestamp - account.lastWaiverPeriodTs <= Constants.USER_REDEMPTION_WAIVER_REFRESH_DURATION) { return account.creditWaiverUsed; } else { unchecked { account.lastWaiverPeriodTs = uint32( block.timestamp / Constants.USER_REDEMPTION_WAIVER_REFRESH_DURATION * Constants.USER_REDEMPTION_WAIVER_REFRESH_DURATION ); } account.creditWaiverUsed = 0; return 0; } } function updateUserSafeboxQuota(UserFloorAccount storage account) internal returns (uint16) { if (block.timestamp - account.lastQuotaPeriodTs <= Constants.USER_SAFEBOX_QUOTA_REFRESH_DURATION) { return account.safeboxQuotaUsed; } else { unchecked { account.lastQuotaPeriodTs = uint32( block.timestamp / Constants.USER_SAFEBOX_QUOTA_REFRESH_DURATION * Constants.USER_SAFEBOX_QUOTA_REFRESH_DURATION ); } account.safeboxQuotaUsed = 0; return 0; } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import {SafeBox, SafeBoxKey} from "./Structs.sol"; library SafeBoxLib { uint64 public constant SAFEBOX_KEY_NOTATION = type(uint64).max; function isInfiniteSafeBox(SafeBox storage safeBox) internal view returns (bool) { return safeBox.expiryTs == 0; } function isSafeBoxExpired(SafeBox storage safeBox) internal view returns (bool) { return safeBox.expiryTs != 0 && safeBox.expiryTs < block.timestamp; } function _isSafeBoxExpired(SafeBox memory safeBox) internal view returns (bool) { return safeBox.expiryTs != 0 && safeBox.expiryTs < block.timestamp; } function isKeyMatchingSafeBox(SafeBox storage safeBox, SafeBoxKey storage safeBoxKey) internal view returns (bool) { return safeBox.keyId == safeBoxKey.keyId; } function _isKeyMatchingSafeBox(SafeBox memory safeBox, SafeBoxKey memory safeBoxKey) internal pure returns (bool) { return safeBox.keyId == safeBoxKey.keyId; } function encodeSafeBoxKey(SafeBoxKey memory key) internal pure returns (uint256) { uint256 val = key.lockingCredit; val |= (uint256(key.keyId) << 96); val |= (uint256(key.vipLevel) << 160); return val; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "../interface/IFragmentToken.sol"; struct SafeBox { /// Either matching a key OR Constants.SAFEBOX_KEY_NOTATION meaning temporarily /// held by a bidder in auction. uint64 keyId; /// The timestamp that the safe box expires. uint32 expiryTs; /// The owner of the safebox. It maybe outdated due to expiry address owner; } struct PrivateOffer { /// private offer end time uint96 endTime; /// which token used to accpet the offer address token; /// price of the offer uint96 price; address owner; /// who should receive the offer address buyer; uint64 activityId; } struct AuctionInfo { /// The end time for the auction. uint96 endTime; /// Bid token address. address bidTokenAddress; /// Minimum Bid. uint96 minimumBid; /// The person who trigger the auction at the beginning. address triggerAddress; uint96 lastBidAmount; address lastBidder; /// Whether the auction is triggered by the NFT owner itself? bool isSelfTriggered; uint64 activityId; uint32 feeRateBips; } struct TicketRecord { /// who buy the tickets address buyer; /// Start index of tickets /// [startIdx, endIdx) uint48 startIdx; /// End index of tickets uint48 endIdx; } struct RaffleInfo { /// raffle end time uint48 endTime; /// max tickets amount the raffle can sell uint48 maxTickets; /// which token used to buy the raffle tickets address token; /// price per ticket uint96 ticketPrice; /// total funds collected by selling tickets uint96 collectedFund; uint64 activityId; address owner; /// total sold tickets amount uint48 ticketSold; uint32 feeRateBips; /// whether the raffle is being settling bool isSettling; /// tickets sold records TicketRecord[] tickets; } struct CollectionState { /// The address of the Floor Token cooresponding to the NFTs. IFragmentToken floorToken; /// Records the active safe box in each time bucket. mapping(uint256 => uint256) countingBuckets; /// Stores all of the NFTs that has been fragmented but *without* locked up limit. uint256[] freeTokenIds; /// Huge map for all the `SafeBox`es in one collection. mapping(uint256 => SafeBox) safeBoxes; /// Stores all the ongoing auctions: nftId => `AuctionInfo`. mapping(uint256 => AuctionInfo) activeAuctions; /// Stores all the ongoing raffles: nftId => `RaffleInfo`. mapping(uint256 => RaffleInfo) activeRaffles; /// Stores all the ongoing private offers: nftId => `PrivateOffer`. mapping(uint256 => PrivateOffer) activePrivateOffers; /// The last bucket time the `countingBuckets` is updated. uint64 lastUpdatedBucket; /// Next Key Id. This should start from 1, we treat key id `SafeboxLib.SAFEBOX_KEY_NOTATION` as temporarily /// being used for activities(auction/raffle). uint64 nextKeyId; /// Active Safe Box Count. uint64 activeSafeBoxCnt; /// The number of infinite lock count. uint64 infiniteCnt; /// Next Activity Id. This should start from 1 uint64 nextActivityId; uint32 lastVaultAuctionPeriodTs; } struct UserFloorAccount { /// @notice it should be maximum of the `totalLockingCredit` across all collections uint96 minMaintCredit; /// @notice used to iterate collection accounts /// packed with `minMaintCredit` to reduce storage slot access address firstCollection; /// @notice user vip level related info /// 0 - 239 bits: store SafeBoxKey Count per vip level, per level using 24 bits /// 240 - 247 bits: store minMaintVipLevel /// 248 - 255 bits: remaining uint256 vipInfo; /// @notice Locked Credit amount which cannot be withdrawn and will be released as time goes. uint256 lockedCredit; mapping(address => CollectionAccount) accounts; mapping(address => uint256) tokenAmounts; uint32 lastQuotaPeriodTs; uint16 safeboxQuotaUsed; uint32 lastWaiverPeriodTs; uint96 creditWaiverUsed; } struct SafeBoxKey { /// locked credit amount of this safebox uint96 lockingCredit; /// corresponding key id of the safebox uint64 keyId; /// which vip level the safebox locked uint8 vipLevel; } struct CollectionAccount { mapping(uint256 => SafeBoxKey) keys; /// total locking credit of all `keys` in this collection uint96 totalLockingCredit; /// track next collection as linked list address next; } /// Internal Structure struct LockParam { address proxyCollection; address collection; uint256[] nftIds; uint256 expiryTs; uint256 vipLevel; uint256 maxCreditCost; address creditToken; }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "openzeppelin-contracts/contracts/utils/math/SafeCast.sol"; import "../Constants.sol"; import "../Errors.sol"; import {UserFloorAccount, CollectionAccount, SafeBoxKey} from "./Structs.sol"; library UserLib { using SafeCast for uint256; /// @notice update the account maintain credit on behalfOf `onBehalfOf` event UpdateMaintainCredit(address indexed onBehalfOf, uint256 minMaintCredit); address internal constant LIST_GUARD = address(1); function ensureVipCredit(UserFloorAccount storage account, uint8 requireVipLevel, address creditToken) internal view returns (uint256) { uint256 totalCredit = tokenBalance(account, creditToken); if (Constants.getVipBalanceRequirements(requireVipLevel) > totalCredit) { revert Errors.InsufficientBalanceForVipLevel(); } return totalCredit; } function getMinMaintVipLevel(UserFloorAccount storage account) internal view returns (uint8) { unchecked { return uint8(account.vipInfo >> 240); } } function getMinLevelAndVipKeyCounts(uint256 vipInfo) internal pure returns (uint8 minLevel, uint256[] memory counts) { unchecked { counts = new uint256[](Constants.VIP_LEVEL_COUNT); minLevel = uint8(vipInfo >> 240); for (uint256 i; i < Constants.VIP_LEVEL_COUNT; ++i) { counts[i] = (vipInfo >> (i * 24)) & 0xFFFFFF; } } } function storeMinLevelAndVipKeyCounts( UserFloorAccount storage account, uint8 minMaintVipLevel, uint256[] memory keyCounts ) internal { unchecked { uint256 _data = (uint256(minMaintVipLevel) << 240); for (uint256 i; i < Constants.VIP_LEVEL_COUNT; ++i) { _data |= ((keyCounts[i] & 0xFFFFFF) << (i * 24)); } account.vipInfo = _data; } } function getOrAddCollection(UserFloorAccount storage user, address collection) internal returns (CollectionAccount storage) { CollectionAccount storage entry = user.accounts[collection]; if (entry.next == address(0)) { if (user.firstCollection == address(0)) { user.firstCollection = collection; entry.next = LIST_GUARD; } else { entry.next = user.firstCollection; user.firstCollection = collection; } } return entry; } function removeCollection(UserFloorAccount storage userAccount, address collection, address prev) internal { CollectionAccount storage cur = userAccount.accounts[collection]; if (cur.next == address(0)) revert Errors.InvalidParam(); if (collection == userAccount.firstCollection) { if (cur.next == LIST_GUARD) { userAccount.firstCollection = address(0); } else { userAccount.firstCollection = cur.next; } } else { CollectionAccount storage prevAccount = userAccount.accounts[prev]; if (prevAccount.next != collection) revert Errors.InvalidParam(); prevAccount.next = cur.next; } delete userAccount.accounts[collection]; } function getByKey(UserFloorAccount storage userAccount, address collection) internal view returns (CollectionAccount storage) { return userAccount.accounts[collection]; } function addSafeboxKey(CollectionAccount storage account, uint256 nftId, SafeBoxKey memory key) internal { if (account.keys[nftId].keyId > 0) { revert Errors.SafeBoxKeyAlreadyExist(); } account.keys[nftId] = key; } function removeSafeboxKey(CollectionAccount storage account, uint256 nftId) internal { delete account.keys[nftId]; } function getByKey(CollectionAccount storage account, uint256 nftId) internal view returns (SafeBoxKey storage) { return account.keys[nftId]; } function tokenBalance(UserFloorAccount storage account, address token) internal view returns (uint256) { return account.tokenAmounts[token]; } function lockCredit(UserFloorAccount storage account, uint256 amount) internal { unchecked { account.lockedCredit += amount; } } function unlockCredit(UserFloorAccount storage account, uint256 amount) internal { unchecked { account.lockedCredit -= amount; } } function depositToken(UserFloorAccount storage account, address token, uint256 amount) internal { account.tokenAmounts[token] += amount; } function withdrawToken(UserFloorAccount storage account, address token, uint256 amount, bool isCreditToken) internal { uint256 balance = account.tokenAmounts[token]; if (balance < amount) { revert Errors.InsufficientCredit(); } if (isCreditToken) { uint256 avaiableBuf; unchecked { avaiableBuf = balance - amount; } if ( avaiableBuf < Constants.getVipBalanceRequirements(getMinMaintVipLevel(account)) || avaiableBuf < account.minMaintCredit || avaiableBuf < account.lockedCredit ) { revert Errors.InsufficientCredit(); } account.tokenAmounts[token] = avaiableBuf; } else { unchecked { account.tokenAmounts[token] = balance - amount; } } } function transferToken( UserFloorAccount storage from, UserFloorAccount storage to, address token, uint256 amount, bool isCreditToken ) internal { withdrawToken(from, token, amount, isCreditToken); depositToken(to, token, amount); } function updateVipKeyCount(UserFloorAccount storage account, uint8 vipLevel, int256 diff) internal { if (vipLevel > 0 && diff != 0) { (uint8 minMaintVipLevel, uint256[] memory keyCounts) = getMinLevelAndVipKeyCounts(account.vipInfo); if (diff < 0) { keyCounts[vipLevel] -= uint256(-diff); if (vipLevel == minMaintVipLevel && keyCounts[vipLevel] == 0) { uint8 newVipLevel = vipLevel; do { unchecked { --newVipLevel; } } while (newVipLevel > 0 && keyCounts[newVipLevel] == 0); minMaintVipLevel = newVipLevel; } } else { keyCounts[vipLevel] += uint256(diff); if (vipLevel > minMaintVipLevel) { minMaintVipLevel = vipLevel; } } storeMinLevelAndVipKeyCounts(account, minMaintVipLevel, keyCounts); } } function recalculateMinMaintCredit(UserFloorAccount storage account, address onBehalfOf) public returns (uint256 maxLocking) { address prev = account.firstCollection; for (address collection = account.firstCollection; collection != LIST_GUARD && collection != address(0);) { (uint256 locking, address next) = (getByKey(account, collection).totalLockingCredit, getByKey(account, collection).next); if (locking == 0) { removeCollection(account, collection, prev); collection = next; } else { if (locking > maxLocking) { maxLocking = locking; } prev = collection; collection = next; } } account.minMaintCredit = uint96(maxLocking); emit UpdateMaintainCredit(onBehalfOf, maxLocking); } }
{ "metadata": { "bytecodeHash": "none", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 800 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"AuctionHasNotCompleted","type":"error"},{"inputs":[],"name":"BucketLengthExceedsLimit","type":"error"},{"inputs":[],"name":"BucketValueExceedsLimit","type":"error"},{"inputs":[],"name":"ClaimableNftInsufficient","type":"error"},{"inputs":[],"name":"ERC721TransferFailed","type":"error"},{"inputs":[],"name":"InsufficientBalanceForVipLevel","type":"error"},{"inputs":[],"name":"InsufficientCredit","type":"error"},{"inputs":[],"name":"InvalidParam","type":"error"},{"inputs":[],"name":"NftHasActiveActivities","type":"error"},{"inputs":[],"name":"NoMatchingSafeBoxKey","type":"error"},{"inputs":[],"name":"PeriodQuotaExhausted","type":"error"},{"inputs":[],"name":"SafeBoxAlreadyExist","type":"error"},{"inputs":[],"name":"SafeBoxHasExpire","type":"error"},{"inputs":[],"name":"SafeBoxHasNotExpire","type":"error"},{"inputs":[],"name":"SafeBoxKeyAlreadyExist","type":"error"},{"inputs":[],"name":"SafeBoxNotExist","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[],"name":"UserQuotaExhausted","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"creditCost","type":"uint256"}],"name":"ClaimRandomNft","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"ExpiredNftToVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"safeBoxKeys","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"safeBoxExpiryTs","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minMaintCredit","type":"uint256"}],"name":"ExtendKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"FragmentNft","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"safeBoxKeys","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"safeBoxExpiryTs","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minMaintCredit","type":"uint256"},{"indexed":false,"internalType":"address","name":"proxyCollection","type":"address"}],"name":"LockNft","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"safeBoxKeys","type":"uint256[]"}],"name":"RemoveExpiredKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"proxyCollection","type":"address"}],"name":"UnlockNft","type":"event"}]
Contract Creation Code
613e5e610035600b8282823980515f1a60731461002957634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610090575f3560e01c8063de8b49b511610063578063de8b49b514610125578063e54dc4cb14610144578063f1cd5ef314610163578063f681534614610183575f80fd5b806352fa7ba81461009457806363698228146100b55780636ceb7d5d146100e7578063aa5ba1ef14610106575b5f80fd5b81801561009f575f80fd5b506100b36100ae3660046137c2565b6101a2565b005b8180156100c0575f80fd5b506100d46100cf366004613847565b6103b0565b6040519081526020015b60405180910390f35b8180156100f2575f80fd5b506100b36101013660046138b3565b610791565b818015610111575f80fd5b506100d4610120366004613905565b610891565b818015610130575f80fd5b506100d461013f3660046139f1565b610c5e565b81801561014f575f80fd5b506100b361015e366004613a3c565b610e2a565b610176610171366004613a9f565b610f5d565b6040516100de9190613ac8565b81801561018e575f80fd5b506100d461019d366004613b0b565b610fb3565b5f6101ad87866112b3565b90505f6101bc898587856112d3565b90505f5b8151811015610204576101fc8282815181106101de576101de613b4d565b6020026020010151604001515f198b61162a9092919063ffffffff16565b6001016101c0565b50306001600160a01b038416036102bd5784515f5b81811015610261578a60020187828151811061023757610237613b4d565b6020908102919091018101518254600181810185555f948552929093209092019190915501610219565b50866001600160a01b0316336001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef23896040516102af9190613b9a565b60405180910390a45061034e565b885485516001600160a01b0390911690639dc29fac9033906102e99069d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561032c575f80fd5b505af115801561033e573d5f803e3d5ffd5b5050505061034e87308588611755565b856001600160a01b0316836001600160a01b0316336001600160a01b03167f550bc81d80b31d12a069c70ba86563e82da48c7a5e6fedea04448763ae4b34e8888b60405161039d929190613bd7565b60405180910390a4505050505050505050565b5f806103bc86866112b3565b90505f8085516001600160401b038111156103d9576103d96136ef565b604051908082528060200260200182016040528015610402578160200160208202803683370190505b5090505f86516001600160401b0381111561041f5761041f6136ef565b604051908082528060200260200182016040528015610448578160200160208202803683370190505b5090505f5b87518110156106dc575f88828151811061046957610469613b4d565b602002602001015190505f610487828861179690919063ffffffff16565b6040518060600160405290815f82015f9054906101000a90046001600160601b03166001600160601b03166001600160601b031681526020015f8201600c9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160149054906101000a900460ff1660ff1660ff168152505090505f8d6003015f8481526020019081526020015f206040518060600160405290815f82015f9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160089054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020015f8201600c9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681525050905081602001516001600160401b03165f036105d957604051633494a40d60e21b815260040160405180910390fd5b6105e2816117a7565b806105ff5750602082015181516001600160401b03908116911614155b156106ce578286888151811061061757610617613b4d565b60209081029190910181019190915282519083015160408401516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b161785888151811061066f5761066f613b4d565b6020908102919091010152815160408301516001600160601b0390911699909901986001909701966106a4908e905f1961162a565b5f838152602089905260409020805474ffffffffffffffffffffffffffffffffffffffffff191690555b83600101935050505061044d565b50841561072d576106ec856117cf565b6001850180545f906107089084906001600160601b0316613c01565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b876001600160a01b0316866001600160a01b0316336001600160a01b03167f356980d99a3c896e33a4b9789ada3ddd36a0660f61a6880943544207b0d3ec90858560405161077c929190613c21565b60405180910390a45050505095945050505050565b81515f5b8181101561083f575f8482815181106107b0576107b0613b4d565b602002602001015190505f6107c5878361180b565b90506107d081611848565b6107ed5760405163f1a3275d60e01b815260040160405180910390fd5b6107f681611877565b61081357604051630a0619fd60e01b815260040160405180910390fd5b61081d878361189f565b50600286018054600181810183555f9283526020909220019190915501610795565b50816001600160a01b0316336001600160a01b03167fcbd8592698d587736ff1c8073851b33b9b08ae92c8d0a71678b48eef0af4049f856040516108839190613b9a565b60405180910390a350505050565b5f8315806108a25750600288015484115b156108c0576040516331ea5a2760e21b815260040160405180910390fd5b600288015460078901545f906108e7908390600160801b90046001600160401b0316613c45565b905060506109076108f88885613c58565b6109028985613c58565b6118f9565b10610925576040516331ea5a2760e21b815260040160405180910390fd5b5f866001600160401b0381111561093e5761093e6136ef565b604051908082528060200260200182016040528015610967578160200160208202803683370190505b50335f90815260208c9052604090209091505b8715610aaa5761099261098d85856118f9565b611925565b61099c9086613c45565b60028d0154604080514260208201524491810191909152606081018690529196505f91608001604051602081830303815290604052805190602001205f1c6109e49190613c7f565b60028e0180545f199b8c019b9788019796909601959192509082908110610a0d57610a0d613b4d565b905f5260205f200154838a81518110610a2857610a28613b4d565b602090810291909101015260028d018054610a4590600190613c58565b81548110610a5557610a55613b4d565b905f5260205f2001548d6002018281548110610a7357610a73613b4d565b5f9182526020909120015560028d01805480610a9157610a91613c9e565b600190038181905f5260205f20015f905590555061097a565b6001600160a01b038a165f908152600482016020526040812054610acd90611958565b90505f610ad983611a19565b9050610ae6878383611ac3565b6005850180546001600160601b039092166a01000000000000000000000275ffffffffffffffffffffffff00000000000000000000199092169190911790559650505086851115610b4a57604051638ac4bc7360e01b815260040160405180910390fd5b305f90815260208c905260409020610b679082908c886001611bd6565b8b5482516001600160a01b0390911690639dc29fac903390610b939069d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610bd6575f80fd5b505af1158015610be8573d5f803e3d5ffd5b50505050610bf889308885611755565b886001600160a01b0316866001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc8589604051610c47929190613cb2565b60405180910390a450505050979650505050505050565b608081015160c08201515f91908290610c7a9086908490611bed565b9050610c93868386606001518760400151516001611c33565b845460208501516001600160601b03909116906060905f90610cb6908990611d4d565b90506060610ccf8a838a604001518b606001518a611e06565b600185015491995094509091506001600160601b031680860380891180610cf957508960a0015189115b15610d1757604051638ac4bc7360e01b815260040160405180910390fd5b505f5b8251811015610d5957610d5181848381518110610d3957610d39613b4d565b60200260200101518d61162a9092919063ffffffff16565b600101610d1a565b50610d63886117cf565b610d6d9082613cd3565b6001840180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015610dbf5789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b50505085602001516001600160a01b0316336001600160a01b03167fa2901e7a2f0ce49b98e66b2473ae57e5b2bf1d28cac05c6565d848a813cb16778860400151848a6060015187604051610e179493929190613cf3565b60405180910390a3505050509392505050565b81515f5b81811015610e765785600201848281518110610e4c57610e4c613b4d565b6020908102919091018101518254600181810185555f948552929093209092019190915501610e2e565b5084546001600160a01b03166340c10f1983610e9c8469d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610edf575f80fd5b505af1158015610ef1573d5f803e3d5ffd5b50505050610f0184333086611755565b836001600160a01b0316826001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef2386604051610f4e9190613b9a565b60405180910390a45050505050565b6060610fab846203f4806203f47f86010460078701546001600160601b039190911690610fa6906001600160401b03166203f4806203f47f8801046001600160601b0316612325565b61233a565b949350505050565b5f306001600160a01b03831603610fdd57604051633494a40d60e21b815260040160405180910390fd5b825f01516001600160a01b031683602001516001600160a01b0316141580156110095750606083015115155b1561102757604051633494a40d60e21b815260040160405180910390fd5b608083015160c08401515f906110409087908490611bed565b9050611058878387606001518860400151515f611c33565b5f61106282611958565b90505f866040015151905061109382885f01516001600160a01b031689602001516001600160a01b0316141561248e565b6110a688836110a1846124c0565b6124ee565b6110b1888583612568565b50508554604086015160208701516001600160601b03909216916060905f906110db908b90611d4d565b90506110ee8b82858c606001518a612625565b600183015491985092506001600160601b03168086038089118061111557508a60a0015189115b1561113357604051638ac4bc7360e01b815260040160405180910390fd5b5061113d886117cf565b6111479082613cd3565b6001830180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015611199578a546bffffffffffffffffffffffff19166001600160601b0382169081178c5594505b505081516111aa908a90879061162a565b895482516001600160a01b03909116906340c10f199089906111d69069d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015611219575f80fd5b505af115801561122b573d5f803e3d5ffd5b5050505061123e885f0151333085611755565b87602001516001600160a01b0316876001600160a01b0316336001600160a01b03167faa75244fec84b237a00e565f2d9ef15139490324abdb7807bd587432976b9a2085858d60600151898f5f015160405161129e959493929190613d2b565b60405180910390a45050505050949350505050565b6001600160a01b0381165f90815260038301602052604090205b92915050565b60605f841180156112e357504284105b1561130157604051631d80cfbf60e21b815260040160405180910390fd5b5f83516001600160401b0381111561131b5761131b6136ef565b60405190808252806020026020018201604052801561136457816020015b604080516060810182525f80825260208083018290529282015282525f199092019101816113395790505b5090506001600160601b036203f480426203f47f0104165f606087156113c65760078901545f906113b2906001600160601b036203f4806203f47f8d010416906001600160401b0316612325565b90506113c260018b018583612981565b9150505b5f5b87518110156115bc575f8882815181106113e4576113e4613b4d565b602002602001015190506113f88b82612ab2565b1561141657604051636bb8676960e11b815260040160405180910390fd5b5f806114238d8b85612b1d565b8054919350915061143d906001600160601b031687613c45565b8254909650600160401b900463ffffffff1661149e5760078d01805460189061147590600160c01b90046001600160401b0316613d76565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550611522565b81545f9088906114c590600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b03166114d89190613c58565b905085518111156114e7575f80fd5b5f5b8181101561151f5786818151811061150357611503613b4d565b60200260200101805161151590613d97565b90526001016114e9565b50505b6040805160608101825282546001600160601b0381168252600160601b81046001600160401b03166020830152600160a01b900460ff1691810191909152885189908690811061157457611574613b4d565b60200260200101819052506115898d8461189f565b50505f908152602088905260409020805474ffffffffffffffffffffffffffffffffffffffffff191690556001016113c8565b506115c6826117cf565b6001870180545f906115e29084906001600160601b0316613c01565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505f8151111561161d5761161d60018a018483612bc4565b5091979650505050505050565b5f8260ff1611801561163b57508015155b15611750575f8061164f8560010154612d02565b915091505f8312156117035761166483613dac565b818560ff168151811061167957611679613b4d565b6020026020010181815161168d9190613c58565b90525060ff8481169083161480156116c05750808460ff16815181106116b5576116b5613b4d565b60200260200101515f145b156116fe57835b5f190160ff8116158015906116f75750818160ff16815181106116ec576116ec613b4d565b60200260200101515f145b6116c75791505b611742565b82818560ff168151811061171957611719613b4d565b6020026020010181815161172d9190613c45565b90525060ff8083169085161115611742578391505b61174d858383612d72565b50505b505050565b80515f5b8181101561178e5761178686868686858151811061177957611779613b4d565b6020026020010151612dca565b600101611759565b505050505050565b5f9081526020919091526040902090565b5f816020015163ffffffff165f141580156112cd575050602001514263ffffffff9091161090565b5f6001600160601b03821115611807576040516306dfcc6560e41b815260606004820152602481018390526044015b60405180910390fd5b5090565b5f8181526003830160205260408120805490916001600160401b0390911690036112cd57604051635672499160e01b815260040160405180910390fd5b80545f90600160401b900463ffffffff16158015906112cd5750505442600160401b90910463ffffffff161090565b80545f904290611898906201518090600160401b900463ffffffff16613c45565b1092915050565b5f8181526003830160205260408120556007820180546010906118d190600160801b90046001600160401b0316613d76565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505050565b5f815f0361190857505f6112cd565b81836064028161191a5761191a613c6b565b0460640390506112cd565b5f602882101561193657505f919050565b600560271983010460020a683635c9adc5dea00000029050919050565b919050565b5f69065a4da25d3016c0000082101561197257505f919050565b69152d02c7e14af680000082101561198c57506001919050565b693f870857a3e0e38000008210156119a657506002919050565b69d3c21bcecceda10000008210156119c057506003919050565b6a027b46536c66c8e30000008210156119db57506004919050565b6a084595161401484a0000008210156119f657506005919050565b6a18d0bf423c03d8de000000821015611a1157506006919050565b506007919050565b60058101545f906201518090611a3f906601000000000000900463ffffffff1642613c58565b11611a635750600501546a010000000000000000000090046001600160601b031690565b5060050180547fffffffffffffffffffff00000000000000000000000000000000ffffffffffff16660100000000000062015180428190040263ffffffff160275ffffffffffffffffffffffff0000000000000000000019161790555f90565b5f80670de0b6b3a7640000848015611b125760018114611b1a5760028114611b265760038114611b325760048114611b3e5760058114611b4a5760068114611b565760078114611b6257611b6b565b5f9150611b6b565b816107d0029150611b6b565b81610fa0029150611b6b565b81611f40029150611b6b565b81613e80029150611b6b565b81617d00029150611b6b565b8161fa00029150611b6b565b816201f4000291505b50836001600160601b0316816001600160601b031611611b915785849250925050611bce565b8390036001600160601b038116861015611bbb57505f9150506001600160601b0382168401611bce565b6001600160601b03811686039250830190505b935093915050565b611be285848484612e25565b61174d848484612f08565b6001600160a01b0381165f90815260048401602052604081205480611c148560ff16612f3b565b1115610fab576040516369f131af60e11b815260040160405180910390fd5b60088460ff16101580611c47575060ff8416155b15611c6557604051633494a40d60e21b815260040160405180910390fd5b5f611c708684612fdf565b90505f8215611c96578415611c86576064611c8f565b611c8f86613029565b9050611cc2565b611c9f8661309f565b9050845f03611cc2575f611cb287613029565b905080821115611cc0578091505b505b80821115611ce357604051633494a40d60e21b815260040160405180910390fd5b8415611d44575f6203f4806203f47f4201046203f4806203f47f880104036001600160601b03169050805f1480611d245750611d218760ff1661311d565b81115b15611d4257604051633494a40d60e21b815260040160405180910390fd5b505b50505050505050565b6001600160a01b038082165f9081526003840160205260408120600181015491929091600160601b900416611dff578354600160601b90046001600160a01b0316611dc55783546001600160a01b038416600160601b9081026001600160601b03928316178655600183018054909216179055611dff565b83546001820180546001600160a01b03600160601b93849004811684026001600160601b0392831617909255865491861690920291161784555b9392505050565b6040805160c08101909152821581526060905f9082908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8901046001600160601b0316815260028b015460078c0154602090920191611e7b9190600160801b90046001600160401b0316613c45565b81525f602082015260078b0154600160c01b90046001600160401b0316604090910152805190915015611ed857611ec9611eb96303b5380042613c45565b6203f4806203f47f919091010490565b6001600160601b031660408201525b5f611eec8a8360200151846040015161233a565b60408051600880825261012082019092529192505f919060208201610100803683370190505090505f89516001600160401b03811115611f2e57611f2e6136ef565b604051908082528060200260200182016040528015611f57578160200160208202803683370190505b5090505f5b8a5181101561226957611f888d8c8381518110611f7b57611f7b613b4d565b6020026020010151612ab2565b15611fa657604051636bb8676960e11b815260040160405180910390fd5b5f80611fcc8f8f8f8681518110611fbf57611fbf613b4d565b6020026020010151612b1d565b602089015182549294509092505f91611ffc90600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b031661200f9190613c58565b90505f5b8181101561207f5787818151811061202d5761202d613b4d565b60200260200101515f0361205457604051633494a40d60e21b815260040160405180910390fd5b87818151811061206657612066613b4d565b6020908102919091010180515f19019052600101612013565b50505f61208d87898e61319e565b82549091506001600160601b03168111156120f55781546120b7906001600160601b031682613c58565b886080018181516120c89190613c45565b9052506120d4816117cf565b82546bffffffffffffffffffffffff19166001600160601b03919091161782555b815460ff600160a01b9091048116908d1681101561217e57825460ff808f16600160a01b0260ff60a01b1990921691909117845587518891831690811061213e5761213e613b4d565b60200260200101805161215090613dc6565b90528651879060ff8f1690811061216957612169613b4d565b60200260200101805161217b90613dda565b90525b8851156121b75783546bffffffff00000000000000001916845560a0890180516121a790613e11565b6001600160401b031690526121db565b835463ffffffff8f16600160401b026bffffffff0000000000000000199091161784555b604080516060808201835285546001600160601b038116808452600160601b82046001600160401b0381166020860152600160a01b90920460ff81169490950193909352901b67ffffffffffffffff60601b161760a09190911b60ff60a01b161786868151811061224e5761224e613b4d565b60200260200101818152505084600101945050505050611f5c565b508351156122b45760a084015160078d0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561230b565b60208401516122c89060018e019085612bc4565b60078c015460408501516001600160401b03909116101561230b57604084015160078d01805467ffffffffffffffff19166001600160401b039092169190911790555b608090930151909550935090915050955095509592505050565b5f8183106123335781611dff565b5090919050565b60078301546060906001600160401b03165f8184101561235c57839150612361565b508083035b8185101561243757805f036123855761237e600187018684612981565b9250612485565b8482036123928282613c45565b6001600160401b038111156123a9576123a96136ef565b6040519080825280602002602001820160405280156123d2578160200160208202803683370190505b5093505f6123e4600189018886612981565b90505f5b8281101561242f5781818151811061240257612402613b4d565b602002602001015186828151811061241c5761241c613b4d565b60209081029190910101526001016123e8565b505050612485565b6124418585613c58565b6001600160401b03811115612458576124586136ef565b604051908082528060200260200182016040528015612481578160200160208202803683370190505b5092505b50509392505050565b80801561249e5750600360ff8316105b156124bc57604051633494a40d60e21b815260040160405180910390fd5b5050565b5f61ffff821115611807576040516306dfcc6560e41b815260106004820152602481018390526044016117fe565b5f6124f884613240565b90505f6125048461329a565b90505f6125118484613e36565b90508161ffff168161ffff16111561253c5760405163d6516b8960e01b815260040160405180910390fd5b6005909501805461ffff9096166401000000000265ffff00000000199096169590951790945550505050565b5f61257283613315565b61ffff1690508181101561259957604051632c1252af60e11b815260040160405180910390fd5b8190035f6125aa8560010154612d02565b9150505f5b600881101561178e578181815181106125ca576125ca613b4d565b60200260200101518310612604578181815181106125ea576125ea613b4d565b6020026020010151836125fd9190613c58565b925061261d565b604051632c1252af60e11b815260040160405180910390fd5b6001016125af565b6040805160c08101909152821581525f906060908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8801046001600160601b0316815260028a015460078b01546020909201916126989190600160801b90046001600160401b0316613c45565b81525f602082015260078a0154600160c01b90046001600160401b03166040909101528051909150156126e5576126d6611eb96303b5380042613c45565b6001600160601b031660408201525b5f6126f9898360200151846040015161233a565b90505f87516001600160401b03811115612715576127156136ef565b60405190808252806020026020018201604052801561273e578160200160208202803683370190505b5090505f5b88518110156128c9575f61275884868a61319e565b85519091501561277d578460a001805161277190613e11565b6001600160401b031690525b5f6040518060600160405280612792846117cf565b6001600160601b031681526020016127a98f613376565b6001600160401b031681526020018a60ff1681525090506127ee8b84815181106127d5576127d5613b4d565b6020026020010151828e6133c19092919063ffffffff16565b6128488d8c858151811061280457612804613b4d565b6020026020010151604051806060016040528085602001516001600160401b031681526020018e63ffffffff168152602001336001600160a01b0316815250613479565b8051602082015160408301516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b161784848151811061289257612892613b4d565b60200260200101818152505081866080018181516128b09190613c45565b9052505050606084018051600190810190915201612743565b508251156129145760a083015160078b0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561296b565b60208301516129289060018c019084612bc4565b60078a015460408401516001600160401b03909116101561296b57604083015160078b01805467ffffffffffffffff19166001600160401b039092169190911790555b6080909201519350909150509550959350505050565b6060818311156129a45760405163d571362560e01b815260040160405180910390fd5b82820360f08111156129c95760405163d571362560e01b815260040160405180910390fd5b5f816001600160401b038111156129e2576129e26136ef565b604051908082528060200260200182016040528015612a0b578160200160208202803683370190505b5090505f8080612a2460f0895b06600a80820492910690565b5f82815260208c90526040902054919350915062ffffff601883021b895b89811015612aa257602086019550818316601885021c868801528160181b9150600184019350600181019050600a8403612a9d57612a8160f082612a18565b5f82815260208f905260409020549196509450925062ffffff91505b612a42565b50949a9950505050505050505050565b5f818152600483016020526040812054426001600160601b03909116101580612af457505f8281526005840160205260409020544265ffffffffffff90911610155b80611dff57505f828152600684016020526040902054426001600160601b039091161015611dff565b5f818152600384016020526040812080549091906001600160401b03168103612b5957604051635672499160e01b815260040160405180910390fd5b612b6282611848565b15612b8057604051631d80cfbf60e21b815260040160405180910390fd5b505f828152602084905260409020805482546001600160401b03908116600160601b9092041614611bce57604051633c3ca9d760e11b815260040160405180910390fd5b805160f0811115612be85760405163d571362560e01b815260040160405180910390fd5b612bf3815f19613c58565b831115612c135760405163d571362560e01b815260040160405180910390fd5b5f80612c2060f086612a18565b5f8281526020898152604090912054600190960181029592945090925062ffffff601884021b19905b85811015612ce8578681015162ffffff811115612c79576040516369600fad60e11b815260040160405180910390fd5b80601886021b808486161794505060018501945062ffffff601886021b199250600a8503612cdf575f86815260208b81526040909120859055612cc29060f09084048b01612a18565b5f82815260208d905260409020549197509550935062ffffff1992505b50602001612c49565b50505f928352602096909652506040902093909355505050565b60408051600880825261012082019092525f9160609190602082016101008036833750505060f084901c925090505f5b6008811015612d6c578060180284901c62ffffff16828281518110612d5957612d59613b4d565b6020908102919091010152600101612d32565b50915091565b60ff60f01b60f083901b165f5b6008811015612dbc5780601802838281518110612d9e57612d9e613b4d565b602002602001015162ffffff16901b82179150806001019050612d7f565b506001909301929092555050565b5f604051632142170760e11b5f5284600452836024528260445260205f60645f808a5af13d15601f3d1160015f511416171691505f60605280604052508061174d57604051636ff8a60f60e11b815260040160405180910390fd5b6001600160a01b0383165f90815260048501602052604090205482811015612e6057604051638ac4bc7360e01b815260040160405180910390fd5b8115612ee357828103612e82612e7a876001015460f01c90565b60ff16612f3b565b811080612e98575085546001600160601b031681105b80612ea65750856002015481105b15612ec457604051638ac4bc7360e01b815260040160405180910390fd5b6001600160a01b0385165f90815260048701602052604090205561174d565b6001600160a01b0384165f908152600486016020526040902083820390555050505050565b6001600160a01b0382165f90815260048401602052604081208054839290612f31908490613c45565b9091555050505050565b5f60088210612f48575f80fd5b8160018114612f865760028114612f905760038114612f9b5760048114612fa65760058114612fb15760068114612fbc5760078114612fc757612fcf565b6175309150612fcf565b620186a09150612fcf565b620493e09150612fcf565b620f42409150612fcf565b622dc6c09150612fcf565b629896809150612fcf565b6301c9c38091505b50670de0b6b3a764000002919050565b600282015460078301545f91908290829061300a90600160801b90046001600160401b031686613c45565b6130149190613c45565b905061302082826118f9565b95945050505050565b5f81600181146130685760028114613068576003811461307057600481146130795760058114613082576006811461308b576007811461309457613099565b5f9150613099565b60149150613099565b601e9150613099565b60289150613099565b60329150613099565b605091505b50919050565b5f81600181146130de57600281146130e757600381146130f057600481146130f95760058114613102576006811461310b576007811461311457613099565b600f9150613099565b60199150613099565b60239150613099565b602d9150613099565b60379150613099565b60419150613099565b50604b92915050565b5f6008821061312a575f80fd5b816001811461316857600281146131715760038114613070576004811461317a5760058114613183576006811461318c576007811461319557613099565b60019150613099565b60059150613099565b603c9150613099565b60789150613099565b60b49150613099565b5060f092915050565b60a08201516060830151845184515f936001600160401b03169291600101602002908490156131f65760205b828110156131f0578089015191506131e482860185613565565b909501946020016131ca565b50613235565b60205b828110156132275780890151915061321382860185613565565b600183018a830152909501946020016131f9565b5061323285876136ac565b94505b505050509392505050565b60058101545f90620151809061325c9063ffffffff1642613c58565b11613275575060050154640100000000900461ffff1690565b50600501805465ffffffffffff191663ffffffff620151804281900402161790555f90565b5f818015613068576001811461316857600281146132df57600381146132e857600481146132f157600581146132fa5760068114613303576007811461330c57613099565b60029150613099565b60049150613099565b60089150613099565b60109150613099565b60209150613099565b50604092915050565b5f81801561306857600181146132e857600281146132f157600381146132fa5760048114613303576005811461335a5760068114613363576007811461336c57613099565b60409150613099565b60809150613099565b5061010092915050565b600781018054600160401b90046001600160401b031690600861339883613e11565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550919050565b5f82815260208490526040902054600160601b90046001600160401b0316156133fd57604051637f471e3f60e11b815260040160405180910390fd5b5f91825260209283526040918290208151815494830151939092015160ff16600160a01b0260ff60a01b196001600160401b03909416600160601b027fffffffffffffffffffffffff00000000000000000000000000000000000000009095166001600160601b03909316929092179390931791909116179055565b5f8281526003840160205260409020546001600160401b0316156134b05760405163985816ed60e01b815260040160405180910390fd5b5f8281526003840160209081526040918290208351815492850151938501516001600160a01b0316600160601b026001600160601b0363ffffffff909516600160401b026bffffffffffffffffffffffff199094166001600160401b03928316179390931793909316919091179055600784018054909160109161353c91600160801b90910416613e11565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b5f80821161357d575068410d586a20a4c000006112cd565b5f82846064028161359057613590613c6b565b049050602881116135ad5768410d586a20a4c000009150506112cd565b603c8110156135dc57600160288203901c68068155a43676e000000268478eae0e571ba00000019150506112cd565b604681101561360b576001603c8203901c680d02ab486cedc0000002688f1d5c1cae37400000019150506112cd565b605081101561363a57600160468203901c681a055690d9db8000000268dd2d5fcf3bc9c00000019150506112cd565b605a81101561366a57600160508203901c68340aad21b3b7000000026901794d673456eec00000019150506112cd565b60648110156136925760056916deb1154f79eb800000605919830160011c1b049150506112cd565b69492f037764b9580000009150506112cd565b5092915050565b5f60038260ff1610156136c05750816112cd565b606460ff600a6001198501028203168402049392505050565b80356001600160a01b0381168114611953575f80fd5b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b0381118282101715613725576137256136ef565b60405290565b5f82601f83011261373a575f80fd5b813560206001600160401b0380831115613756576137566136ef565b8260051b604051601f19603f8301168101818110848211171561377b5761377b6136ef565b604052938452858101830193838101925087851115613798575f80fd5b83870191505b848210156137b75781358352918301919083019061379e565b979650505050505050565b5f805f805f805f60e0888a0312156137d8575f80fd5b87359650602088013595506137ef604089016136d9565b94506137fd606089016136d9565b935060808801356001600160401b03811115613817575f80fd5b6138238a828b0161372b565b93505060a0880135915061383960c089016136d9565b905092959891949750929550565b5f805f805f60a0868803121561385b575f80fd5b8535945060208601359350613872604087016136d9565b925060608601356001600160401b0381111561388c575f80fd5b6138988882890161372b565b9250506138a7608087016136d9565b90509295509295909350565b5f805f606084860312156138c5575f80fd5b8335925060208401356001600160401b038111156138e1575f80fd5b6138ed8682870161372b565b9250506138fc604085016136d9565b90509250925092565b5f805f805f805f60e0888a03121561391b575f80fd5b8735965060208801359550613932604089016136d9565b9450613940606089016136d9565b93506080880135925060a0880135915061383960c089016136d9565b5f60e0828403121561396c575f80fd5b613974613703565b905061397f826136d9565b815261398d602083016136d9565b602082015260408201356001600160401b038111156139aa575f80fd5b6139b68482850161372b565b604083015250606082013560608201526080820135608082015260a082013560a08201526139e660c083016136d9565b60c082015292915050565b5f805f60608486031215613a03575f80fd5b833592506020840135915060408401356001600160401b03811115613a26575f80fd5b613a328682870161395c565b9150509250925092565b5f805f8060808587031215613a4f575f80fd5b84359350613a5f602086016136d9565b925060408501356001600160401b03811115613a79575f80fd5b613a858782880161372b565b925050613a94606086016136d9565b905092959194509250565b5f805f60608486031215613ab1575f80fd5b505081359360208301359350604090920135919050565b602080825282518282018190525f9190848201906040850190845b81811015613aff57835183529284019291840191600101613ae3565b50909695505050505050565b5f805f8060808587031215613b1e575f80fd5b843593506020850135925060408501356001600160401b03811115613b41575f80fd5b613a858782880161395c565b634e487b7160e01b5f52603260045260245ffd5b5f8151808452602080850194508084015f5b83811015613b8f57815187529582019590820190600101613b73565b509495945050505050565b602081525f611dff6020830184613b61565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176112cd576112cd613bac565b604081525f613be96040830185613b61565b90506001600160a01b03831660208301529392505050565b6001600160601b038281168282160390808211156136a5576136a5613bac565b604081525f613c336040830185613b61565b82810360208401526130208185613b61565b808201808211156112cd576112cd613bac565b818103818111156112cd576112cd613bac565b634e487b7160e01b5f52601260045260245ffd5b5f82613c9957634e487b7160e01b5f52601260045260245ffd5b500690565b634e487b7160e01b5f52603160045260245ffd5b604081525f613cc46040830185613b61565b90508260208301529392505050565b6001600160601b038181168382160190808211156136a5576136a5613bac565b608081525f613d056080830187613b61565b8281036020840152613d178187613b61565b604084019590955250506060015292915050565b60a081525f613d3d60a0830188613b61565b8281036020840152613d4f8188613b61565b6040840196909652505060608101929092526001600160a01b031660809091015292915050565b5f6001600160401b03821680613d8e57613d8e613bac565b5f190192915050565b5f81613da557613da5613bac565b505f190190565b5f600160ff1b8201613dc057613dc0613bac565b505f0390565b5f600160ff1b8201613da557613da5613bac565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613e0a57613e0a613bac565b5060010190565b5f6001600160401b03808316818103613e2c57613e2c613bac565b6001019392505050565b61ffff8181168382160190808211156136a5576136a5613bac56fea164736f6c6343000814000a
Deployed Bytecode
0x73580dee40a126f32e355037954a75326a483f35a53014608060405260043610610090575f3560e01c8063de8b49b511610063578063de8b49b514610125578063e54dc4cb14610144578063f1cd5ef314610163578063f681534614610183575f80fd5b806352fa7ba81461009457806363698228146100b55780636ceb7d5d146100e7578063aa5ba1ef14610106575b5f80fd5b81801561009f575f80fd5b506100b36100ae3660046137c2565b6101a2565b005b8180156100c0575f80fd5b506100d46100cf366004613847565b6103b0565b6040519081526020015b60405180910390f35b8180156100f2575f80fd5b506100b36101013660046138b3565b610791565b818015610111575f80fd5b506100d4610120366004613905565b610891565b818015610130575f80fd5b506100d461013f3660046139f1565b610c5e565b81801561014f575f80fd5b506100b361015e366004613a3c565b610e2a565b610176610171366004613a9f565b610f5d565b6040516100de9190613ac8565b81801561018e575f80fd5b506100d461019d366004613b0b565b610fb3565b5f6101ad87866112b3565b90505f6101bc898587856112d3565b90505f5b8151811015610204576101fc8282815181106101de576101de613b4d565b6020026020010151604001515f198b61162a9092919063ffffffff16565b6001016101c0565b50306001600160a01b038416036102bd5784515f5b81811015610261578a60020187828151811061023757610237613b4d565b6020908102919091018101518254600181810185555f948552929093209092019190915501610219565b50866001600160a01b0316336001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef23896040516102af9190613b9a565b60405180910390a45061034e565b885485516001600160a01b0390911690639dc29fac9033906102e99069d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561032c575f80fd5b505af115801561033e573d5f803e3d5ffd5b5050505061034e87308588611755565b856001600160a01b0316836001600160a01b0316336001600160a01b03167f550bc81d80b31d12a069c70ba86563e82da48c7a5e6fedea04448763ae4b34e8888b60405161039d929190613bd7565b60405180910390a4505050505050505050565b5f806103bc86866112b3565b90505f8085516001600160401b038111156103d9576103d96136ef565b604051908082528060200260200182016040528015610402578160200160208202803683370190505b5090505f86516001600160401b0381111561041f5761041f6136ef565b604051908082528060200260200182016040528015610448578160200160208202803683370190505b5090505f5b87518110156106dc575f88828151811061046957610469613b4d565b602002602001015190505f610487828861179690919063ffffffff16565b6040518060600160405290815f82015f9054906101000a90046001600160601b03166001600160601b03166001600160601b031681526020015f8201600c9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160149054906101000a900460ff1660ff1660ff168152505090505f8d6003015f8481526020019081526020015f206040518060600160405290815f82015f9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160089054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020015f8201600c9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681525050905081602001516001600160401b03165f036105d957604051633494a40d60e21b815260040160405180910390fd5b6105e2816117a7565b806105ff5750602082015181516001600160401b03908116911614155b156106ce578286888151811061061757610617613b4d565b60209081029190910181019190915282519083015160408401516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b161785888151811061066f5761066f613b4d565b6020908102919091010152815160408301516001600160601b0390911699909901986001909701966106a4908e905f1961162a565b5f838152602089905260409020805474ffffffffffffffffffffffffffffffffffffffffff191690555b83600101935050505061044d565b50841561072d576106ec856117cf565b6001850180545f906107089084906001600160601b0316613c01565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b876001600160a01b0316866001600160a01b0316336001600160a01b03167f356980d99a3c896e33a4b9789ada3ddd36a0660f61a6880943544207b0d3ec90858560405161077c929190613c21565b60405180910390a45050505095945050505050565b81515f5b8181101561083f575f8482815181106107b0576107b0613b4d565b602002602001015190505f6107c5878361180b565b90506107d081611848565b6107ed5760405163f1a3275d60e01b815260040160405180910390fd5b6107f681611877565b61081357604051630a0619fd60e01b815260040160405180910390fd5b61081d878361189f565b50600286018054600181810183555f9283526020909220019190915501610795565b50816001600160a01b0316336001600160a01b03167fcbd8592698d587736ff1c8073851b33b9b08ae92c8d0a71678b48eef0af4049f856040516108839190613b9a565b60405180910390a350505050565b5f8315806108a25750600288015484115b156108c0576040516331ea5a2760e21b815260040160405180910390fd5b600288015460078901545f906108e7908390600160801b90046001600160401b0316613c45565b905060506109076108f88885613c58565b6109028985613c58565b6118f9565b10610925576040516331ea5a2760e21b815260040160405180910390fd5b5f866001600160401b0381111561093e5761093e6136ef565b604051908082528060200260200182016040528015610967578160200160208202803683370190505b50335f90815260208c9052604090209091505b8715610aaa5761099261098d85856118f9565b611925565b61099c9086613c45565b60028d0154604080514260208201524491810191909152606081018690529196505f91608001604051602081830303815290604052805190602001205f1c6109e49190613c7f565b60028e0180545f199b8c019b9788019796909601959192509082908110610a0d57610a0d613b4d565b905f5260205f200154838a81518110610a2857610a28613b4d565b602090810291909101015260028d018054610a4590600190613c58565b81548110610a5557610a55613b4d565b905f5260205f2001548d6002018281548110610a7357610a73613b4d565b5f9182526020909120015560028d01805480610a9157610a91613c9e565b600190038181905f5260205f20015f905590555061097a565b6001600160a01b038a165f908152600482016020526040812054610acd90611958565b90505f610ad983611a19565b9050610ae6878383611ac3565b6005850180546001600160601b039092166a01000000000000000000000275ffffffffffffffffffffffff00000000000000000000199092169190911790559650505086851115610b4a57604051638ac4bc7360e01b815260040160405180910390fd5b305f90815260208c905260409020610b679082908c886001611bd6565b8b5482516001600160a01b0390911690639dc29fac903390610b939069d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610bd6575f80fd5b505af1158015610be8573d5f803e3d5ffd5b50505050610bf889308885611755565b886001600160a01b0316866001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc8589604051610c47929190613cb2565b60405180910390a450505050979650505050505050565b608081015160c08201515f91908290610c7a9086908490611bed565b9050610c93868386606001518760400151516001611c33565b845460208501516001600160601b03909116906060905f90610cb6908990611d4d565b90506060610ccf8a838a604001518b606001518a611e06565b600185015491995094509091506001600160601b031680860380891180610cf957508960a0015189115b15610d1757604051638ac4bc7360e01b815260040160405180910390fd5b505f5b8251811015610d5957610d5181848381518110610d3957610d39613b4d565b60200260200101518d61162a9092919063ffffffff16565b600101610d1a565b50610d63886117cf565b610d6d9082613cd3565b6001840180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015610dbf5789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b50505085602001516001600160a01b0316336001600160a01b03167fa2901e7a2f0ce49b98e66b2473ae57e5b2bf1d28cac05c6565d848a813cb16778860400151848a6060015187604051610e179493929190613cf3565b60405180910390a3505050509392505050565b81515f5b81811015610e765785600201848281518110610e4c57610e4c613b4d565b6020908102919091018101518254600181810185555f948552929093209092019190915501610e2e565b5084546001600160a01b03166340c10f1983610e9c8469d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610edf575f80fd5b505af1158015610ef1573d5f803e3d5ffd5b50505050610f0184333086611755565b836001600160a01b0316826001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef2386604051610f4e9190613b9a565b60405180910390a45050505050565b6060610fab846203f4806203f47f86010460078701546001600160601b039190911690610fa6906001600160401b03166203f4806203f47f8801046001600160601b0316612325565b61233a565b949350505050565b5f306001600160a01b03831603610fdd57604051633494a40d60e21b815260040160405180910390fd5b825f01516001600160a01b031683602001516001600160a01b0316141580156110095750606083015115155b1561102757604051633494a40d60e21b815260040160405180910390fd5b608083015160c08401515f906110409087908490611bed565b9050611058878387606001518860400151515f611c33565b5f61106282611958565b90505f866040015151905061109382885f01516001600160a01b031689602001516001600160a01b0316141561248e565b6110a688836110a1846124c0565b6124ee565b6110b1888583612568565b50508554604086015160208701516001600160601b03909216916060905f906110db908b90611d4d565b90506110ee8b82858c606001518a612625565b600183015491985092506001600160601b03168086038089118061111557508a60a0015189115b1561113357604051638ac4bc7360e01b815260040160405180910390fd5b5061113d886117cf565b6111479082613cd3565b6001830180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015611199578a546bffffffffffffffffffffffff19166001600160601b0382169081178c5594505b505081516111aa908a90879061162a565b895482516001600160a01b03909116906340c10f199089906111d69069d3c21bcecceda1000000613bc0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015611219575f80fd5b505af115801561122b573d5f803e3d5ffd5b5050505061123e885f0151333085611755565b87602001516001600160a01b0316876001600160a01b0316336001600160a01b03167faa75244fec84b237a00e565f2d9ef15139490324abdb7807bd587432976b9a2085858d60600151898f5f015160405161129e959493929190613d2b565b60405180910390a45050505050949350505050565b6001600160a01b0381165f90815260038301602052604090205b92915050565b60605f841180156112e357504284105b1561130157604051631d80cfbf60e21b815260040160405180910390fd5b5f83516001600160401b0381111561131b5761131b6136ef565b60405190808252806020026020018201604052801561136457816020015b604080516060810182525f80825260208083018290529282015282525f199092019101816113395790505b5090506001600160601b036203f480426203f47f0104165f606087156113c65760078901545f906113b2906001600160601b036203f4806203f47f8d010416906001600160401b0316612325565b90506113c260018b018583612981565b9150505b5f5b87518110156115bc575f8882815181106113e4576113e4613b4d565b602002602001015190506113f88b82612ab2565b1561141657604051636bb8676960e11b815260040160405180910390fd5b5f806114238d8b85612b1d565b8054919350915061143d906001600160601b031687613c45565b8254909650600160401b900463ffffffff1661149e5760078d01805460189061147590600160c01b90046001600160401b0316613d76565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550611522565b81545f9088906114c590600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b03166114d89190613c58565b905085518111156114e7575f80fd5b5f5b8181101561151f5786818151811061150357611503613b4d565b60200260200101805161151590613d97565b90526001016114e9565b50505b6040805160608101825282546001600160601b0381168252600160601b81046001600160401b03166020830152600160a01b900460ff1691810191909152885189908690811061157457611574613b4d565b60200260200101819052506115898d8461189f565b50505f908152602088905260409020805474ffffffffffffffffffffffffffffffffffffffffff191690556001016113c8565b506115c6826117cf565b6001870180545f906115e29084906001600160601b0316613c01565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505f8151111561161d5761161d60018a018483612bc4565b5091979650505050505050565b5f8260ff1611801561163b57508015155b15611750575f8061164f8560010154612d02565b915091505f8312156117035761166483613dac565b818560ff168151811061167957611679613b4d565b6020026020010181815161168d9190613c58565b90525060ff8481169083161480156116c05750808460ff16815181106116b5576116b5613b4d565b60200260200101515f145b156116fe57835b5f190160ff8116158015906116f75750818160ff16815181106116ec576116ec613b4d565b60200260200101515f145b6116c75791505b611742565b82818560ff168151811061171957611719613b4d565b6020026020010181815161172d9190613c45565b90525060ff8083169085161115611742578391505b61174d858383612d72565b50505b505050565b80515f5b8181101561178e5761178686868686858151811061177957611779613b4d565b6020026020010151612dca565b600101611759565b505050505050565b5f9081526020919091526040902090565b5f816020015163ffffffff165f141580156112cd575050602001514263ffffffff9091161090565b5f6001600160601b03821115611807576040516306dfcc6560e41b815260606004820152602481018390526044015b60405180910390fd5b5090565b5f8181526003830160205260408120805490916001600160401b0390911690036112cd57604051635672499160e01b815260040160405180910390fd5b80545f90600160401b900463ffffffff16158015906112cd5750505442600160401b90910463ffffffff161090565b80545f904290611898906201518090600160401b900463ffffffff16613c45565b1092915050565b5f8181526003830160205260408120556007820180546010906118d190600160801b90046001600160401b0316613d76565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505050565b5f815f0361190857505f6112cd565b81836064028161191a5761191a613c6b565b0460640390506112cd565b5f602882101561193657505f919050565b600560271983010460020a683635c9adc5dea00000029050919050565b919050565b5f69065a4da25d3016c0000082101561197257505f919050565b69152d02c7e14af680000082101561198c57506001919050565b693f870857a3e0e38000008210156119a657506002919050565b69d3c21bcecceda10000008210156119c057506003919050565b6a027b46536c66c8e30000008210156119db57506004919050565b6a084595161401484a0000008210156119f657506005919050565b6a18d0bf423c03d8de000000821015611a1157506006919050565b506007919050565b60058101545f906201518090611a3f906601000000000000900463ffffffff1642613c58565b11611a635750600501546a010000000000000000000090046001600160601b031690565b5060050180547fffffffffffffffffffff00000000000000000000000000000000ffffffffffff16660100000000000062015180428190040263ffffffff160275ffffffffffffffffffffffff0000000000000000000019161790555f90565b5f80670de0b6b3a7640000848015611b125760018114611b1a5760028114611b265760038114611b325760048114611b3e5760058114611b4a5760068114611b565760078114611b6257611b6b565b5f9150611b6b565b816107d0029150611b6b565b81610fa0029150611b6b565b81611f40029150611b6b565b81613e80029150611b6b565b81617d00029150611b6b565b8161fa00029150611b6b565b816201f4000291505b50836001600160601b0316816001600160601b031611611b915785849250925050611bce565b8390036001600160601b038116861015611bbb57505f9150506001600160601b0382168401611bce565b6001600160601b03811686039250830190505b935093915050565b611be285848484612e25565b61174d848484612f08565b6001600160a01b0381165f90815260048401602052604081205480611c148560ff16612f3b565b1115610fab576040516369f131af60e11b815260040160405180910390fd5b60088460ff16101580611c47575060ff8416155b15611c6557604051633494a40d60e21b815260040160405180910390fd5b5f611c708684612fdf565b90505f8215611c96578415611c86576064611c8f565b611c8f86613029565b9050611cc2565b611c9f8661309f565b9050845f03611cc2575f611cb287613029565b905080821115611cc0578091505b505b80821115611ce357604051633494a40d60e21b815260040160405180910390fd5b8415611d44575f6203f4806203f47f4201046203f4806203f47f880104036001600160601b03169050805f1480611d245750611d218760ff1661311d565b81115b15611d4257604051633494a40d60e21b815260040160405180910390fd5b505b50505050505050565b6001600160a01b038082165f9081526003840160205260408120600181015491929091600160601b900416611dff578354600160601b90046001600160a01b0316611dc55783546001600160a01b038416600160601b9081026001600160601b03928316178655600183018054909216179055611dff565b83546001820180546001600160a01b03600160601b93849004811684026001600160601b0392831617909255865491861690920291161784555b9392505050565b6040805160c08101909152821581526060905f9082908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8901046001600160601b0316815260028b015460078c0154602090920191611e7b9190600160801b90046001600160401b0316613c45565b81525f602082015260078b0154600160c01b90046001600160401b0316604090910152805190915015611ed857611ec9611eb96303b5380042613c45565b6203f4806203f47f919091010490565b6001600160601b031660408201525b5f611eec8a8360200151846040015161233a565b60408051600880825261012082019092529192505f919060208201610100803683370190505090505f89516001600160401b03811115611f2e57611f2e6136ef565b604051908082528060200260200182016040528015611f57578160200160208202803683370190505b5090505f5b8a5181101561226957611f888d8c8381518110611f7b57611f7b613b4d565b6020026020010151612ab2565b15611fa657604051636bb8676960e11b815260040160405180910390fd5b5f80611fcc8f8f8f8681518110611fbf57611fbf613b4d565b6020026020010151612b1d565b602089015182549294509092505f91611ffc90600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b031661200f9190613c58565b90505f5b8181101561207f5787818151811061202d5761202d613b4d565b60200260200101515f0361205457604051633494a40d60e21b815260040160405180910390fd5b87818151811061206657612066613b4d565b6020908102919091010180515f19019052600101612013565b50505f61208d87898e61319e565b82549091506001600160601b03168111156120f55781546120b7906001600160601b031682613c58565b886080018181516120c89190613c45565b9052506120d4816117cf565b82546bffffffffffffffffffffffff19166001600160601b03919091161782555b815460ff600160a01b9091048116908d1681101561217e57825460ff808f16600160a01b0260ff60a01b1990921691909117845587518891831690811061213e5761213e613b4d565b60200260200101805161215090613dc6565b90528651879060ff8f1690811061216957612169613b4d565b60200260200101805161217b90613dda565b90525b8851156121b75783546bffffffff00000000000000001916845560a0890180516121a790613e11565b6001600160401b031690526121db565b835463ffffffff8f16600160401b026bffffffff0000000000000000199091161784555b604080516060808201835285546001600160601b038116808452600160601b82046001600160401b0381166020860152600160a01b90920460ff81169490950193909352901b67ffffffffffffffff60601b161760a09190911b60ff60a01b161786868151811061224e5761224e613b4d565b60200260200101818152505084600101945050505050611f5c565b508351156122b45760a084015160078d0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561230b565b60208401516122c89060018e019085612bc4565b60078c015460408501516001600160401b03909116101561230b57604084015160078d01805467ffffffffffffffff19166001600160401b039092169190911790555b608090930151909550935090915050955095509592505050565b5f8183106123335781611dff565b5090919050565b60078301546060906001600160401b03165f8184101561235c57839150612361565b508083035b8185101561243757805f036123855761237e600187018684612981565b9250612485565b8482036123928282613c45565b6001600160401b038111156123a9576123a96136ef565b6040519080825280602002602001820160405280156123d2578160200160208202803683370190505b5093505f6123e4600189018886612981565b90505f5b8281101561242f5781818151811061240257612402613b4d565b602002602001015186828151811061241c5761241c613b4d565b60209081029190910101526001016123e8565b505050612485565b6124418585613c58565b6001600160401b03811115612458576124586136ef565b604051908082528060200260200182016040528015612481578160200160208202803683370190505b5092505b50509392505050565b80801561249e5750600360ff8316105b156124bc57604051633494a40d60e21b815260040160405180910390fd5b5050565b5f61ffff821115611807576040516306dfcc6560e41b815260106004820152602481018390526044016117fe565b5f6124f884613240565b90505f6125048461329a565b90505f6125118484613e36565b90508161ffff168161ffff16111561253c5760405163d6516b8960e01b815260040160405180910390fd5b6005909501805461ffff9096166401000000000265ffff00000000199096169590951790945550505050565b5f61257283613315565b61ffff1690508181101561259957604051632c1252af60e11b815260040160405180910390fd5b8190035f6125aa8560010154612d02565b9150505f5b600881101561178e578181815181106125ca576125ca613b4d565b60200260200101518310612604578181815181106125ea576125ea613b4d565b6020026020010151836125fd9190613c58565b925061261d565b604051632c1252af60e11b815260040160405180910390fd5b6001016125af565b6040805160c08101909152821581525f906060908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8801046001600160601b0316815260028a015460078b01546020909201916126989190600160801b90046001600160401b0316613c45565b81525f602082015260078a0154600160c01b90046001600160401b03166040909101528051909150156126e5576126d6611eb96303b5380042613c45565b6001600160601b031660408201525b5f6126f9898360200151846040015161233a565b90505f87516001600160401b03811115612715576127156136ef565b60405190808252806020026020018201604052801561273e578160200160208202803683370190505b5090505f5b88518110156128c9575f61275884868a61319e565b85519091501561277d578460a001805161277190613e11565b6001600160401b031690525b5f6040518060600160405280612792846117cf565b6001600160601b031681526020016127a98f613376565b6001600160401b031681526020018a60ff1681525090506127ee8b84815181106127d5576127d5613b4d565b6020026020010151828e6133c19092919063ffffffff16565b6128488d8c858151811061280457612804613b4d565b6020026020010151604051806060016040528085602001516001600160401b031681526020018e63ffffffff168152602001336001600160a01b0316815250613479565b8051602082015160408301516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b161784848151811061289257612892613b4d565b60200260200101818152505081866080018181516128b09190613c45565b9052505050606084018051600190810190915201612743565b508251156129145760a083015160078b0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561296b565b60208301516129289060018c019084612bc4565b60078a015460408401516001600160401b03909116101561296b57604083015160078b01805467ffffffffffffffff19166001600160401b039092169190911790555b6080909201519350909150509550959350505050565b6060818311156129a45760405163d571362560e01b815260040160405180910390fd5b82820360f08111156129c95760405163d571362560e01b815260040160405180910390fd5b5f816001600160401b038111156129e2576129e26136ef565b604051908082528060200260200182016040528015612a0b578160200160208202803683370190505b5090505f8080612a2460f0895b06600a80820492910690565b5f82815260208c90526040902054919350915062ffffff601883021b895b89811015612aa257602086019550818316601885021c868801528160181b9150600184019350600181019050600a8403612a9d57612a8160f082612a18565b5f82815260208f905260409020549196509450925062ffffff91505b612a42565b50949a9950505050505050505050565b5f818152600483016020526040812054426001600160601b03909116101580612af457505f8281526005840160205260409020544265ffffffffffff90911610155b80611dff57505f828152600684016020526040902054426001600160601b039091161015611dff565b5f818152600384016020526040812080549091906001600160401b03168103612b5957604051635672499160e01b815260040160405180910390fd5b612b6282611848565b15612b8057604051631d80cfbf60e21b815260040160405180910390fd5b505f828152602084905260409020805482546001600160401b03908116600160601b9092041614611bce57604051633c3ca9d760e11b815260040160405180910390fd5b805160f0811115612be85760405163d571362560e01b815260040160405180910390fd5b612bf3815f19613c58565b831115612c135760405163d571362560e01b815260040160405180910390fd5b5f80612c2060f086612a18565b5f8281526020898152604090912054600190960181029592945090925062ffffff601884021b19905b85811015612ce8578681015162ffffff811115612c79576040516369600fad60e11b815260040160405180910390fd5b80601886021b808486161794505060018501945062ffffff601886021b199250600a8503612cdf575f86815260208b81526040909120859055612cc29060f09084048b01612a18565b5f82815260208d905260409020549197509550935062ffffff1992505b50602001612c49565b50505f928352602096909652506040902093909355505050565b60408051600880825261012082019092525f9160609190602082016101008036833750505060f084901c925090505f5b6008811015612d6c578060180284901c62ffffff16828281518110612d5957612d59613b4d565b6020908102919091010152600101612d32565b50915091565b60ff60f01b60f083901b165f5b6008811015612dbc5780601802838281518110612d9e57612d9e613b4d565b602002602001015162ffffff16901b82179150806001019050612d7f565b506001909301929092555050565b5f604051632142170760e11b5f5284600452836024528260445260205f60645f808a5af13d15601f3d1160015f511416171691505f60605280604052508061174d57604051636ff8a60f60e11b815260040160405180910390fd5b6001600160a01b0383165f90815260048501602052604090205482811015612e6057604051638ac4bc7360e01b815260040160405180910390fd5b8115612ee357828103612e82612e7a876001015460f01c90565b60ff16612f3b565b811080612e98575085546001600160601b031681105b80612ea65750856002015481105b15612ec457604051638ac4bc7360e01b815260040160405180910390fd5b6001600160a01b0385165f90815260048701602052604090205561174d565b6001600160a01b0384165f908152600486016020526040902083820390555050505050565b6001600160a01b0382165f90815260048401602052604081208054839290612f31908490613c45565b9091555050505050565b5f60088210612f48575f80fd5b8160018114612f865760028114612f905760038114612f9b5760048114612fa65760058114612fb15760068114612fbc5760078114612fc757612fcf565b6175309150612fcf565b620186a09150612fcf565b620493e09150612fcf565b620f42409150612fcf565b622dc6c09150612fcf565b629896809150612fcf565b6301c9c38091505b50670de0b6b3a764000002919050565b600282015460078301545f91908290829061300a90600160801b90046001600160401b031686613c45565b6130149190613c45565b905061302082826118f9565b95945050505050565b5f81600181146130685760028114613068576003811461307057600481146130795760058114613082576006811461308b576007811461309457613099565b5f9150613099565b60149150613099565b601e9150613099565b60289150613099565b60329150613099565b605091505b50919050565b5f81600181146130de57600281146130e757600381146130f057600481146130f95760058114613102576006811461310b576007811461311457613099565b600f9150613099565b60199150613099565b60239150613099565b602d9150613099565b60379150613099565b60419150613099565b50604b92915050565b5f6008821061312a575f80fd5b816001811461316857600281146131715760038114613070576004811461317a5760058114613183576006811461318c576007811461319557613099565b60019150613099565b60059150613099565b603c9150613099565b60789150613099565b60b49150613099565b5060f092915050565b60a08201516060830151845184515f936001600160401b03169291600101602002908490156131f65760205b828110156131f0578089015191506131e482860185613565565b909501946020016131ca565b50613235565b60205b828110156132275780890151915061321382860185613565565b600183018a830152909501946020016131f9565b5061323285876136ac565b94505b505050509392505050565b60058101545f90620151809061325c9063ffffffff1642613c58565b11613275575060050154640100000000900461ffff1690565b50600501805465ffffffffffff191663ffffffff620151804281900402161790555f90565b5f818015613068576001811461316857600281146132df57600381146132e857600481146132f157600581146132fa5760068114613303576007811461330c57613099565b60029150613099565b60049150613099565b60089150613099565b60109150613099565b60209150613099565b50604092915050565b5f81801561306857600181146132e857600281146132f157600381146132fa5760048114613303576005811461335a5760068114613363576007811461336c57613099565b60409150613099565b60809150613099565b5061010092915050565b600781018054600160401b90046001600160401b031690600861339883613e11565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550919050565b5f82815260208490526040902054600160601b90046001600160401b0316156133fd57604051637f471e3f60e11b815260040160405180910390fd5b5f91825260209283526040918290208151815494830151939092015160ff16600160a01b0260ff60a01b196001600160401b03909416600160601b027fffffffffffffffffffffffff00000000000000000000000000000000000000009095166001600160601b03909316929092179390931791909116179055565b5f8281526003840160205260409020546001600160401b0316156134b05760405163985816ed60e01b815260040160405180910390fd5b5f8281526003840160209081526040918290208351815492850151938501516001600160a01b0316600160601b026001600160601b0363ffffffff909516600160401b026bffffffffffffffffffffffff199094166001600160401b03928316179390931793909316919091179055600784018054909160109161353c91600160801b90910416613e11565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b5f80821161357d575068410d586a20a4c000006112cd565b5f82846064028161359057613590613c6b565b049050602881116135ad5768410d586a20a4c000009150506112cd565b603c8110156135dc57600160288203901c68068155a43676e000000268478eae0e571ba00000019150506112cd565b604681101561360b576001603c8203901c680d02ab486cedc0000002688f1d5c1cae37400000019150506112cd565b605081101561363a57600160468203901c681a055690d9db8000000268dd2d5fcf3bc9c00000019150506112cd565b605a81101561366a57600160508203901c68340aad21b3b7000000026901794d673456eec00000019150506112cd565b60648110156136925760056916deb1154f79eb800000605919830160011c1b049150506112cd565b69492f037764b9580000009150506112cd565b5092915050565b5f60038260ff1610156136c05750816112cd565b606460ff600a6001198501028203168402049392505050565b80356001600160a01b0381168114611953575f80fd5b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b0381118282101715613725576137256136ef565b60405290565b5f82601f83011261373a575f80fd5b813560206001600160401b0380831115613756576137566136ef565b8260051b604051601f19603f8301168101818110848211171561377b5761377b6136ef565b604052938452858101830193838101925087851115613798575f80fd5b83870191505b848210156137b75781358352918301919083019061379e565b979650505050505050565b5f805f805f805f60e0888a0312156137d8575f80fd5b87359650602088013595506137ef604089016136d9565b94506137fd606089016136d9565b935060808801356001600160401b03811115613817575f80fd5b6138238a828b0161372b565b93505060a0880135915061383960c089016136d9565b905092959891949750929550565b5f805f805f60a0868803121561385b575f80fd5b8535945060208601359350613872604087016136d9565b925060608601356001600160401b0381111561388c575f80fd5b6138988882890161372b565b9250506138a7608087016136d9565b90509295509295909350565b5f805f606084860312156138c5575f80fd5b8335925060208401356001600160401b038111156138e1575f80fd5b6138ed8682870161372b565b9250506138fc604085016136d9565b90509250925092565b5f805f805f805f60e0888a03121561391b575f80fd5b8735965060208801359550613932604089016136d9565b9450613940606089016136d9565b93506080880135925060a0880135915061383960c089016136d9565b5f60e0828403121561396c575f80fd5b613974613703565b905061397f826136d9565b815261398d602083016136d9565b602082015260408201356001600160401b038111156139aa575f80fd5b6139b68482850161372b565b604083015250606082013560608201526080820135608082015260a082013560a08201526139e660c083016136d9565b60c082015292915050565b5f805f60608486031215613a03575f80fd5b833592506020840135915060408401356001600160401b03811115613a26575f80fd5b613a328682870161395c565b9150509250925092565b5f805f8060808587031215613a4f575f80fd5b84359350613a5f602086016136d9565b925060408501356001600160401b03811115613a79575f80fd5b613a858782880161372b565b925050613a94606086016136d9565b905092959194509250565b5f805f60608486031215613ab1575f80fd5b505081359360208301359350604090920135919050565b602080825282518282018190525f9190848201906040850190845b81811015613aff57835183529284019291840191600101613ae3565b50909695505050505050565b5f805f8060808587031215613b1e575f80fd5b843593506020850135925060408501356001600160401b03811115613b41575f80fd5b613a858782880161395c565b634e487b7160e01b5f52603260045260245ffd5b5f8151808452602080850194508084015f5b83811015613b8f57815187529582019590820190600101613b73565b509495945050505050565b602081525f611dff6020830184613b61565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176112cd576112cd613bac565b604081525f613be96040830185613b61565b90506001600160a01b03831660208301529392505050565b6001600160601b038281168282160390808211156136a5576136a5613bac565b604081525f613c336040830185613b61565b82810360208401526130208185613b61565b808201808211156112cd576112cd613bac565b818103818111156112cd576112cd613bac565b634e487b7160e01b5f52601260045260245ffd5b5f82613c9957634e487b7160e01b5f52601260045260245ffd5b500690565b634e487b7160e01b5f52603160045260245ffd5b604081525f613cc46040830185613b61565b90508260208301529392505050565b6001600160601b038181168382160190808211156136a5576136a5613bac565b608081525f613d056080830187613b61565b8281036020840152613d178187613b61565b604084019590955250506060015292915050565b60a081525f613d3d60a0830188613b61565b8281036020840152613d4f8188613b61565b6040840196909652505060608101929092526001600160a01b031660809091015292915050565b5f6001600160401b03821680613d8e57613d8e613bac565b5f190192915050565b5f81613da557613da5613bac565b505f190190565b5f600160ff1b8201613dc057613dc0613bac565b505f0390565b5f600160ff1b8201613da557613da5613bac565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613e0a57613e0a613bac565b5060010190565b5f6001600160401b03808316818103613e2c57613e2c613bac565b6001019392505050565b61ffff8181168382160190808211156136a5576136a5613bac56fea164736f6c6343000814000a
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.