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 ClaimExpiredNft( address indexed operator, address indexed receiver, address indexed collection, uint256[] tokenIds, uint256 creditCost, address proxyCollection ); 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(); uint8 vipLevel = uint8(param.vipLevel); uint256 totalCredit = account.ensureVipCredit(vipLevel, param.creditToken); Helper.ensureMaxLocking(collection, vipLevel, param.expiryTs, param.nftIds.length); Helper.ensureProxyVipLevel(Constants.getVipLevel(totalCredit), param.collection != param.proxyCollection); /// 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); collection.floorToken.burn(msg.sender, Constants.FLOOR_TOKEN_AMOUNT * nftIds.length); for (uint256 i = 0; i < releasedKeys.length;) { userAccount.updateVipKeyCount(releasedKeys[i].vipLevel, -1); unchecked { ++i; } } 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 claimExpiredNfts( CollectionState storage collectionState, mapping(address => UserFloorAccount) storage userAccounts, address creditToken, address proxyCollection, address collectionId, uint256[] memory nftIds, uint256 maxCreditCost, address receiver ) public returns (uint256 totalCreditCost) { for (uint256 i = 0; i < nftIds.length;) { SafeBox storage safeBox = Helper.useSafeBox(collectionState, nftIds[i]); if (!safeBox.isSafeBoxExpired()) revert Errors.SafeBoxHasNotExpire(); if (!Helper.isAuctionPeriodOver(safeBox)) revert Errors.AuctionHasNotCompleted(); removeSafeBox(collectionState, nftIds[i]); unchecked { ++i; } } uint256 currentlyLocked = Helper.getActiveSafeBoxes(collectionState, block.timestamp) + collectionState.infiniteCnt; UserFloorAccount storage userAccount = userAccounts[msg.sender]; totalCreditCost = nftIds.length * Constants.getClaimExpiredCost( currentlyLocked, collectionState.activeSafeBoxCnt + collectionState.freeTokenIds.length, Constants.getVipLevel(userAccount.tokenBalance(creditToken)) ); if (totalCreditCost > maxCreditCost) { revert Errors.InsufficientCredit(); } userAccount.transferToken(userAccounts[address(this)], creditToken, totalCreditCost, true); collectionState.floorToken.burn(msg.sender, Constants.FLOOR_TOKEN_AMOUNT * nftIds.length); ERC721Transfer.safeBatchTransferFrom(proxyCollection, address(this), receiver, nftIds); emit ClaimExpiredNft(msg.sender, receiver, collectionId, nftIds, totalCreditCost, proxyCollection); } 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); 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 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 currentlyLocked = Helper.getActiveSafeBoxes(collection, block.timestamp) + collection.infiniteCnt; uint256 totalManaged = collection.activeSafeBoxCnt + collection.freeTokenIds.length; uint256[] memory selectedTokenIds = new uint256[](claimCnt); UserFloorAccount storage userAccount = userAccounts[msg.sender]; uint8 vipLevel = Constants.getVipLevel(userAccount.tokenBalance(creditToken)); while (claimCnt > 0) { totalCreditCost += Constants.getClaimRandomCost(currentlyLocked, totalManaged, vipLevel); /// just compute a deterministic random number uint256 chosenNftIdx = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, totalManaged))) % collection.freeTokenIds.length; unchecked { --claimCnt; --totalManaged; } selectedTokenIds[claimCnt] = collection.freeTokenIds[chosenNftIdx]; collection.freeTokenIds[chosenNftIdx] = collection.freeTokenIds[collection.freeTokenIds.length - 1]; collection.freeTokenIds.pop(); } 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 = 100 ether; /// @dev admin fee charged per NFT when someone starts aution on expired safebox uint256 public constant AUCTION_ON_EXPIRED_SAFEBOX_COST = 100 ether; /// @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 LOCKING_RAMP_UP_THRESHOLD = 20; uint256 public constant LOCKING_PCT_TO_SAFEBOX_MAINT_MIN = 1200 ether; uint256 public constant LOCKING_PCT_TO_SAFEBOX_MAINT_MAX = 345600 ether; /// @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 } } } 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 < Constants.LOCKING_RAMP_UP_THRESHOLD) { 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; } } } function getVipClaimCostWithDiscount(uint256 cost, uint8 vipLevel) internal pure returns (uint256) { if (vipLevel < 3) { return cost; } unchecked { uint256 discount = 4000 ether << (vipLevel - 3); if (cost < discount) { return 0; } else { return cost - discount; } } } function getClaimExpiredCost(uint256 locked, uint256 totalManaged, uint8 vipLevel) internal pure returns (uint256) { uint256 realCost = getClaimCost(locked, totalManaged); return getVipClaimCostWithDiscount(realCost, vipLevel); } function getClaimRandomCost(uint256 locked, uint256 totalManaged, uint8 vipLevel) internal pure returns (uint256) { uint256 realCost = getClaimCost(locked, totalManaged); return getVipClaimCostWithDiscount(realCost, vipLevel); } function getClaimCost(uint256 locked, uint256 totalManaged) private pure returns (uint256) { if (totalManaged < Constants.LOCKING_RAMP_UP_THRESHOLD) { return 0; } unchecked { uint256 lockingRatioPct = locked * 100 / totalManaged; if (lockingRatioPct <= 60) { return 0; } else if (lockingRatioPct < 100) { uint256 cost = 1 ether; assembly { switch shr(1, sub(lockingRatioPct, 60)) case 0 { cost := mul(cost, 400) } case 1 { cost := mul(cost, 600) } case 2 { cost := mul(cost, 800) } case 3 { cost := mul(cost, 1200) } case 4 { cost := mul(cost, 1600) } case 5 { cost := mul(cost, 2400) } case 6 { cost := mul(cost, 3200) } case 7 { cost := mul(cost, 4800) } case 8 { cost := mul(cost, 6400) } case 9 { cost := mul(cost, 9600) } case 10 { cost := mul(cost, 12800) } case 11 { cost := mul(cost, 19200) } case 12 { cost := mul(cost, 25600) } case 13 { cost := mul(cost, 38400) } case 14 { cost := mul(cost, 51200) } case 15 { cost := mul(cost, 76800) } case 16 { cost := mul(cost, 102400) } case 17 { cost := mul(cost, 153600) } case 18 { cost := mul(cost, 204800) } case 19 { cost := mul(cost, 307200) } } return cost; } else { return 307200 ether; } } } }
// 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(); }
// 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 Claim `nftIds` which had been locked and had expired /// 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 claimExpiredNfts(address collection, uint256[] memory nftIds, uint256 maxCredit, address receiver) external returns (uint256); /// @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, uint256 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 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 ) internal view { /// vip level 0 can not use safebox utilities. if (vipLevel >= Constants.VIP_LEVEL_COUNT || vipLevel == 0) { 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(); } } else { uint256 expectLocked = requireLockCnt + collection.infiniteCnt + getActiveSafeBoxes(collection, block.timestamp); uint256 totalManaged = requireLockCnt + collection.activeSafeBoxCnt + collection.freeTokenIds.length; uint256 lockingRatio = (expectLocked * 100 / totalManaged); uint256 restrictRatio = Constants.getLockingRatioForInfinite(vipLevel); if (lockingRatio > restrictRatio) { 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); } }
// 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; } 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; } 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":"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"},{"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"},{"indexed":false,"internalType":"address","name":"proxyCollection","type":"address"}],"name":"ClaimExpiredNft","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":"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[]"},{"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
613c34610035600b8282823980515f1a60731461002957634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610090575f3560e01c8063de8b49b511610063578063de8b49b514610125578063e54dc4cb14610144578063f1cd5ef314610163578063f681534614610183575f80fd5b806352fa7ba81461009457806363698228146100b55780638093a148146100e7578063aa5ba1ef14610106575b5f80fd5b81801561009f575f80fd5b506100b36100ae366004613537565b6101a2565b005b8180156100c0575f80fd5b506100d46100cf3660046135bc565b6102f7565b6040519081526020015b60405180910390f35b8180156100f2575f80fd5b506100d4610101366004613628565b6106d8565b818015610111575f80fd5b506100d46101203660046136be565b610957565b818015610130575f80fd5b506100d461013f3660046137aa565b610caf565b81801561014f575f80fd5b506100b361015e3660046137f5565b610e79565b610176610171366004613858565b610fac565b6040516100de9190613881565b81801561018e575f80fd5b506100d461019d3660046138c4565b611002565b5f6101ad878661128a565b90505f6101bc898587856112aa565b895486519192506001600160a01b031690639dc29fac9033906101e99069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561022c575f80fd5b505af115801561023e573d5f803e3d5ffd5b505050505f5b81518110156102885761028082828151811061026257610262613931565b6020026020010151604001515f198b6116019092919063ffffffff16565b600101610244565b506102958730858861172c565b856001600160a01b0316836001600160a01b0316336001600160a01b03167f550bc81d80b31d12a069c70ba86563e82da48c7a5e6fedea04448763ae4b34e8888b6040516102e492919061397e565b60405180910390a4505050505050505050565b5f80610303868661128a565b90505f8085516001600160401b0381111561032057610320613464565b604051908082528060200260200182016040528015610349578160200160208202803683370190505b5090505f86516001600160401b0381111561036657610366613464565b60405190808252806020026020018201604052801561038f578160200160208202803683370190505b5090505f5b8751811015610623575f8882815181106103b0576103b0613931565b602002602001015190505f6103ce828861176d90919063ffffffff16565b6040518060600160405290815f82015f9054906101000a90046001600160601b03166001600160601b03166001600160601b031681526020015f8201600c9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160149054906101000a900460ff1660ff1660ff168152505090505f8d6003015f8481526020019081526020015f206040518060600160405290815f82015f9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160089054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020015f8201600c9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681525050905081602001516001600160401b03165f0361052057604051633494a40d60e21b815260040160405180910390fd5b6105298161177e565b806105465750602082015181516001600160401b03908116911614155b15610615578286888151811061055e5761055e613931565b60209081029190910181019190915282519083015160408401516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b16178588815181106105b6576105b6613931565b6020908102919091010152815160408301516001600160601b0390911699909901986001909701966105eb908e905f19611601565b5f838152602089905260409020805474ffffffffffffffffffffffffffffffffffffffffff191690555b836001019350505050610394565b50841561067457610633856117a6565b6001850180545f9061064f9084906001600160601b03166139a8565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b876001600160a01b0316866001600160a01b0316336001600160a01b03167f356980d99a3c896e33a4b9789ada3ddd36a0660f61a6880943544207b0d3ec9085856040516106c39291906139c8565b60405180910390a45050505095945050505050565b5f805b8451811015610782575f6107088b8784815181106106fb576106fb613931565b60200260200101516117e1565b90506107138161181e565b6107305760405163f1a3275d60e01b815260040160405180910390fd5b6107398161184d565b61075657604051630a0619fd60e01b815260040160405180910390fd5b6107798b87848151811061076c5761076c613931565b6020026020010151611875565b506001016106db565b5060078901545f90600160c01b90046001600160401b03166107a48b426118cf565b6107ae91906139ec565b335f90815260208b90526040902060028c015460078d015492935090916108139184916107eb9190600160801b90046001600160401b03166139ec565b6001600160a01b038c165f90815260048501602052604090205461080e90611911565b6119d7565b865161081f919061391a565b92508483111561084257604051638ac4bc7360e01b815260040160405180910390fd5b305f90815260208b90526040902061085f9082908b8660016119f8565b8a5486516001600160a01b0390911690639dc29fac90339061088b9069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b1580156108ce575f80fd5b505af11580156108e0573d5f803e3d5ffd5b505050506108f08830868961172c565b866001600160a01b0316846001600160a01b0316336001600160a01b03167f32297e00acad9155e59ec3f8584928858ccaf0d988b7fee2262cfa08696debaf89878d604051610941939291906139ff565b60405180910390a4505098975050505050505050565b5f8315806109685750600288015484115b15610986576040516331ea5a2760e21b815260040160405180910390fd5b60078801545f90600160c01b90046001600160401b03166109a78a426118cf565b6109b191906139ec565b60028a015460078b01549192505f916109da9190600160801b90046001600160401b03166139ec565b90505f866001600160401b038111156109f5576109f5613464565b604051908082528060200260200182016040528015610a1e578160200160208202803683370190505b50335f90815260208c815260408083206001600160a01b038e1684526004810190925282205492935091610a5190611911565b90505b8815610b7957610a658585836119d7565b610a6f90876139ec565b60028e0154604080514260208201524491810191909152606081018790529197505f91608001604051602081830303815290604052805190602001205f1c610ab79190613a44565b905089600190039950846001900394508d6002018181548110610adc57610adc613931565b905f5260205f200154848b81518110610af757610af7613931565b602090810291909101015260028e018054610b1490600190613a57565b81548110610b2457610b24613931565b905f5260205f2001548e6002018281548110610b4257610b42613931565b5f9182526020909120015560028e01805480610b6057610b60613a6a565b600190038181905f5260205f20015f9055905550610a54565b87861115610b9a57604051638ac4bc7360e01b815260040160405180910390fd5b305f90815260208d905260409020610bb79083908d8960016119f8565b8c5483516001600160a01b0390911690639dc29fac903390610be39069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610c26575f80fd5b505af1158015610c38573d5f803e3d5ffd5b50505050610c488a30898661172c565b896001600160a01b0316876001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc868a604051610c97929190613a7e565b60405180910390a45050505050979650505050505050565b608081015160c08201515f91908290610ccb9086908490611a0f565b9050610ce286838660600151876040015151611a55565b845460208501516001600160601b03909116906060905f90610d05908990611ba6565b90506060610d1e8a838a604001518b606001518a611c5f565b600185015491995094509091506001600160601b031680860380891180610d4857508960a0015189115b15610d6657604051638ac4bc7360e01b815260040160405180910390fd5b505f5b8251811015610da857610da081848381518110610d8857610d88613931565b60200260200101518d6116019092919063ffffffff16565b600101610d69565b50610db2886117a6565b610dbc9082613a9f565b6001840180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015610e0e5789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b50505085602001516001600160a01b0316336001600160a01b03167fa2901e7a2f0ce49b98e66b2473ae57e5b2bf1d28cac05c6565d848a813cb16778860400151848a6060015187604051610e669493929190613abf565b60405180910390a3505050509392505050565b81515f5b81811015610ec55785600201848281518110610e9b57610e9b613931565b6020908102919091018101518254600181810185555f948552929093209092019190915501610e7d565b5084546001600160a01b03166340c10f1983610eeb8469d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610f2e575f80fd5b505af1158015610f40573d5f803e3d5ffd5b50505050610f508433308661172c565b836001600160a01b0316826001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef2386604051610f9d9190613af7565b60405180910390a45050505050565b6060610ffa846203f4806203f47f86010460078701546001600160601b039190911690610ff5906001600160401b03166203f4806203f47f8801046001600160601b031661217e565b612193565b949350505050565b5f306001600160a01b0383160361102c57604051633494a40d60e21b815260040160405180910390fd5b608083015160c08401515f906110459087908490611a0f565b905061105c87838760600151886040015151611a55565b61108a61106882611911565b865f01516001600160a01b031687602001516001600160a01b031614156122e7565b8554604086015160208701516001600160601b03909216916060905f906110b2908b90611ba6565b90506110c58b82858c606001518a612319565b600183015491985092506001600160601b0316808603808911806110ec57508a60a0015189115b1561110a57604051638ac4bc7360e01b815260040160405180910390fd5b50611114886117a6565b61111e9082613a9f565b6001830180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015611170578a546bffffffffffffffffffffffff19166001600160601b0382169081178c5594505b50508151611181908a908790611601565b895482516001600160a01b03909116906340c10f199089906111ad9069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b1580156111f0575f80fd5b505af1158015611202573d5f803e3d5ffd5b50505050611215885f015133308561172c565b87602001516001600160a01b0316876001600160a01b0316336001600160a01b03167faa75244fec84b237a00e565f2d9ef15139490324abdb7807bd587432976b9a2085858d60600151898f5f0151604051611275959493929190613b09565b60405180910390a45050505050949350505050565b6001600160a01b0381165f90815260038301602052604090205b92915050565b60605f841180156112ba57504284105b156112d857604051631d80cfbf60e21b815260040160405180910390fd5b5f83516001600160401b038111156112f2576112f2613464565b60405190808252806020026020018201604052801561133b57816020015b604080516060810182525f80825260208083018290529282015282525f199092019101816113105790505b5090506001600160601b036203f480426203f47f0104165f6060871561139d5760078901545f90611389906001600160601b036203f4806203f47f8d010416906001600160401b031661217e565b905061139960018b018583612675565b9150505b5f5b8751811015611593575f8882815181106113bb576113bb613931565b602002602001015190506113cf8b826127a6565b156113ed57604051636bb8676960e11b815260040160405180910390fd5b5f806113fa8d8b85612811565b80549193509150611414906001600160601b0316876139ec565b8254909650600160401b900463ffffffff166114755760078d01805460189061144c90600160c01b90046001600160401b0316613b54565b91906101000a8154816001600160401b0302191690836001600160401b031602179055506114f9565b81545f90889061149c90600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b03166114af9190613a57565b905085518111156114be575f80fd5b5f5b818110156114f6578681815181106114da576114da613931565b6020026020010180516114ec90613b75565b90526001016114c0565b50505b6040805160608101825282546001600160601b0381168252600160601b81046001600160401b03166020830152600160a01b900460ff1691810191909152885189908690811061154b5761154b613931565b60200260200101819052506115608d84611875565b50505f908152602088905260409020805474ffffffffffffffffffffffffffffffffffffffffff1916905560010161139f565b5061159d826117a6565b6001870180545f906115b99084906001600160601b03166139a8565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505f815111156115f4576115f460018a0184836128c0565b5091979650505050505050565b5f8260ff1611801561161257508015155b15611727575f8061162685600101546129fe565b915091505f8312156116da5761163b83613b8a565b818560ff168151811061165057611650613931565b602002602001018181516116649190613a57565b90525060ff8481169083161480156116975750808460ff168151811061168c5761168c613931565b60200260200101515f145b156116d557835b5f190160ff8116158015906116ce5750818160ff16815181106116c3576116c3613931565b60200260200101515f145b61169e5791505b611719565b82818560ff16815181106116f0576116f0613931565b6020026020010181815161170491906139ec565b90525060ff8083169085161115611719578391505b611724858383612a6e565b50505b505050565b80515f5b818110156117655761175d86868686858151811061175057611750613931565b6020026020010151612ac6565b600101611730565b505050505050565b5f9081526020919091526040902090565b5f816020015163ffffffff165f141580156112a4575050602001514263ffffffff9091161090565b5f6001600160601b038211156117dd576040516306dfcc6560e41b8152606060048201526024810183905260440160405180910390fd5b5090565b5f8181526003830160205260408120805490916001600160401b0390911690036112a457604051635672499160e01b815260040160405180910390fd5b80545f90600160401b900463ffffffff16158015906112a45750505442600160401b90910463ffffffff161090565b80545f90429061186e906201518090600160401b900463ffffffff166139ec565b1092915050565b5f8181526003830160205260408120556007820180546010906118a790600160801b90046001600160401b0316613b54565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505050565b60078201545f906001600160601b036203f4806203f47f85010416906001600160401b0316811115611904575f9150506112a4565b610ffa6001850182612b21565b5f69065a4da25d3016c0000082101561192b57505f919050565b69152d02c7e14af680000082101561194557506001919050565b693f870857a3e0e380000082101561195f57506002919050565b69d3c21bcecceda100000082101561197957506003919050565b6a027b46536c66c8e300000082101561199457506004919050565b6a084595161401484a0000008210156119af57506005919050565b6a18d0bf423c03d8de0000008210156119ca57506006919050565b506007919050565b919050565b5f806119e38585612b53565b90506119ef8184612d5d565b95945050505050565b611a0485848484612d9e565b611724848484612e81565b6001600160a01b0381165f90815260048401602052604081205480611a368560ff16612eb4565b1115610ffa576040516369f131af60e11b815260040160405180910390fd5b60088360ff16101580611a69575060ff8316155b15611a8757604051633494a40d60e21b815260040160405180910390fd5b8115611aec575f6203f4806203f47f4201046203f4806203f47f850104036001600160601b03169050805f1480611ac85750611ac58460ff16612f58565b81115b15611ae657604051633494a40d60e21b815260040160405180910390fd5b50611ba0565b5f611af785426118cf565b6007860154611b1690600160c01b90046001600160401b0316846139ec565b611b2091906139ec565b600286015460078701549192505f91611b4990600160801b90046001600160401b0316856139ec565b611b5391906139ec565b90505f81611b6284606461391a565b611b6c9190613ba4565b90505f611b7887612fe4565b905080821115611b9b57604051633494a40d60e21b815260040160405180910390fd5b505050505b50505050565b6001600160a01b038082165f9081526003840160205260408120600181015491929091600160601b900416611c58578354600160601b90046001600160a01b0316611c1e5783546001600160a01b038416600160601b9081026001600160601b03928316178655600183018054909216179055611c58565b83546001820180546001600160a01b03600160601b93849004811684026001600160601b0392831617909255865491861690920291161784555b9392505050565b6040805160c08101909152821581526060905f9082908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8901046001600160601b0316815260028b015460078c0154602090920191611cd49190600160801b90046001600160401b03166139ec565b81525f602082015260078b0154600160c01b90046001600160401b0316604090910152805190915015611d3157611d22611d126303b53800426139ec565b6203f4806203f47f919091010490565b6001600160601b031660408201525b5f611d458a83602001518460400151612193565b60408051600880825261012082019092529192505f919060208201610100803683370190505090505f89516001600160401b03811115611d8757611d87613464565b604051908082528060200260200182016040528015611db0578160200160208202803683370190505b5090505f5b8a518110156120c257611de18d8c8381518110611dd457611dd4613931565b60200260200101516127a6565b15611dff57604051636bb8676960e11b815260040160405180910390fd5b5f80611e258f8f8f8681518110611e1857611e18613931565b6020026020010151612811565b602089015182549294509092505f91611e5590600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b0316611e689190613a57565b90505f5b81811015611ed857878181518110611e8657611e86613931565b60200260200101515f03611ead57604051633494a40d60e21b815260040160405180910390fd5b878181518110611ebf57611ebf613931565b6020908102919091010180515f19019052600101611e6c565b50505f611ee687898e61304f565b82549091506001600160601b0316811115611f4e578154611f10906001600160601b031682613a57565b88608001818151611f2191906139ec565b905250611f2d816117a6565b82546bffffffffffffffffffffffff19166001600160601b03919091161782555b815460ff600160a01b9091048116908d16811015611fd757825460ff808f16600160a01b0260ff60a01b19909216919091178455875188918316908110611f9757611f97613931565b602002602001018051611fa990613bb7565b90528651879060ff8f16908110611fc257611fc2613931565b602002602001018051611fd490613bcb565b90525b8851156120105783546bffffffff00000000000000001916845560a08901805161200090613c02565b6001600160401b03169052612034565b835463ffffffff8f16600160401b026bffffffff0000000000000000199091161784555b604080516060808201835285546001600160601b038116808452600160601b82046001600160401b0381166020860152600160a01b90920460ff81169490950193909352901b67ffffffffffffffff60601b161760a09190911b60ff60a01b16178686815181106120a7576120a7613931565b60200260200101818152505084600101945050505050611db5565b5083511561210d5760a084015160078d0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055612164565b60208401516121219060018e0190856128c0565b60078c015460408501516001600160401b03909116101561216457604084015160078d01805467ffffffffffffffff19166001600160401b039092169190911790555b608090930151909550935090915050955095509592505050565b5f81831061218c5781611c58565b5090919050565b60078301546060906001600160401b03165f818410156121b5578391506121ba565b508083035b8185101561229057805f036121de576121d7600187018684612675565b92506122de565b8482036121eb82826139ec565b6001600160401b0381111561220257612202613464565b60405190808252806020026020018201604052801561222b578160200160208202803683370190505b5093505f61223d600189018886612675565b90505f5b828110156122885781818151811061225b5761225b613931565b602002602001015186828151811061227557612275613931565b6020908102919091010152600101612241565b5050506122de565b61229a8585613a57565b6001600160401b038111156122b1576122b1613464565b6040519080825280602002602001820160405280156122da578160200160208202803683370190505b5092505b50509392505050565b8080156122f75750600360ff8316105b1561231557604051633494a40d60e21b815260040160405180910390fd5b5050565b6040805160c08101909152821581525f906060908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8801046001600160601b0316815260028a015460078b015460209092019161238c9190600160801b90046001600160401b03166139ec565b81525f602082015260078a0154600160c01b90046001600160401b03166040909101528051909150156123d9576123ca611d126303b53800426139ec565b6001600160601b031660408201525b5f6123ed8983602001518460400151612193565b90505f87516001600160401b0381111561240957612409613464565b604051908082528060200260200182016040528015612432578160200160208202803683370190505b5090505f5b88518110156125bd575f61244c84868a61304f565b855190915015612471578460a001805161246590613c02565b6001600160401b031690525b5f6040518060600160405280612486846117a6565b6001600160601b0316815260200161249d8f6130f0565b6001600160401b031681526020018a60ff1681525090506124e28b84815181106124c9576124c9613931565b6020026020010151828e61313b9092919063ffffffff16565b61253c8d8c85815181106124f8576124f8613931565b6020026020010151604051806060016040528085602001516001600160401b031681526020018e63ffffffff168152602001336001600160a01b03168152506131f3565b8051602082015160408301516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b161784848151811061258657612586613931565b60200260200101818152505081866080018181516125a491906139ec565b9052505050606084018051600190810190915201612437565b508251156126085760a083015160078b0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561265f565b602083015161261c9060018c0190846128c0565b60078a015460408401516001600160401b03909116101561265f57604083015160078b01805467ffffffffffffffff19166001600160401b039092169190911790555b6080909201519350909150509550959350505050565b6060818311156126985760405163d571362560e01b815260040160405180910390fd5b82820360f08111156126bd5760405163d571362560e01b815260040160405180910390fd5b5f816001600160401b038111156126d6576126d6613464565b6040519080825280602002602001820160405280156126ff578160200160208202803683370190505b5090505f808061271860f0895b06600a80820492910690565b5f82815260208c90526040902054919350915062ffffff601883021b895b8981101561279657602086019550818316601885021c868801528160181b9150600184019350600181019050600a84036127915761277560f08261270c565b5f82815260208f905260409020549196509450925062ffffff91505b612736565b50949a9950505050505050505050565b5f818152600483016020526040812054426001600160601b039091161015806127e857505f8281526005840160205260409020544265ffffffffffff90911610155b80611c5857505f828152600684016020526040902054426001600160601b039091161015611c58565b5f818152600384016020526040812080549091906001600160401b0316810361284d57604051635672499160e01b815260040160405180910390fd5b6128568261181e565b1561287457604051631d80cfbf60e21b815260040160405180910390fd5b505f828152602084905260409020805482546001600160401b03908116600160601b90920416146128b857604051633c3ca9d760e11b815260040160405180910390fd5b935093915050565b805160f08111156128e45760405163d571362560e01b815260040160405180910390fd5b6128ef815f19613a57565b83111561290f5760405163d571362560e01b815260040160405180910390fd5b5f8061291c60f08661270c565b5f8281526020898152604090912054600190960181029592945090925062ffffff601884021b19905b858110156129e4578681015162ffffff811115612975576040516369600fad60e11b815260040160405180910390fd5b80601886021b808486161794505060018501945062ffffff601886021b199250600a85036129db575f86815260208b815260409091208590556129be9060f09084048b0161270c565b5f82815260208d905260409020549197509550935062ffffff1992505b50602001612945565b50505f928352602096909652506040902093909355505050565b60408051600880825261012082019092525f9160609190602082016101008036833750505060f084901c925090505f5b6008811015612a68578060180284901c62ffffff16828281518110612a5557612a55613931565b6020908102919091010152600101612a2e565b50915091565b60ff60f01b60f083901b165f5b6008811015612ab85780601802838281518110612a9a57612a9a613931565b602002602001015162ffffff16901b82179150806001019050612a7b565b506001909301929092555050565b5f604051632142170760e11b5f5284600452836024528260445260205f60645f808a5af13d15601f3d1160015f511416171691505f60605280604052508061172457604051636ff8a60f60e11b815260040160405180910390fd5b5f8080612b2f60f08561270c565b5f918252602087905260409091205460189091021c62ffffff169250505092915050565b5f6014821015612b6457505f6112a4565b5f828460640281612b7757612b77613a30565b049050603c8111612b8b575f9150506112a4565b6064811015612d4357670de0b6b3a7640000603b19820160011c8015612c485760018114612c545760028114612c605760038114612c6c5760048114612c785760058114612c845760068114612c905760078114612c9c5760088114612ca85760098114612cb457600a8114612cc057600b8114612ccc57600c8114612cd857600d8114612ce457600e8114612cf057600f8114612cfc5760108114612d095760118114612d165760128114612d235760138114612d3057612d39565b61019082029150612d39565b61025882029150612d39565b61032082029150612d39565b6104b082029150612d39565b61064082029150612d39565b61096082029150612d39565b610c8082029150612d39565b6112c082029150612d39565b61190082029150612d39565b61258082029150612d39565b61320082029150612d39565b614b0082029150612d39565b61640082029150612d39565b61960082029150612d39565b61c80082029150612d39565b62012c0082029150612d39565b6201900082029150612d39565b6202580082029150612d39565b6203200082029150612d39565b6204b000820291505b5091506112a49050565b69410d586a20a4c00000009150506112a4565b5092915050565b5f60038260ff161015612d715750816112a4565b68d8d726b7177a80000060ff6002198401161b80841015612d95575f9150506112a4565b830390506112a4565b6001600160a01b0383165f90815260048501602052604090205482811015612dd957604051638ac4bc7360e01b815260040160405180910390fd5b8115612e5c57828103612dfb612df3876001015460f01c90565b60ff16612eb4565b811080612e11575085546001600160601b031681105b80612e1f5750856002015481105b15612e3d57604051638ac4bc7360e01b815260040160405180910390fd5b6001600160a01b0385165f908152600487016020526040902055611724565b6001600160a01b0384165f908152600486016020526040902083820390555050505050565b6001600160a01b0382165f90815260048401602052604081208054839290612eaa9084906139ec565b9091555050505050565b5f60088210612ec1575f80fd5b8160018114612eff5760028114612f095760038114612f145760048114612f1f5760058114612f2a5760068114612f355760078114612f4057612f48565b6175309150612f48565b620186a09150612f48565b620493e09150612f48565b620f42409150612f48565b622dc6c09150612f48565b629896809150612f48565b6301c9c38091505b50670de0b6b3a764000002919050565b5f60088210612f65575f80fd5b8160018114612fa35760028114612fac5760038114612fb55760048114612fbe5760058114612fc75760068114612fd05760078114612fd957612fde565b60019150612fde565b60059150612fde565b60149150612fde565b603c9150612fde565b60789150612fde565b60b49150612fde565b60f091505b50919050565b5f816001811461302357600281146130235760038114612fb5576004811461302b5760058114613034576006811461303d576007811461304657612fde565b5f9150612fde565b601e9150612fde565b60289150612fde565b60329150612fde565b50605092915050565b60a08201516060830151845184515f936001600160401b03169291600101602002908490156130a75760205b828110156130a157808901519150613095828601856132df565b9095019460200161307b565b506130da565b60205b828110156130d8578089015191506130c4828601856132df565b600183018a830152909501946020016130aa565b505b6130e48587613421565b98975050505050505050565b600781018054600160401b90046001600160401b031690600861311283613c02565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550919050565b5f82815260208490526040902054600160601b90046001600160401b03161561317757604051637f471e3f60e11b815260040160405180910390fd5b5f91825260209283526040918290208151815494830151939092015160ff16600160a01b0260ff60a01b196001600160401b03909416600160601b027fffffffffffffffffffffffff00000000000000000000000000000000000000009095166001600160601b03909316929092179390931791909116179055565b5f8281526003840160205260409020546001600160401b03161561322a5760405163985816ed60e01b815260040160405180910390fd5b5f8281526003840160209081526040918290208351815492850151938501516001600160a01b0316600160601b026001600160601b0363ffffffff909516600160401b026bffffffffffffffffffffffff199094166001600160401b0392831617939093179390931691909117905560078401805490916010916132b691600160801b90910416613c02565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b5f60148210156132f9575068410d586a20a4c000006112a4565b5f82846064028161330c5761330c613a30565b049050602881116133295768410d586a20a4c000009150506112a4565b603c81101561335857600160288203901c68068155a43676e000000268478eae0e571ba00000019150506112a4565b6046811015613387576001603c8203901c680d02ab486cedc0000002688f1d5c1cae37400000019150506112a4565b60508110156133b657600160468203901c681a055690d9db8000000268dd2d5fcf3bc9c00000019150506112a4565b605a8110156133e657600160508203901c68340aad21b3b7000000026901794d673456eec00000019150506112a4565b606481101561340e5760056916deb1154f79eb800000605919830160011c1b049150506112a4565b69492f037764b9580000009150506112a4565b5f60038260ff1610156134355750816112a4565b606460ff600a6001198501028203168402049392505050565b80356001600160a01b03811681146119d2575f80fd5b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b038111828210171561349a5761349a613464565b60405290565b5f82601f8301126134af575f80fd5b813560206001600160401b03808311156134cb576134cb613464565b8260051b604051601f19603f830116810181811084821117156134f0576134f0613464565b60405293845285810183019383810192508785111561350d575f80fd5b83870191505b8482101561352c57813583529183019190830190613513565b979650505050505050565b5f805f805f805f60e0888a03121561354d575f80fd5b87359650602088013595506135646040890161344e565b94506135726060890161344e565b935060808801356001600160401b0381111561358c575f80fd5b6135988a828b016134a0565b93505060a088013591506135ae60c0890161344e565b905092959891949750929550565b5f805f805f60a086880312156135d0575f80fd5b85359450602086013593506135e76040870161344e565b925060608601356001600160401b03811115613601575f80fd5b61360d888289016134a0565b92505061361c6080870161344e565b90509295509295909350565b5f805f805f805f80610100898b031215613640575f80fd5b883597506020890135965061365760408a0161344e565b955061366560608a0161344e565b945061367360808a0161344e565b935060a08901356001600160401b0381111561368d575f80fd5b6136998b828c016134a0565b93505060c089013591506136af60e08a0161344e565b90509295985092959890939650565b5f805f805f805f60e0888a0312156136d4575f80fd5b87359650602088013595506136eb6040890161344e565b94506136f96060890161344e565b93506080880135925060a088013591506135ae60c0890161344e565b5f60e08284031215613725575f80fd5b61372d613478565b90506137388261344e565b81526137466020830161344e565b602082015260408201356001600160401b03811115613763575f80fd5b61376f848285016134a0565b604083015250606082013560608201526080820135608082015260a082013560a082015261379f60c0830161344e565b60c082015292915050565b5f805f606084860312156137bc575f80fd5b833592506020840135915060408401356001600160401b038111156137df575f80fd5b6137eb86828701613715565b9150509250925092565b5f805f8060808587031215613808575f80fd5b843593506138186020860161344e565b925060408501356001600160401b03811115613832575f80fd5b61383e878288016134a0565b92505061384d6060860161344e565b905092959194509250565b5f805f6060848603121561386a575f80fd5b505081359360208301359350604090920135919050565b602080825282518282018190525f9190848201906040850190845b818110156138b85783518352928401929184019160010161389c565b50909695505050505050565b5f805f80608085870312156138d7575f80fd5b843593506020850135925060408501356001600160401b038111156138fa575f80fd5b61383e87828801613715565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176112a4576112a4613906565b634e487b7160e01b5f52603260045260245ffd5b5f8151808452602080850194508084015f5b8381101561397357815187529582019590820190600101613957565b509495945050505050565b604081525f6139906040830185613945565b90506001600160a01b03831660208301529392505050565b6001600160601b03828116828216039080821115612d5657612d56613906565b604081525f6139da6040830185613945565b82810360208401526119ef8185613945565b808201808211156112a4576112a4613906565b606081525f613a116060830186613945565b90508360208301526001600160a01b0383166040830152949350505050565b634e487b7160e01b5f52601260045260245ffd5b5f82613a5257613a52613a30565b500690565b818103818111156112a4576112a4613906565b634e487b7160e01b5f52603160045260245ffd5b604081525f613a906040830185613945565b90508260208301529392505050565b6001600160601b03818116838216019080821115612d5657612d56613906565b608081525f613ad16080830187613945565b8281036020840152613ae38187613945565b604084019590955250506060015292915050565b602081525f611c586020830184613945565b60a081525f613b1b60a0830188613945565b8281036020840152613b2d8188613945565b6040840196909652505060608101929092526001600160a01b031660809091015292915050565b5f6001600160401b03821680613b6c57613b6c613906565b5f190192915050565b5f81613b8357613b83613906565b505f190190565b5f600160ff1b8201613b9e57613b9e613906565b505f0390565b5f82613bb257613bb2613a30565b500490565b5f600160ff1b8201613b8357613b83613906565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613bfb57613bfb613906565b5060010190565b5f6001600160401b03808316818103613c1d57613c1d613906565b600101939250505056fea164736f6c6343000814000a
Deployed Bytecode
0x73d83a4695ae2b19aff0276f1016439dac88b51aed3014608060405260043610610090575f3560e01c8063de8b49b511610063578063de8b49b514610125578063e54dc4cb14610144578063f1cd5ef314610163578063f681534614610183575f80fd5b806352fa7ba81461009457806363698228146100b55780638093a148146100e7578063aa5ba1ef14610106575b5f80fd5b81801561009f575f80fd5b506100b36100ae366004613537565b6101a2565b005b8180156100c0575f80fd5b506100d46100cf3660046135bc565b6102f7565b6040519081526020015b60405180910390f35b8180156100f2575f80fd5b506100d4610101366004613628565b6106d8565b818015610111575f80fd5b506100d46101203660046136be565b610957565b818015610130575f80fd5b506100d461013f3660046137aa565b610caf565b81801561014f575f80fd5b506100b361015e3660046137f5565b610e79565b610176610171366004613858565b610fac565b6040516100de9190613881565b81801561018e575f80fd5b506100d461019d3660046138c4565b611002565b5f6101ad878661128a565b90505f6101bc898587856112aa565b895486519192506001600160a01b031690639dc29fac9033906101e99069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561022c575f80fd5b505af115801561023e573d5f803e3d5ffd5b505050505f5b81518110156102885761028082828151811061026257610262613931565b6020026020010151604001515f198b6116019092919063ffffffff16565b600101610244565b506102958730858861172c565b856001600160a01b0316836001600160a01b0316336001600160a01b03167f550bc81d80b31d12a069c70ba86563e82da48c7a5e6fedea04448763ae4b34e8888b6040516102e492919061397e565b60405180910390a4505050505050505050565b5f80610303868661128a565b90505f8085516001600160401b0381111561032057610320613464565b604051908082528060200260200182016040528015610349578160200160208202803683370190505b5090505f86516001600160401b0381111561036657610366613464565b60405190808252806020026020018201604052801561038f578160200160208202803683370190505b5090505f5b8751811015610623575f8882815181106103b0576103b0613931565b602002602001015190505f6103ce828861176d90919063ffffffff16565b6040518060600160405290815f82015f9054906101000a90046001600160601b03166001600160601b03166001600160601b031681526020015f8201600c9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160149054906101000a900460ff1660ff1660ff168152505090505f8d6003015f8481526020019081526020015f206040518060600160405290815f82015f9054906101000a90046001600160401b03166001600160401b03166001600160401b031681526020015f820160089054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020015f8201600c9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681525050905081602001516001600160401b03165f0361052057604051633494a40d60e21b815260040160405180910390fd5b6105298161177e565b806105465750602082015181516001600160401b03908116911614155b15610615578286888151811061055e5761055e613931565b60209081029190910181019190915282519083015160408401516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b16178588815181106105b6576105b6613931565b6020908102919091010152815160408301516001600160601b0390911699909901986001909701966105eb908e905f19611601565b5f838152602089905260409020805474ffffffffffffffffffffffffffffffffffffffffff191690555b836001019350505050610394565b50841561067457610633856117a6565b6001850180545f9061064f9084906001600160601b03166139a8565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b876001600160a01b0316866001600160a01b0316336001600160a01b03167f356980d99a3c896e33a4b9789ada3ddd36a0660f61a6880943544207b0d3ec9085856040516106c39291906139c8565b60405180910390a45050505095945050505050565b5f805b8451811015610782575f6107088b8784815181106106fb576106fb613931565b60200260200101516117e1565b90506107138161181e565b6107305760405163f1a3275d60e01b815260040160405180910390fd5b6107398161184d565b61075657604051630a0619fd60e01b815260040160405180910390fd5b6107798b87848151811061076c5761076c613931565b6020026020010151611875565b506001016106db565b5060078901545f90600160c01b90046001600160401b03166107a48b426118cf565b6107ae91906139ec565b335f90815260208b90526040902060028c015460078d015492935090916108139184916107eb9190600160801b90046001600160401b03166139ec565b6001600160a01b038c165f90815260048501602052604090205461080e90611911565b6119d7565b865161081f919061391a565b92508483111561084257604051638ac4bc7360e01b815260040160405180910390fd5b305f90815260208b90526040902061085f9082908b8660016119f8565b8a5486516001600160a01b0390911690639dc29fac90339061088b9069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b1580156108ce575f80fd5b505af11580156108e0573d5f803e3d5ffd5b505050506108f08830868961172c565b866001600160a01b0316846001600160a01b0316336001600160a01b03167f32297e00acad9155e59ec3f8584928858ccaf0d988b7fee2262cfa08696debaf89878d604051610941939291906139ff565b60405180910390a4505098975050505050505050565b5f8315806109685750600288015484115b15610986576040516331ea5a2760e21b815260040160405180910390fd5b60078801545f90600160c01b90046001600160401b03166109a78a426118cf565b6109b191906139ec565b60028a015460078b01549192505f916109da9190600160801b90046001600160401b03166139ec565b90505f866001600160401b038111156109f5576109f5613464565b604051908082528060200260200182016040528015610a1e578160200160208202803683370190505b50335f90815260208c815260408083206001600160a01b038e1684526004810190925282205492935091610a5190611911565b90505b8815610b7957610a658585836119d7565b610a6f90876139ec565b60028e0154604080514260208201524491810191909152606081018790529197505f91608001604051602081830303815290604052805190602001205f1c610ab79190613a44565b905089600190039950846001900394508d6002018181548110610adc57610adc613931565b905f5260205f200154848b81518110610af757610af7613931565b602090810291909101015260028e018054610b1490600190613a57565b81548110610b2457610b24613931565b905f5260205f2001548e6002018281548110610b4257610b42613931565b5f9182526020909120015560028e01805480610b6057610b60613a6a565b600190038181905f5260205f20015f9055905550610a54565b87861115610b9a57604051638ac4bc7360e01b815260040160405180910390fd5b305f90815260208d905260409020610bb79083908d8960016119f8565b8c5483516001600160a01b0390911690639dc29fac903390610be39069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610c26575f80fd5b505af1158015610c38573d5f803e3d5ffd5b50505050610c488a30898661172c565b896001600160a01b0316876001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc868a604051610c97929190613a7e565b60405180910390a45050505050979650505050505050565b608081015160c08201515f91908290610ccb9086908490611a0f565b9050610ce286838660600151876040015151611a55565b845460208501516001600160601b03909116906060905f90610d05908990611ba6565b90506060610d1e8a838a604001518b606001518a611c5f565b600185015491995094509091506001600160601b031680860380891180610d4857508960a0015189115b15610d6657604051638ac4bc7360e01b815260040160405180910390fd5b505f5b8251811015610da857610da081848381518110610d8857610d88613931565b60200260200101518d6116019092919063ffffffff16565b600101610d69565b50610db2886117a6565b610dbc9082613a9f565b6001840180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015610e0e5789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b50505085602001516001600160a01b0316336001600160a01b03167fa2901e7a2f0ce49b98e66b2473ae57e5b2bf1d28cac05c6565d848a813cb16778860400151848a6060015187604051610e669493929190613abf565b60405180910390a3505050509392505050565b81515f5b81811015610ec55785600201848281518110610e9b57610e9b613931565b6020908102919091018101518254600181810185555f948552929093209092019190915501610e7d565b5084546001600160a01b03166340c10f1983610eeb8469d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610f2e575f80fd5b505af1158015610f40573d5f803e3d5ffd5b50505050610f508433308661172c565b836001600160a01b0316826001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef2386604051610f9d9190613af7565b60405180910390a45050505050565b6060610ffa846203f4806203f47f86010460078701546001600160601b039190911690610ff5906001600160401b03166203f4806203f47f8801046001600160601b031661217e565b612193565b949350505050565b5f306001600160a01b0383160361102c57604051633494a40d60e21b815260040160405180910390fd5b608083015160c08401515f906110459087908490611a0f565b905061105c87838760600151886040015151611a55565b61108a61106882611911565b865f01516001600160a01b031687602001516001600160a01b031614156122e7565b8554604086015160208701516001600160601b03909216916060905f906110b2908b90611ba6565b90506110c58b82858c606001518a612319565b600183015491985092506001600160601b0316808603808911806110ec57508a60a0015189115b1561110a57604051638ac4bc7360e01b815260040160405180910390fd5b50611114886117a6565b61111e9082613a9f565b6001830180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015611170578a546bffffffffffffffffffffffff19166001600160601b0382169081178c5594505b50508151611181908a908790611601565b895482516001600160a01b03909116906340c10f199089906111ad9069d3c21bcecceda100000061391a565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b1580156111f0575f80fd5b505af1158015611202573d5f803e3d5ffd5b50505050611215885f015133308561172c565b87602001516001600160a01b0316876001600160a01b0316336001600160a01b03167faa75244fec84b237a00e565f2d9ef15139490324abdb7807bd587432976b9a2085858d60600151898f5f0151604051611275959493929190613b09565b60405180910390a45050505050949350505050565b6001600160a01b0381165f90815260038301602052604090205b92915050565b60605f841180156112ba57504284105b156112d857604051631d80cfbf60e21b815260040160405180910390fd5b5f83516001600160401b038111156112f2576112f2613464565b60405190808252806020026020018201604052801561133b57816020015b604080516060810182525f80825260208083018290529282015282525f199092019101816113105790505b5090506001600160601b036203f480426203f47f0104165f6060871561139d5760078901545f90611389906001600160601b036203f4806203f47f8d010416906001600160401b031661217e565b905061139960018b018583612675565b9150505b5f5b8751811015611593575f8882815181106113bb576113bb613931565b602002602001015190506113cf8b826127a6565b156113ed57604051636bb8676960e11b815260040160405180910390fd5b5f806113fa8d8b85612811565b80549193509150611414906001600160601b0316876139ec565b8254909650600160401b900463ffffffff166114755760078d01805460189061144c90600160c01b90046001600160401b0316613b54565b91906101000a8154816001600160401b0302191690836001600160401b031602179055506114f9565b81545f90889061149c90600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b03166114af9190613a57565b905085518111156114be575f80fd5b5f5b818110156114f6578681815181106114da576114da613931565b6020026020010180516114ec90613b75565b90526001016114c0565b50505b6040805160608101825282546001600160601b0381168252600160601b81046001600160401b03166020830152600160a01b900460ff1691810191909152885189908690811061154b5761154b613931565b60200260200101819052506115608d84611875565b50505f908152602088905260409020805474ffffffffffffffffffffffffffffffffffffffffff1916905560010161139f565b5061159d826117a6565b6001870180545f906115b99084906001600160601b03166139a8565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505f815111156115f4576115f460018a0184836128c0565b5091979650505050505050565b5f8260ff1611801561161257508015155b15611727575f8061162685600101546129fe565b915091505f8312156116da5761163b83613b8a565b818560ff168151811061165057611650613931565b602002602001018181516116649190613a57565b90525060ff8481169083161480156116975750808460ff168151811061168c5761168c613931565b60200260200101515f145b156116d557835b5f190160ff8116158015906116ce5750818160ff16815181106116c3576116c3613931565b60200260200101515f145b61169e5791505b611719565b82818560ff16815181106116f0576116f0613931565b6020026020010181815161170491906139ec565b90525060ff8083169085161115611719578391505b611724858383612a6e565b50505b505050565b80515f5b818110156117655761175d86868686858151811061175057611750613931565b6020026020010151612ac6565b600101611730565b505050505050565b5f9081526020919091526040902090565b5f816020015163ffffffff165f141580156112a4575050602001514263ffffffff9091161090565b5f6001600160601b038211156117dd576040516306dfcc6560e41b8152606060048201526024810183905260440160405180910390fd5b5090565b5f8181526003830160205260408120805490916001600160401b0390911690036112a457604051635672499160e01b815260040160405180910390fd5b80545f90600160401b900463ffffffff16158015906112a45750505442600160401b90910463ffffffff161090565b80545f90429061186e906201518090600160401b900463ffffffff166139ec565b1092915050565b5f8181526003830160205260408120556007820180546010906118a790600160801b90046001600160401b0316613b54565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505050565b60078201545f906001600160601b036203f4806203f47f85010416906001600160401b0316811115611904575f9150506112a4565b610ffa6001850182612b21565b5f69065a4da25d3016c0000082101561192b57505f919050565b69152d02c7e14af680000082101561194557506001919050565b693f870857a3e0e380000082101561195f57506002919050565b69d3c21bcecceda100000082101561197957506003919050565b6a027b46536c66c8e300000082101561199457506004919050565b6a084595161401484a0000008210156119af57506005919050565b6a18d0bf423c03d8de0000008210156119ca57506006919050565b506007919050565b919050565b5f806119e38585612b53565b90506119ef8184612d5d565b95945050505050565b611a0485848484612d9e565b611724848484612e81565b6001600160a01b0381165f90815260048401602052604081205480611a368560ff16612eb4565b1115610ffa576040516369f131af60e11b815260040160405180910390fd5b60088360ff16101580611a69575060ff8316155b15611a8757604051633494a40d60e21b815260040160405180910390fd5b8115611aec575f6203f4806203f47f4201046203f4806203f47f850104036001600160601b03169050805f1480611ac85750611ac58460ff16612f58565b81115b15611ae657604051633494a40d60e21b815260040160405180910390fd5b50611ba0565b5f611af785426118cf565b6007860154611b1690600160c01b90046001600160401b0316846139ec565b611b2091906139ec565b600286015460078701549192505f91611b4990600160801b90046001600160401b0316856139ec565b611b5391906139ec565b90505f81611b6284606461391a565b611b6c9190613ba4565b90505f611b7887612fe4565b905080821115611b9b57604051633494a40d60e21b815260040160405180910390fd5b505050505b50505050565b6001600160a01b038082165f9081526003840160205260408120600181015491929091600160601b900416611c58578354600160601b90046001600160a01b0316611c1e5783546001600160a01b038416600160601b9081026001600160601b03928316178655600183018054909216179055611c58565b83546001820180546001600160a01b03600160601b93849004811684026001600160601b0392831617909255865491861690920291161784555b9392505050565b6040805160c08101909152821581526060905f9082908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8901046001600160601b0316815260028b015460078c0154602090920191611cd49190600160801b90046001600160401b03166139ec565b81525f602082015260078b0154600160c01b90046001600160401b0316604090910152805190915015611d3157611d22611d126303b53800426139ec565b6203f4806203f47f919091010490565b6001600160601b031660408201525b5f611d458a83602001518460400151612193565b60408051600880825261012082019092529192505f919060208201610100803683370190505090505f89516001600160401b03811115611d8757611d87613464565b604051908082528060200260200182016040528015611db0578160200160208202803683370190505b5090505f5b8a518110156120c257611de18d8c8381518110611dd457611dd4613931565b60200260200101516127a6565b15611dff57604051636bb8676960e11b815260040160405180910390fd5b5f80611e258f8f8f8681518110611e1857611e18613931565b6020026020010151612811565b602089015182549294509092505f91611e5590600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b0316611e689190613a57565b90505f5b81811015611ed857878181518110611e8657611e86613931565b60200260200101515f03611ead57604051633494a40d60e21b815260040160405180910390fd5b878181518110611ebf57611ebf613931565b6020908102919091010180515f19019052600101611e6c565b50505f611ee687898e61304f565b82549091506001600160601b0316811115611f4e578154611f10906001600160601b031682613a57565b88608001818151611f2191906139ec565b905250611f2d816117a6565b82546bffffffffffffffffffffffff19166001600160601b03919091161782555b815460ff600160a01b9091048116908d16811015611fd757825460ff808f16600160a01b0260ff60a01b19909216919091178455875188918316908110611f9757611f97613931565b602002602001018051611fa990613bb7565b90528651879060ff8f16908110611fc257611fc2613931565b602002602001018051611fd490613bcb565b90525b8851156120105783546bffffffff00000000000000001916845560a08901805161200090613c02565b6001600160401b03169052612034565b835463ffffffff8f16600160401b026bffffffff0000000000000000199091161784555b604080516060808201835285546001600160601b038116808452600160601b82046001600160401b0381166020860152600160a01b90920460ff81169490950193909352901b67ffffffffffffffff60601b161760a09190911b60ff60a01b16178686815181106120a7576120a7613931565b60200260200101818152505084600101945050505050611db5565b5083511561210d5760a084015160078d0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055612164565b60208401516121219060018e0190856128c0565b60078c015460408501516001600160401b03909116101561216457604084015160078d01805467ffffffffffffffff19166001600160401b039092169190911790555b608090930151909550935090915050955095509592505050565b5f81831061218c5781611c58565b5090919050565b60078301546060906001600160401b03165f818410156121b5578391506121ba565b508083035b8185101561229057805f036121de576121d7600187018684612675565b92506122de565b8482036121eb82826139ec565b6001600160401b0381111561220257612202613464565b60405190808252806020026020018201604052801561222b578160200160208202803683370190505b5093505f61223d600189018886612675565b90505f5b828110156122885781818151811061225b5761225b613931565b602002602001015186828151811061227557612275613931565b6020908102919091010152600101612241565b5050506122de565b61229a8585613a57565b6001600160401b038111156122b1576122b1613464565b6040519080825280602002602001820160405280156122da578160200160208202803683370190505b5092505b50509392505050565b8080156122f75750600360ff8316105b1561231557604051633494a40d60e21b815260040160405180910390fd5b5050565b6040805160c08101909152821581525f906060908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8801046001600160601b0316815260028a015460078b015460209092019161238c9190600160801b90046001600160401b03166139ec565b81525f602082015260078a0154600160c01b90046001600160401b03166040909101528051909150156123d9576123ca611d126303b53800426139ec565b6001600160601b031660408201525b5f6123ed8983602001518460400151612193565b90505f87516001600160401b0381111561240957612409613464565b604051908082528060200260200182016040528015612432578160200160208202803683370190505b5090505f5b88518110156125bd575f61244c84868a61304f565b855190915015612471578460a001805161246590613c02565b6001600160401b031690525b5f6040518060600160405280612486846117a6565b6001600160601b0316815260200161249d8f6130f0565b6001600160401b031681526020018a60ff1681525090506124e28b84815181106124c9576124c9613931565b6020026020010151828e61313b9092919063ffffffff16565b61253c8d8c85815181106124f8576124f8613931565b6020026020010151604051806060016040528085602001516001600160401b031681526020018e63ffffffff168152602001336001600160a01b03168152506131f3565b8051602082015160408301516001600160601b0390921660609190911b67ffffffffffffffff60601b161760a09190911b60ff60a01b161784848151811061258657612586613931565b60200260200101818152505081866080018181516125a491906139ec565b9052505050606084018051600190810190915201612437565b508251156126085760a083015160078b0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561265f565b602083015161261c9060018c0190846128c0565b60078a015460408401516001600160401b03909116101561265f57604083015160078b01805467ffffffffffffffff19166001600160401b039092169190911790555b6080909201519350909150509550959350505050565b6060818311156126985760405163d571362560e01b815260040160405180910390fd5b82820360f08111156126bd5760405163d571362560e01b815260040160405180910390fd5b5f816001600160401b038111156126d6576126d6613464565b6040519080825280602002602001820160405280156126ff578160200160208202803683370190505b5090505f808061271860f0895b06600a80820492910690565b5f82815260208c90526040902054919350915062ffffff601883021b895b8981101561279657602086019550818316601885021c868801528160181b9150600184019350600181019050600a84036127915761277560f08261270c565b5f82815260208f905260409020549196509450925062ffffff91505b612736565b50949a9950505050505050505050565b5f818152600483016020526040812054426001600160601b039091161015806127e857505f8281526005840160205260409020544265ffffffffffff90911610155b80611c5857505f828152600684016020526040902054426001600160601b039091161015611c58565b5f818152600384016020526040812080549091906001600160401b0316810361284d57604051635672499160e01b815260040160405180910390fd5b6128568261181e565b1561287457604051631d80cfbf60e21b815260040160405180910390fd5b505f828152602084905260409020805482546001600160401b03908116600160601b90920416146128b857604051633c3ca9d760e11b815260040160405180910390fd5b935093915050565b805160f08111156128e45760405163d571362560e01b815260040160405180910390fd5b6128ef815f19613a57565b83111561290f5760405163d571362560e01b815260040160405180910390fd5b5f8061291c60f08661270c565b5f8281526020898152604090912054600190960181029592945090925062ffffff601884021b19905b858110156129e4578681015162ffffff811115612975576040516369600fad60e11b815260040160405180910390fd5b80601886021b808486161794505060018501945062ffffff601886021b199250600a85036129db575f86815260208b815260409091208590556129be9060f09084048b0161270c565b5f82815260208d905260409020549197509550935062ffffff1992505b50602001612945565b50505f928352602096909652506040902093909355505050565b60408051600880825261012082019092525f9160609190602082016101008036833750505060f084901c925090505f5b6008811015612a68578060180284901c62ffffff16828281518110612a5557612a55613931565b6020908102919091010152600101612a2e565b50915091565b60ff60f01b60f083901b165f5b6008811015612ab85780601802838281518110612a9a57612a9a613931565b602002602001015162ffffff16901b82179150806001019050612a7b565b506001909301929092555050565b5f604051632142170760e11b5f5284600452836024528260445260205f60645f808a5af13d15601f3d1160015f511416171691505f60605280604052508061172457604051636ff8a60f60e11b815260040160405180910390fd5b5f8080612b2f60f08561270c565b5f918252602087905260409091205460189091021c62ffffff169250505092915050565b5f6014821015612b6457505f6112a4565b5f828460640281612b7757612b77613a30565b049050603c8111612b8b575f9150506112a4565b6064811015612d4357670de0b6b3a7640000603b19820160011c8015612c485760018114612c545760028114612c605760038114612c6c5760048114612c785760058114612c845760068114612c905760078114612c9c5760088114612ca85760098114612cb457600a8114612cc057600b8114612ccc57600c8114612cd857600d8114612ce457600e8114612cf057600f8114612cfc5760108114612d095760118114612d165760128114612d235760138114612d3057612d39565b61019082029150612d39565b61025882029150612d39565b61032082029150612d39565b6104b082029150612d39565b61064082029150612d39565b61096082029150612d39565b610c8082029150612d39565b6112c082029150612d39565b61190082029150612d39565b61258082029150612d39565b61320082029150612d39565b614b0082029150612d39565b61640082029150612d39565b61960082029150612d39565b61c80082029150612d39565b62012c0082029150612d39565b6201900082029150612d39565b6202580082029150612d39565b6203200082029150612d39565b6204b000820291505b5091506112a49050565b69410d586a20a4c00000009150506112a4565b5092915050565b5f60038260ff161015612d715750816112a4565b68d8d726b7177a80000060ff6002198401161b80841015612d95575f9150506112a4565b830390506112a4565b6001600160a01b0383165f90815260048501602052604090205482811015612dd957604051638ac4bc7360e01b815260040160405180910390fd5b8115612e5c57828103612dfb612df3876001015460f01c90565b60ff16612eb4565b811080612e11575085546001600160601b031681105b80612e1f5750856002015481105b15612e3d57604051638ac4bc7360e01b815260040160405180910390fd5b6001600160a01b0385165f908152600487016020526040902055611724565b6001600160a01b0384165f908152600486016020526040902083820390555050505050565b6001600160a01b0382165f90815260048401602052604081208054839290612eaa9084906139ec565b9091555050505050565b5f60088210612ec1575f80fd5b8160018114612eff5760028114612f095760038114612f145760048114612f1f5760058114612f2a5760068114612f355760078114612f4057612f48565b6175309150612f48565b620186a09150612f48565b620493e09150612f48565b620f42409150612f48565b622dc6c09150612f48565b629896809150612f48565b6301c9c38091505b50670de0b6b3a764000002919050565b5f60088210612f65575f80fd5b8160018114612fa35760028114612fac5760038114612fb55760048114612fbe5760058114612fc75760068114612fd05760078114612fd957612fde565b60019150612fde565b60059150612fde565b60149150612fde565b603c9150612fde565b60789150612fde565b60b49150612fde565b60f091505b50919050565b5f816001811461302357600281146130235760038114612fb5576004811461302b5760058114613034576006811461303d576007811461304657612fde565b5f9150612fde565b601e9150612fde565b60289150612fde565b60329150612fde565b50605092915050565b60a08201516060830151845184515f936001600160401b03169291600101602002908490156130a75760205b828110156130a157808901519150613095828601856132df565b9095019460200161307b565b506130da565b60205b828110156130d8578089015191506130c4828601856132df565b600183018a830152909501946020016130aa565b505b6130e48587613421565b98975050505050505050565b600781018054600160401b90046001600160401b031690600861311283613c02565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550919050565b5f82815260208490526040902054600160601b90046001600160401b03161561317757604051637f471e3f60e11b815260040160405180910390fd5b5f91825260209283526040918290208151815494830151939092015160ff16600160a01b0260ff60a01b196001600160401b03909416600160601b027fffffffffffffffffffffffff00000000000000000000000000000000000000009095166001600160601b03909316929092179390931791909116179055565b5f8281526003840160205260409020546001600160401b03161561322a5760405163985816ed60e01b815260040160405180910390fd5b5f8281526003840160209081526040918290208351815492850151938501516001600160a01b0316600160601b026001600160601b0363ffffffff909516600160401b026bffffffffffffffffffffffff199094166001600160401b0392831617939093179390931691909117905560078401805490916010916132b691600160801b90910416613c02565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b5f60148210156132f9575068410d586a20a4c000006112a4565b5f82846064028161330c5761330c613a30565b049050602881116133295768410d586a20a4c000009150506112a4565b603c81101561335857600160288203901c68068155a43676e000000268478eae0e571ba00000019150506112a4565b6046811015613387576001603c8203901c680d02ab486cedc0000002688f1d5c1cae37400000019150506112a4565b60508110156133b657600160468203901c681a055690d9db8000000268dd2d5fcf3bc9c00000019150506112a4565b605a8110156133e657600160508203901c68340aad21b3b7000000026901794d673456eec00000019150506112a4565b606481101561340e5760056916deb1154f79eb800000605919830160011c1b049150506112a4565b69492f037764b9580000009150506112a4565b5f60038260ff1610156134355750816112a4565b606460ff600a6001198501028203168402049392505050565b80356001600160a01b03811681146119d2575f80fd5b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b038111828210171561349a5761349a613464565b60405290565b5f82601f8301126134af575f80fd5b813560206001600160401b03808311156134cb576134cb613464565b8260051b604051601f19603f830116810181811084821117156134f0576134f0613464565b60405293845285810183019383810192508785111561350d575f80fd5b83870191505b8482101561352c57813583529183019190830190613513565b979650505050505050565b5f805f805f805f60e0888a03121561354d575f80fd5b87359650602088013595506135646040890161344e565b94506135726060890161344e565b935060808801356001600160401b0381111561358c575f80fd5b6135988a828b016134a0565b93505060a088013591506135ae60c0890161344e565b905092959891949750929550565b5f805f805f60a086880312156135d0575f80fd5b85359450602086013593506135e76040870161344e565b925060608601356001600160401b03811115613601575f80fd5b61360d888289016134a0565b92505061361c6080870161344e565b90509295509295909350565b5f805f805f805f80610100898b031215613640575f80fd5b883597506020890135965061365760408a0161344e565b955061366560608a0161344e565b945061367360808a0161344e565b935060a08901356001600160401b0381111561368d575f80fd5b6136998b828c016134a0565b93505060c089013591506136af60e08a0161344e565b90509295985092959890939650565b5f805f805f805f60e0888a0312156136d4575f80fd5b87359650602088013595506136eb6040890161344e565b94506136f96060890161344e565b93506080880135925060a088013591506135ae60c0890161344e565b5f60e08284031215613725575f80fd5b61372d613478565b90506137388261344e565b81526137466020830161344e565b602082015260408201356001600160401b03811115613763575f80fd5b61376f848285016134a0565b604083015250606082013560608201526080820135608082015260a082013560a082015261379f60c0830161344e565b60c082015292915050565b5f805f606084860312156137bc575f80fd5b833592506020840135915060408401356001600160401b038111156137df575f80fd5b6137eb86828701613715565b9150509250925092565b5f805f8060808587031215613808575f80fd5b843593506138186020860161344e565b925060408501356001600160401b03811115613832575f80fd5b61383e878288016134a0565b92505061384d6060860161344e565b905092959194509250565b5f805f6060848603121561386a575f80fd5b505081359360208301359350604090920135919050565b602080825282518282018190525f9190848201906040850190845b818110156138b85783518352928401929184019160010161389c565b50909695505050505050565b5f805f80608085870312156138d7575f80fd5b843593506020850135925060408501356001600160401b038111156138fa575f80fd5b61383e87828801613715565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176112a4576112a4613906565b634e487b7160e01b5f52603260045260245ffd5b5f8151808452602080850194508084015f5b8381101561397357815187529582019590820190600101613957565b509495945050505050565b604081525f6139906040830185613945565b90506001600160a01b03831660208301529392505050565b6001600160601b03828116828216039080821115612d5657612d56613906565b604081525f6139da6040830185613945565b82810360208401526119ef8185613945565b808201808211156112a4576112a4613906565b606081525f613a116060830186613945565b90508360208301526001600160a01b0383166040830152949350505050565b634e487b7160e01b5f52601260045260245ffd5b5f82613a5257613a52613a30565b500690565b818103818111156112a4576112a4613906565b634e487b7160e01b5f52603160045260245ffd5b604081525f613a906040830185613945565b90508260208301529392505050565b6001600160601b03818116838216019080821115612d5657612d56613906565b608081525f613ad16080830187613945565b8281036020840152613ae38187613945565b604084019590955250506060015292915050565b602081525f611c586020830184613945565b60a081525f613b1b60a0830188613945565b8281036020840152613b2d8188613945565b6040840196909652505060608101929092526001600160a01b031660809091015292915050565b5f6001600160401b03821680613b6c57613b6c613906565b5f190192915050565b5f81613b8357613b83613906565b505f190190565b5f600160ff1b8201613b9e57613b9e613906565b505f0390565b5f82613bb257613bb2613a30565b500490565b5f600160ff1b8201613b8357613b83613906565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613bfb57613bfb613906565b5060010190565b5f6001600160401b03808316818103613c1d57613c1d613906565b600101939250505056fea164736f6c6343000814000a
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.