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/utils/math/Math.sol"; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "../library/RollingBuckets.sol"; import "../library/ERC721Transfer.sol"; import "../library/Array.sol"; import "../Errors.sol"; import "../Constants.sol"; import "./User.sol"; import "./Helper.sol"; import "./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 ExpiredNftToVault(address indexed operator, address indexed collection, uint256[] tokenIds); event FragmentNft( address indexed operator, address indexed onBehalfOf, address indexed collection, uint256[] tokenIds ); event ClaimRandomNft( address indexed operator, address indexed receiver, address indexed collection, uint256[] tokenIds, uint256 creditCost ); function fragmentNFTs( CollectionState storage collectionState, UserFloorAccount storage account, address collection, uint256[] memory nftIds, address onBehalfOf ) public { uint256 nftLen = nftIds.length; /// tricky logic: if `onBehalfOf` is the contract, shaffle the correspounding amount of NFTs bool shuffle = onBehalfOf == address(this); uint256[] memory pickedTokenIds; if (shuffle) { uint256 vaultCnt = collectionState.freeTokenIds.length; /// no enough nft to shuffle if (nftLen > vaultCnt) revert Errors.ClaimableNftInsufficient(); (pickedTokenIds,) = pickFromVault(collectionState, nftLen, 0, true); } /// after shuffling, supply new NFTs to vault uint32 contQuota; for (uint256 i; i < nftLen;) { collectionState.freeTokenIds.push(nftIds[i]); if (!shuffle) { contQuota += Constants.getVaultContQuotaAtLR(Helper.calculateLockingRatio(collectionState, 0)); } unchecked { ++i; } } if (!shuffle) { /// if no shuffling, give back the Fragment Tokens collectionState.floorToken.mint(onBehalfOf, Constants.FLOOR_TOKEN_AMOUNT * nftLen); Helper.checkAndUpdateVaultQuota(account.getByKey(collection), int32(contQuota)); ERC721Transfer.safeBatchTransferFrom(collection, msg.sender, address(this), nftIds); } else { /// if shuffling, transfer user's NFTs first to avoid repetition. ERC721Transfer.safeBatchTransferFrom(collection, msg.sender, address(this), nftIds); /// if shuffling, give back the picked NFTs ERC721Transfer.safeBatchTransferFrom(collection, address(this), msg.sender, pickedTokenIds); /// tracking the out NFTs emit ClaimRandomNft(msg.sender, msg.sender, collection, pickedTokenIds, 0); } emit FragmentNft(msg.sender, onBehalfOf, collection, nftIds); } struct LockInfo { bool isInfinite; uint256 currentBucket; uint256 newExpiryBucket; uint256 selfLocked; uint256 totalManaged; uint256 newRequireLockCredit; uint64 infiniteCnt; } function lockNfts( CollectionState storage collection, UserFloorAccount storage account, LockParam memory param, address onBehalfOf ) public returns (uint256 totalCreditCost) { if (onBehalfOf == address(this)) revert Errors.InvalidParam(); /// proxy collection only enabled when infinity lock if (param.collection != param.proxyCollection && param.expiryTs != 0) revert Errors.InvalidParam(); uint256 totalCredit = account.ensureVipCredit(param.vipLevel, param.creditToken); Helper.ensureMaxLocking(collection, param.vipLevel, param.expiryTs, param.nftIds.length, false); { uint8 maxVipLevel = Constants.getVipLevel(totalCredit); uint256 newLocked = param.nftIds.length; Helper.ensureProxyVipLevel(maxVipLevel, param.collection != param.proxyCollection); /// check period quota and global quota for the account Helper.checkAndUpdateUserSafeboxQuota(account, maxVipLevel, newLocked.toUint16()); /// don't try to add the collection account Helper.checkCollectionSafeboxQuota(account.getByKey(param.collection), collection, newLocked); } /// cache value to avoid multi-reads uint256 minMaintCredit = account.minMaintCredit; uint256[] memory nftIds = param.nftIds; uint256[] memory newKeys; { CollectionAccount storage userCollectionAccount = account.getOrAddCollection(param.collection); (totalCreditCost, newKeys) = _lockNfts(collection, userCollectionAccount, nftIds, param.expiryTs, param.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(param.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), selfLocked: account.keyCnt, 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; ++info.selfLocked; ++idx; } } if (info.isInfinite) { collectionState.infiniteCnt = info.infiniteCnt; } else { collectionState.countingBuckets.batchSet(info.currentBucket, buckets); if (info.newExpiryBucket > collectionState.lastUpdatedBucket) { collectionState.lastUpdatedBucket = uint64(info.newExpiryBucket); } } return (info.newRequireLockCredit, keys); } function unlockNfts( CollectionState storage collection, UserFloorAccount storage userAccount, address proxyCollection, address collectionId, uint256[] memory nftIds, uint256 maxExpiryTs, address receiver ) public { CollectionAccount storage userCollectionAccount = userAccount.getByKey(collectionId); SafeBoxKey[] memory releasedKeys = _unlockNfts(collection, maxExpiryTs, nftIds, userCollectionAccount); for (uint256 i = 0; i < releasedKeys.length;) { userAccount.updateVipKeyCount(releasedKeys[i].vipLevel, -1); unchecked { ++i; } } /// @dev if the receiver is the contract self, then unlock the safeboxes and dump the NFTs to the vault if (receiver == address(this)) { uint256 nftLen = nftIds.length; uint32 contQuota; for (uint256 i; i < nftLen;) { collection.freeTokenIds.push(nftIds[i]); contQuota += Constants.getVaultContQuotaAtLR(Helper.calculateLockingRatio(collection, 0)); unchecked { ++i; } } Helper.checkAndUpdateVaultQuota(userAccount.getByKey(collectionId), int32(contQuota)); emit FragmentNft(msg.sender, msg.sender, collectionId, nftIds); } else { collection.floorToken.burn(msg.sender, Constants.FLOOR_TOKEN_AMOUNT * nftIds.length); ERC721Transfer.safeBatchTransferFrom(proxyCollection, address(this), receiver, nftIds); } emit UnlockNft(msg.sender, receiver, collectionId, nftIds, proxyCollection); } function _unlockNfts( CollectionState storage collectionState, uint256 maxExpiryTs, uint256[] memory nftIds, CollectionAccount storage userCollectionAccount ) private returns (SafeBoxKey[] memory) { if (maxExpiryTs > 0 && maxExpiryTs < block.timestamp) revert Errors.SafeBoxHasExpire(); SafeBoxKey[] memory expiredKeys = new SafeBoxKey[](nftIds.length); uint256 currentBucketTime = Helper.counterStamp(block.timestamp); uint256 creditToRelease = 0; uint256[] memory buckets; /// if maxExpiryTs == 0, it means all nftIds in this batch being locked infinitely that we don't need to update countingBuckets if (maxExpiryTs > 0) { uint256 maxExpiryBucketTime = Math.min(Helper.counterStamp(maxExpiryTs), collectionState.lastUpdatedBucket); buckets = collectionState.countingBuckets.batchGet(currentBucketTime, maxExpiryBucketTime); } for (uint256 i; i < nftIds.length;) { uint256 nftId = nftIds[i]; if (Helper.hasActiveActivities(collectionState, nftId)) revert Errors.NftHasActiveActivities(); (SafeBox storage safeBox, SafeBoxKey storage safeBoxKey) = Helper.useSafeBoxAndKey(collectionState, userCollectionAccount, nftId); creditToRelease += safeBoxKey.lockingCredit; if (safeBox.isInfiniteSafeBox()) { --collectionState.infiniteCnt; } else { uint256 limit = Helper.counterStamp(safeBox.expiryTs) - currentBucketTime; if (limit > buckets.length) revert(); for (uint256 idx; idx < limit;) { --buckets[idx]; unchecked { ++idx; } } } expiredKeys[i] = safeBoxKey; removeSafeBox(collectionState, nftId); userCollectionAccount.removeSafeboxKey(nftId); unchecked { ++i; } } userCollectionAccount.totalLockingCredit -= creditToRelease.toUint96(); if (buckets.length > 0) { collectionState.countingBuckets.batchSet(currentBucketTime, buckets); } return expiredKeys; } function extendLockingForKeys( CollectionState storage collection, UserFloorAccount storage userAccount, LockParam memory param ) public returns (uint256 totalCreditCost) { uint8 newVipLevel = uint8(param.vipLevel); uint256 totalCredit = userAccount.ensureVipCredit(newVipLevel, param.creditToken); Helper.ensureMaxLocking(collection, newVipLevel, param.expiryTs, param.nftIds.length, true); uint256 minMaintCredit = userAccount.minMaintCredit; uint256[] memory safeBoxKeys; { CollectionAccount storage collectionAccount = userAccount.getOrAddCollection(param.collection); // extend lock duration int256[] memory vipLevelDiffs; (vipLevelDiffs, totalCreditCost, safeBoxKeys) = _extendLockingForKeys(collection, collectionAccount, param.nftIds, param.expiryTs, uint8(newVipLevel)); // compute max credit for locking cost uint96 totalLockingCredit = collectionAccount.totalLockingCredit; { uint256 creditBuffer; unchecked { creditBuffer = totalCredit - totalLockingCredit; } if (totalCreditCost > creditBuffer || totalCreditCost > param.maxCreditCost) { revert Errors.InsufficientCredit(); } } // update user vip key counts for (uint256 vipLevel = 0; vipLevel < vipLevelDiffs.length;) { userAccount.updateVipKeyCount(uint8(vipLevel), vipLevelDiffs[vipLevel]); unchecked { ++vipLevel; } } totalLockingCredit += totalCreditCost.toUint96(); collectionAccount.totalLockingCredit = totalLockingCredit; if (totalLockingCredit > minMaintCredit) { userAccount.minMaintCredit = totalLockingCredit; minMaintCredit = totalLockingCredit; } } emit ExtendKey(msg.sender, param.collection, param.nftIds, safeBoxKeys, param.expiryTs, minMaintCredit); } function _extendLockingForKeys( CollectionState storage collectionState, CollectionAccount storage userCollectionAccount, uint256[] memory nftIds, uint256 newExpiryTs, // expiryTs of 0 is infinite. uint8 newVipLevel ) private returns (int256[] memory, uint256, uint256[] memory) { LockInfo memory info = LockInfo({ isInfinite: newExpiryTs == 0, currentBucket: Helper.counterStamp(block.timestamp), newExpiryBucket: Helper.counterStamp(newExpiryTs), selfLocked: userCollectionAccount.keyCnt, 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]) /// listing safebox can be extended && !Helper.hasActiveListOffer(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); } result = Constants.getRequiredStakingWithSelfRatio( result, Helper.calculateSelfLockingRatio(lockInfo.selfLocked, totalManaged) ); } } function tidyExpiredNFTs(CollectionState storage collection, uint256[] memory nftIds, address collectionId) public { uint256 nftLen = nftIds.length; for (uint256 i; i < nftLen;) { uint256 nftId = nftIds[i]; SafeBox storage safeBox = Helper.useSafeBox(collection, nftId); if (!safeBox.isSafeBoxExpired()) revert Errors.SafeBoxHasNotExpire(); if (!Helper.isAuctionPeriodOver(safeBox)) revert Errors.AuctionHasNotCompleted(); /// remove expired safebox, and dump it to vault removeSafeBox(collection, nftId); collection.freeTokenIds.push(nftId); unchecked { ++i; } } emit ExpiredNftToVault(msg.sender, collectionId, nftIds); } function claimRandomNFT( CollectionState storage collection, mapping(address => UserFloorAccount) storage userAccounts, mapping(address => FeeConfig) storage feeConfs, address collectionId, uint256 claimCnt, uint256 maxCreditCost, address receiver ) public returns (uint256 totalCreditCost) { if (claimCnt == 0 || collection.freeTokenIds.length < claimCnt) revert Errors.ClaimableNftInsufficient(); { uint256 freeAmount = collection.freeTokenIds.length; uint256 totalManaged = collection.activeSafeBoxCnt + freeAmount; /// when locking ratio greater than xx%, stop redemption if ( Helper.calculateLockingRatioRaw(freeAmount - claimCnt, totalManaged - claimCnt) > Constants.VAULT_REDEMPTION_MAX_LOKING_RATIO ) { revert Errors.ClaimableNftInsufficient(); } } /// quota represented with 32 bits /// fragment token fee with 10^18 decimals bool useQuotaTx = (maxCreditCost >> 32) == 0; uint256[] memory selectedTokenIds; (selectedTokenIds, totalCreditCost) = pickFromVault( collection, claimCnt, feeConfs[address(collection.floorToken)].vaultFee.redemptionBase, useQuotaTx ); if (totalCreditCost > maxCreditCost) { revert Errors.InsufficientCredit(); } if (useQuotaTx) { UserFloorAccount storage userAccount = userAccounts[msg.sender]; Helper.checkAndUpdateVaultQuota(userAccount.getByKey(collectionId), -int32(uint32(totalCreditCost))); } else { distributeRedemptionFunds(userAccounts, feeConfs, address(collection.floorToken), claimCnt, totalCreditCost); } 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 pickFromVault(CollectionState storage collection, uint256 pickCnt, uint16 baseFeeRate, bool payWithQuota) private returns (uint256[] memory pickedTokenIds, uint256 totalCost) { pickedTokenIds = new uint256[](pickCnt); uint256 freeCnt = collection.freeTokenIds.length; uint256 totalManaged = collection.activeSafeBoxCnt + freeCnt; while (pickCnt > 0) { uint256 lockingRatio = Helper.calculateLockingRatioRaw(freeCnt, totalManaged); if (payWithQuota) { totalCost += Constants.getVaultQuotaFeeAtLR(lockingRatio); } else { totalCost += Constants.getVaultRedemptionFee(lockingRatio, baseFeeRate); } /// just compute a deterministic random number uint256 chosenNftIdx = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, totalManaged))) % freeCnt; unchecked { --pickCnt; --totalManaged; --freeCnt; } pickedTokenIds[pickCnt] = collection.freeTokenIds[chosenNftIdx]; collection.freeTokenIds[chosenNftIdx] = collection.freeTokenIds[freeCnt]; collection.freeTokenIds.pop(); } } function distributeRedemptionFunds( mapping(address => UserFloorAccount) storage accounts, mapping(address => FeeConfig) storage feeConfs, address token, uint256 redeemCnt, uint256 totalPrice ) private { address protocolReceipt = feeConfs[token].vaultFee.receipt; address royaltyReceipt = feeConfs[token].royalty.receipt; /// a bit difference, for vault redemption with fee, we treat royaltyRate as bips of the Fragment Token amount of 1 NFT /// so the dust will be the incoming to the protocol uint256 royaltyRate = feeConfs[token].royalty.vault; uint256 royalty = redeemCnt * (Constants.FLOOR_TOKEN_AMOUNT / 10000 * royaltyRate); if (totalPrice < royalty) { royalty = totalPrice; } UserFloorAccount storage userAccount = accounts[msg.sender]; userAccount.transferToken(accounts[protocolReceipt], token, totalPrice - royalty, false); userAccount.transferToken(accounts[royaltyReceipt], token, royalty, false); } 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/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or * {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.20; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be * reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/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 admin fee charged per NFT when someone starts aution on expired safebox uint256 public constant AUCTION_ON_EXPIRED_SAFEBOX_COST = 0; /// @dev admin fee charged per NFT when owner starts aution on himself safebox uint256 public constant AUCTION_COST = 100 ether; /// @notice Raffle Config uint256 public constant RAFFLE_COST = 500 ether; uint256 public constant RAFFLE_COMPLETE_GRACE_PERIODS = 2 days; /// @notice Private offer Config uint256 public constant PRIVATE_OFFER_DURATION = 24 hours; uint256 public constant PRIVATE_OFFER_COMPLETE_GRACE_DURATION = 2 days; uint256 public constant PRIVATE_OFFER_COST = 0; uint256 public constant ADD_FREE_NFT_REWARD = 0; /// @notice Lock/Unlock config uint256 public constant USER_SAFEBOX_QUOTA_REFRESH_DURATION = 1 days; uint256 public constant USER_REDEMPTION_WAIVER_REFRESH_DURATION = 1 days; /// @notice The max percentage of the collection that one user can lock uint256 public constant USER_COLLECTION_LOCKED_BOUND_PCT = 50; /// @notice The max locking ratio of the collection that the NFTs in the vault can be redeemed uint256 public constant VAULT_REDEMPTION_MAX_LOKING_RATIO = 80; uint256 public constant VAULT_QUOTA_RESET_PERIOD = 5 days; /// @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% uint256 public constant VIP_LEVEL_COUNT = 8; struct AuctionBidOption { uint256 extendDurationSecs; uint256 minimumRaisePct; uint256 vipLevel; } function getVipLockingBuckets(uint256 vipLevel) internal pure returns (uint256 buckets) { require(vipLevel < VIP_LEVEL_COUNT); assembly { switch vipLevel case 1 { buckets := 1 } case 2 { buckets := 5 } case 3 { buckets := 20 } case 4 { buckets := 60 } case 5 { buckets := 120 } case 6 { buckets := 180 } case 7 { buckets := MAX_LOCKING_BUCKET } } } function getVipLevel(uint256 totalCredit) internal pure returns (uint8) { if (totalCredit < 30_000 ether) { return 0; } else if (totalCredit < 100_000 ether) { return 1; } else if (totalCredit < 300_000 ether) { return 2; } else if (totalCredit < 1_000_000 ether) { return 3; } else if (totalCredit < 3_000_000 ether) { return 4; } else if (totalCredit < 10_000_000 ether) { return 5; } else if (totalCredit < 30_000_000 ether) { return 6; } else { return 7; } } function getVipBalanceRequirements(uint256 vipLevel) internal pure returns (uint256 required) { require(vipLevel < VIP_LEVEL_COUNT); assembly { switch vipLevel case 1 { required := 30000 } case 2 { required := 100000 } case 3 { required := 300000 } case 4 { required := 1000000 } case 5 { required := 3000000 } case 6 { required := 10000000 } case 7 { required := 30000000 } } /// credit token should be scaled with 18 decimals(1 ether == 10**18) unchecked { return required * 1 ether; } } function getBidOption(uint256 idx) internal pure returns (AuctionBidOption memory) { require(idx < 4); AuctionBidOption[4] memory bidOptions = [ AuctionBidOption({extendDurationSecs: 5 minutes, minimumRaisePct: 1, vipLevel: 0}), AuctionBidOption({extendDurationSecs: 8 hours, minimumRaisePct: 10, vipLevel: 3}), AuctionBidOption({extendDurationSecs: 16 hours, minimumRaisePct: 20, vipLevel: 5}), AuctionBidOption({extendDurationSecs: 24 hours, minimumRaisePct: 40, vipLevel: 7}) ]; return bidOptions[idx]; } function raffleDurations(uint256 idx) internal pure returns (uint256 vipLevel, uint256 duration) { require(idx < 6); vipLevel = idx; assembly { switch idx case 1 { duration := 1 } case 2 { duration := 2 } case 3 { duration := 3 } case 4 { duration := 5 } case 5 { duration := 7 } } unchecked { duration *= 1 days; } } /// return locking ratio restrictions indicates that the vipLevel can utility infinite lock NFTs at corresponding ratio function getLockingRatioForInfinite(uint8 vipLevel) internal pure returns (uint256 ratio) { assembly { switch vipLevel case 1 { ratio := 0 } case 2 { ratio := 0 } case 3 { ratio := 20 } case 4 { ratio := 30 } case 5 { ratio := 40 } case 6 { ratio := 50 } case 7 { ratio := 80 } } } /// return locking ratio restrictions indicates that the vipLevel can utility safebox to lock NFTs at corresponding ratio function getLockingRatioForSafebox(uint8 vipLevel) internal pure returns (uint256 ratio) { assembly { switch vipLevel case 1 { ratio := 10 } case 2 { ratio := 20 } case 3 { ratio := 30 } case 4 { ratio := 40 } case 5 { ratio := 50 } case 6 { ratio := 60 } case 7 { ratio := 70 } } } function getRequiredStakingWithSelfRatio(uint256 requiredStaking, uint256 selfRatio) internal pure returns (uint256) { if (selfRatio < 10) { return requiredStaking; } return (selfRatio + 1) * requiredStaking / 10; } 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: 5% - 25% return requiredStaking * (100 - (vipLevel - 2) * 5) / 100; } } function getRequiredStakingForLockRatio(uint256 locked, uint256 totalManaged) internal pure returns (uint256) { if (totalManaged <= 0) { return 1200 ether; } unchecked { uint256 lockingRatioPct = locked * 100 / totalManaged; if (lockingRatioPct <= 40) { return 1200 ether; } else if (lockingRatioPct < 60) { return 1320 ether + ((lockingRatioPct - 40) >> 1) * 120 ether; } else if (lockingRatioPct < 70) { return 2640 ether + ((lockingRatioPct - 60) >> 1) * 240 ether; } else if (lockingRatioPct < 80) { return 4080 ether + ((lockingRatioPct - 70) >> 1) * 480 ether; } else if (lockingRatioPct < 90) { return 6960 ether + ((lockingRatioPct - 80) >> 1) * 960 ether; } else if (lockingRatioPct < 100) { /// 108000 * 2^x return (108000 ether << ((lockingRatioPct - 90) >> 1)) / 5; } else { return 345600 ether; } } } function getVaultAuctionDurationAtLR(uint256 lockingRatio) internal pure returns (uint256) { if (lockingRatio < 80) return 1 hours; else if (lockingRatio < 85) return 3 hours; else if (lockingRatio < 90) return 6 hours; else if (lockingRatio < 95) return 12 hours; else return 24 hours; } function getSafeboxPeriodQuota(uint8 vipLevel) internal pure returns (uint16 quota) { assembly { switch vipLevel case 0 { quota := 0 } case 1 { quota := 1 } case 2 { quota := 2 } case 3 { quota := 4 } case 4 { quota := 8 } case 5 { quota := 16 } case 6 { quota := 32 } case 7 { quota := 64 } } } function getSafeboxUserQuota(uint8 vipLevel) internal pure returns (uint16 quota) { assembly { switch vipLevel case 0 { quota := 0 } case 1 { quota := 4 } case 2 { quota := 8 } case 3 { quota := 16 } case 4 { quota := 32 } case 5 { quota := 64 } case 6 { quota := 128 } case 7 { quota := 256 } } } function getVaultContQuotaAtLR(uint256 lockingRatio) internal pure returns (uint32 contQuota) { if (lockingRatio <= 70) { return 1; } else if (lockingRatio <= 80) { return 2; } else if (lockingRatio <= 90) { return 4; } else { return 8; } } /// two options to redeem from vault /// pay fee with fragment token or consume quota function getVaultQuotaFeeAtLR(uint256 lockingRatio) internal pure returns (uint32) { if (lockingRatio <= 50) { return 1; } else { return uint32(2 ** ((lockingRatio - 51) / 10 + 1)); } } function getVaultRedemptionFee(uint256 lockingRatio, uint16 baseRate) internal pure returns (uint256) { if (lockingRatio <= 50) { return FLOOR_TOKEN_AMOUNT * baseRate / 10000; } else { uint256 rate = ((lockingRatio - 51) / 10 + 2) * baseRate; return FLOOR_TOKEN_AMOUNT * rate / 10000; } } /// @return protocol fee after discount function getListingProtocolFeeWithDiscount(uint256 protocolFee, uint8 vipLevel) internal pure returns (uint256) { if (vipLevel < 3) { return protocolFee; } unchecked { /// the higher vip level, more discount for protocol fee /// discount range: 5% - 25% return protocolFee * (100 - (vipLevel - 2) * 5) / 100; } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; library Errors { /// @notice Safe Box error error SafeBoxHasExpire(); error SafeBoxNotExist(); error SafeBoxHasNotExpire(); error SafeBoxAlreadyExist(); error NoMatchingSafeBoxKey(); error SafeBoxKeyAlreadyExist(); /// @notice Auction error error AuctionHasNotCompleted(); error AuctionHasExpire(); error AuctionBidIsNotHighEnough(); error AuctionBidTokenMismatch(); error AuctionSelfBid(); error AuctionInvalidBidAmount(); error AuctionNotExist(); error SafeBoxAuctionWindowHasPassed(); /// @notice Activity common error error NftHasActiveActivities(); error ActivityHasNotCompleted(); error ActivityHasExpired(); error ActivityNotExist(); /// @notice User account error error InsufficientCredit(); error InsufficientBalanceForVipLevel(); error NoPrivilege(); /// @notice Parameter error error InvalidParam(); error NftCollectionNotSupported(); error NftCollectionAlreadySupported(); error ClaimableNftInsufficient(); error TokenNotSupported(); error PeriodQuotaExhausted(); error UserQuotaExhausted(); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "@openzeppelin/contracts/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 payable; /// @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 payable; /// @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 payable; /// @notice withdraw platform fee accumulated. /// Note. withdraw from `address(this)`'s account. function withdrawPlatformFee(address token, uint256 amount) external payable; /// @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 payable; /// @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 payable; /// 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 and the vault contribution quota.(note. the NFTs of the msg.sender will be transfered) /// if onBehalfOf == address(this), it means msg.sender intends to swap the same quantity of NFTs from the vault as the `nftIds` function fragmentNFTs(address collection, uint256[] memory nftIds, address onBehalfOf) external; /// @notice Kick expired safeboxes to the vault function tidyExpiredNFTs(address collection, uint256[] memory nftIds) external; /// @notice Randomly claim `claimCnt` NFTs from Floor Vault /// sender's wallet should have enough Fragment Tokens of the `collection` which will be burned to redeem the NFTs /// @param maxCredit maximum credit can be costed in this operation, if real cost exceeds this limit, the tx will fail /// @param receiver who will receive the NFTs. /// note. - the msg.sender will pay the redemption cost. /// - The Fragment Tokens of the msg.sender will be burned. function claimRandomNFT(address collection, uint256 claimCnt, uint256 maxCredit, address receiver) external returns (uint256); /// @notice Start auctions on specified `nftIds` with an initial bid price(`bidAmount`) /// This kind of auctions will be settled with Floor Credit Token /// @param bidAmount initial bid price function initAuctionOnExpiredSafeBoxes( address collection, uint256[] memory nftIds, address bidToken, uint256 bidAmount ) external; /// @notice Start auctions on specified `nftIds` index in the vault with an initial bid price(`bidAmount`) /// This kind of auctions will be settled with Fragment Token of the collection /// @param bidAmount initial bid price function initAuctionOnVault(address collection, uint256[] memory vaultIdx, address bidToken, uint96 bidAmount) external; /// @notice Owner starts auctions on his locked Safeboxes /// @param maxExpiry the latest nft's expiry, we need this to clear locking records /// @param token which token should be used to settle auctions(bid, settle) /// @param minimumBid minimum bid price when someone place a bid on the auction function ownerInitAuctions( address collection, uint256[] memory nftIds, uint256 maxExpiry, address token, uint256 minimumBid ) external; /// @notice Place a bid on specified `nftId`'s action /// @param bidAmount bid price /// @param bidOptionIdx which option used to extend auction expiry and bid price function placeBidOnAuction(address collection, uint256 nftId, uint256 bidAmount, uint256 bidOptionIdx) external 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 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; enum OfferOpType { Cancel, Decline, ChangePrice } struct ChangeOfferPriceData { uint96[] priceList; } /// @notice Owner or Receiver cancel the private offers of `nftIds` function modifyOffers(address collectionId, uint256[] memory nftIds, OfferOpType opTy, bytes calldata data) external; /// @notice Receiver accept the private offers of `nftIds` function buyerAcceptPrivateOffers(address collectionId, uint256[] memory nftIds, uint256 maxSafeboxExpiry) 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, bool verifyLocking ) 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 Array { /// @notice Compress `data` to [Length]:{[BytesLength][val...]} /// eg. [0, 255, 256] will be convert to bytes series: 0x03 0x00 0x01 0xFF 0x02 0x00 0x01 /// 0x03 means there are 3 numbers /// 0x00 means first number is 0 /// 0x01 means next number(255) has 1 byte to store the real value /// 0xFF equals 255 /// 256 need 2 bytes(0x02) to store, and its value represented in hex is 0x0100 function encodeUints(uint256[] memory data) internal pure returns (bytes memory res) { uint256 dataLen = data.length; require(dataLen <= type(uint8).max); unchecked { uint256 totalBytes; for (uint256 i; i < dataLen; ++i) { uint256 val = data[i]; while (val > 0) { val >>= 8; ++totalBytes; } } res = new bytes(dataLen + totalBytes + 1); assembly { /// skip res's length, store data length mstore8(add(res, 0x20), dataLen) } /// start from the second element idx uint256 resIdx = 0x21; for (uint256 i; i < dataLen; ++i) { uint256 val = data[i]; uint256 byteLen; while (val > 0) { val >>= 8; ++byteLen; } assembly { /// store bytes length of the `i`th element mstore8(add(res, resIdx), byteLen) } ++resIdx; val = data[i]; for (uint256 j; j < byteLen; ++j) { assembly { mstore8(add(res, resIdx), val) } val >>= 8; ++resIdx; } } } } function decodeUints(bytes memory data) internal pure returns (uint256[] memory res) { uint256 dataLen = data.length; require(dataLen > 0); res = new uint256[](uint8(data[0])); uint256 k; unchecked { for (uint256 i = 1; i < dataLen; ++i) { uint256 byteLen = uint8(data[i]); /// if byteLen is zero, it means current element is zero, no need to update `res`, just increment `k` if (byteLen > 0) { uint256 tmp; /// combine next `byteLen` bytes to `tmp` for (uint256 j; j < byteLen; ++j) { /// skip `byteLen` ++i; tmp |= ((uint256(uint8(data[i]))) << (j * 8)); } res[k] = tmp; } ++k; } } } function slice(uint256[] memory data, uint256 start, uint256 length) internal pure returns (uint256[] memory res) { require(start + length <= data.length); res = new uint256[](length); unchecked { start *= 0x20; length *= 0x20; } for (uint256 i = 0x20; i <= length;) { assembly { mstore(add(res, i), mload(add(data, add(i, start)))) } unchecked { i += 0x20; } } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; library CurrencyTransfer { /// @notice Thrown when an ERC20 transfer fails error ERC20TransferFailed(); /// @notice Thrown when an NATIVE transfer fails error NativeTransferFailed(); address public constant NATIVE = address(0); function safeTransfer(address token, address to, uint256 amount) internal { // ref // https://docs.soliditylang.org/en/latest/internals/layout_in_memory.html // implementation from // https://github.com/transmissions11/solmate/blob/v7/src/utils/SafeTransferLib.sol // https://github.com/Uniswap/v4-core/blob/main/contracts/types/Currency.sol bool success; if (token == NATIVE) { assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } if (!success) revert NativeTransferFailed(); } else { /// @solidity memory-safe-assembly assembly { // We'll write our calldata to this slot below, but restore it later. let memPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(0, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(4, to) // Append the "to" argument. mstore(36, amount) // Append the "amount" argument. 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 68 because that's the total length of our calldata (4 + 32 * 2) // Counterintuitively, this call() must be positioned after the or() in the // surrounding and() because and() evaluates its arguments from right to left. call(gas(), token, 0, 0, 68, 0, 32) ) mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, memPointer) // Restore the memPointer. } if (!success) revert ERC20TransferFailed(); } } function safeTransferFrom(address token, address from, address to, uint256 amount) 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, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(4, from) // Append and mask the "from" argument. mstore(36, to) // Append and mask the "to" argument. // Append the "amount" argument. Masking not required as it's a full 32 byte type. mstore(68, amount) 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(), token, 0, 0, 100, 0, 32) ) mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, memPointer) // Restore the memPointer. } if (!success) revert ERC20TransferFailed(); } }
// 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, FeeRate, PrivateOffer, Fees } 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 checkCollectionSafeboxQuota( CollectionAccount storage account, CollectionState storage collection, uint256 newLocked ) internal view { uint256 selfRatio = calculateSelfLockingRatio( account.keyCnt + newLocked, collection.activeSafeBoxCnt + collection.freeTokenIds.length + newLocked ); if (selfRatio > Constants.USER_COLLECTION_LOCKED_BOUND_PCT) revert Errors.UserQuotaExhausted(); } function checkAndUpdateUserSafeboxQuota(UserFloorAccount storage account, uint8 vipLevel, uint16 newLocked) internal { uint16 used = updateUserSafeboxQuota(account); uint16 totalQuota = Constants.getSafeboxPeriodQuota(vipLevel); uint16 nextUsed = used + newLocked; if (nextUsed > totalQuota) revert Errors.PeriodQuotaExhausted(); account.safeboxQuotaUsed = nextUsed; checkSafeboxQuotaGlobal(account, vipLevel, newLocked); } function checkSafeboxQuotaGlobal(UserFloorAccount storage account, uint8 vipLevel, uint256 newLocked) internal view { uint256 totalQuota = Constants.getSafeboxUserQuota(vipLevel); if (totalQuota < newLocked) { revert Errors.UserQuotaExhausted(); } else { unchecked { totalQuota -= newLocked; } } (, uint256[] memory keyCnts) = UserLib.getMinLevelAndVipKeyCounts(account.vipInfo); for (uint256 i; i < Constants.VIP_LEVEL_COUNT;) { if (totalQuota >= keyCnts[i]) { unchecked { totalQuota -= keyCnts[i]; } } else { revert Errors.UserQuotaExhausted(); } unchecked { ++i; } } } function ensureProxyVipLevel(uint8 vipLevel, bool proxy) internal pure { if (proxy && vipLevel < Constants.PROXY_COLLECTION_VIP_THRESHOLD) { revert Errors.InvalidParam(); } } function ensureMaxLocking( CollectionState storage collection, uint8 vipLevel, uint256 requireExpiryTs, uint256 requireLockCnt, bool extend ) internal view { /// vip level 0 can not use safebox utilities. if (vipLevel >= Constants.VIP_LEVEL_COUNT || vipLevel == 0) { revert Errors.InvalidParam(); } uint256 lockingRatio = calculateLockingRatio(collection, requireLockCnt); uint256 restrictRatio; if (extend) { /// try to extend exist safebox /// only restrict infinity locking, normal safebox with expiry should be skipped restrictRatio = requireExpiryTs == 0 ? Constants.getLockingRatioForInfinite(vipLevel) : 100; } else { /// try to lock(create new safebox) /// restrict maximum locking ratio to use safebox restrictRatio = Constants.getLockingRatioForSafebox(vipLevel); if (requireExpiryTs == 0) { uint256 extraRatio = Constants.getLockingRatioForInfinite(vipLevel); if (restrictRatio > extraRatio) restrictRatio = extraRatio; } } if (lockingRatio > restrictRatio) revert Errors.InvalidParam(); /// only check when it is not infinite lock if (requireExpiryTs > 0) { uint256 deltaBucket; unchecked { deltaBucket = counterStamp(requireExpiryTs) - counterStamp(block.timestamp); } if (deltaBucket == 0 || deltaBucket > Constants.getVipLockingBuckets(vipLevel)) { revert Errors.InvalidParam(); } } } function useSafeBoxAndKey(CollectionState storage collection, CollectionAccount storage userAccount, uint256 nftId) internal view returns (SafeBox storage safeBox, SafeBoxKey storage key) { safeBox = collection.safeBoxes[nftId]; if (safeBox.keyId == 0) revert Errors.SafeBoxNotExist(); if (safeBox.isSafeBoxExpired()) revert Errors.SafeBoxHasExpire(); key = userAccount.getByKey(nftId); if (!safeBox.isKeyMatchingSafeBox(key)) revert Errors.NoMatchingSafeBoxKey(); } function useSafeBox(CollectionState storage collection, uint256 nftId) internal view returns (SafeBox storage safeBox) { safeBox = collection.safeBoxes[nftId]; if (safeBox.keyId == 0) revert Errors.SafeBoxNotExist(); } function generateNextKeyId(CollectionState storage collectionState) internal returns (uint64 nextKeyId) { nextKeyId = collectionState.nextKeyId; ++collectionState.nextKeyId; } function generateNextActivityId(CollectionState storage collection) internal returns (uint64 nextActivityId) { nextActivityId = collection.nextActivityId; ++collection.nextActivityId; } function isAuctionPeriodOver(SafeBox storage safeBox) internal view returns (bool) { return safeBox.expiryTs + Constants.FREE_AUCTION_PERIOD < block.timestamp; } function hasActiveActivities(CollectionState storage collection, uint256 nftId) internal view returns (bool) { return hasActiveAuction(collection, nftId) || hasActiveRaffle(collection, nftId) || hasActivePrivateOffer(collection, nftId) || hasActiveListOffer(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 hasActiveListOffer(CollectionState storage collection, uint256 nftId) internal view returns (bool) { PrivateOffer storage offer = collection.activePrivateOffers[nftId]; return offer.activityId > 0 && offer.buyer == address(0) && !useSafeBox(collection, nftId).isSafeBoxExpired(); } function calculateActivityFee(uint256 settleAmount, uint256 feeRateBips) internal pure returns (uint256 afterFee, uint256 fee) { fee = settleAmount * feeRateBips / 10000; unchecked { afterFee = settleAmount - fee; } } function prepareBucketUpdate(CollectionState storage collection, uint256 startBucket, uint256 endBucket) internal view returns (uint256[] memory buckets) { uint256 validEnd = collection.lastUpdatedBucket; uint256 padding; if (endBucket < validEnd) { validEnd = endBucket; } else { unchecked { padding = endBucket - validEnd; } } if (startBucket < validEnd) { if (padding == 0) { buckets = collection.countingBuckets.batchGet(startBucket, validEnd); } else { uint256 validLen; unchecked { validLen = validEnd - startBucket; } buckets = new uint256[](validLen + padding); uint256[] memory tmp = collection.countingBuckets.batchGet(startBucket, validEnd); for (uint256 i; i < validLen;) { buckets[i] = tmp[i]; unchecked { ++i; } } } } else { buckets = new uint256[](endBucket - startBucket); } } function getActiveSafeBoxes(CollectionState storage collectionState, uint256 timestamp) internal view returns (uint256) { uint256 bucketStamp = counterStamp(timestamp); if (collectionState.lastUpdatedBucket < bucketStamp) { return 0; } return collectionState.countingBuckets.get(bucketStamp); } function calculateLockingRatio(CollectionState storage collection, uint256 newLocked) internal view returns (uint256) { uint256 freeAmount = collection.freeTokenIds.length; uint256 totalManaged = newLocked + collection.activeSafeBoxCnt + freeAmount; return calculateLockingRatioRaw(freeAmount, totalManaged); } function calculateLockingRatioRaw(uint256 freeAmount, uint256 totalManaged) internal pure returns (uint256) { if (totalManaged == 0) { return 0; } else { unchecked { return (100 - freeAmount * 100 / totalManaged); } } } function calculateSelfLockingRatio(uint256 selfLocked, uint256 collectionTatalManaged) internal pure returns (uint256) { if (collectionTatalManaged == 0) { return 0; } else { unchecked { return (selfLocked * 100 / collectionTatalManaged); } } } function updateUserSafeboxQuota(UserFloorAccount storage account) internal returns (uint16) { if (block.timestamp - account.lastQuotaPeriodTs <= Constants.USER_SAFEBOX_QUOTA_REFRESH_DURATION) { return account.safeboxQuotaUsed; } else { unchecked { account.lastQuotaPeriodTs = uint32( block.timestamp / Constants.USER_SAFEBOX_QUOTA_REFRESH_DURATION * Constants.USER_SAFEBOX_QUOTA_REFRESH_DURATION ); } account.safeboxQuotaUsed = 0; return 0; } } function applyDiffToCounters( CollectionState storage collectionState, uint256 startBucket, uint256 endBucket, int256 diff ) internal { if (startBucket == endBucket) return; uint256[] memory buckets = Helper.prepareBucketUpdate(collectionState, startBucket, endBucket); unchecked { uint256 bucketLen = buckets.length; if (diff > 0) { uint256 tmp = uint256(diff); for (uint256 i; i < bucketLen; ++i) { buckets[i] += tmp; } } else { uint256 tmp = uint256(-diff); for (uint256 i; i < bucketLen; ++i) { buckets[i] -= tmp; } } } collectionState.countingBuckets.batchSet(startBucket, buckets); if (endBucket > collectionState.lastUpdatedBucket) { collectionState.lastUpdatedBucket = uint64(endBucket); } } function computeFees(uint256 price, Fees memory fees) internal pure returns (uint256 priceWithoutFee, uint256 protocolFee, uint256 royalty) { protocolFee = price * fees.protocol.rateBips / 10_000; royalty = price * fees.royalty.rateBips / 10_000; unchecked { priceWithoutFee = price - protocolFee - royalty; } } function checkAndUpdateVaultQuota(CollectionAccount storage account, int32 diff) internal { if (block.timestamp - account.lastVaultActiveTs > Constants.VAULT_QUOTA_RESET_PERIOD) { // exceeds the reset period if (diff < 0) revert Errors.UserQuotaExhausted(); else account.vaultContQuota = uint32(diff); } else { int32 res = int32(account.vaultContQuota) + diff; if (res < 0) revert Errors.UserQuotaExhausted(); else account.vaultContQuota = uint32(res); } if (diff > 0) { /// only refresh when increasing quota account.lastVaultActiveTs = uint32(block.timestamp); } } }
// 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; Fees fees; } enum AuctionType { Owned, Expired, Vault } 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; /// [Deprecated] Whether the auction is triggered by the NFT owner itself? /// Note. Don't remove it directly as we need keep mainnet contract layout bool isSelfTriggered; uint64 activityId; /// [Deprecated] fee config /// Note. Don't remove it directly as we need keep mainnet contract layout uint32 oldFeeRateBips; AuctionType typ; Fees fees; } 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; /// owner of raffle address owner; /// price per ticket uint96 ticketPrice; uint64 activityId; /// total funds collected by selling tickets uint96 collectedFund; /// total sold tickets amount uint48 ticketSold; /// whether the raffle is being settling bool isSettling; /// tickets sold records TicketRecord[] tickets; Fees fees; } struct CollectionState { /// The address of the Floor Token cooresponding to the NFTs. IFragmentToken floorToken; /// Records the active safe box in each time bucket. mapping(uint256 => uint256) countingBuckets; /// Stores all of the NFTs that has been fragmented but *without* locked up limit. uint256[] freeTokenIds; /// Huge map for all the `SafeBox`es in one collection. mapping(uint256 => SafeBox) safeBoxes; /// Stores all the ongoing auctions: nftId => `AuctionInfo`. mapping(uint256 => AuctionInfo) activeAuctions; /// Stores all the ongoing raffles: nftId => `RaffleInfo`. mapping(uint256 => RaffleInfo) activeRaffles; /// Stores all the ongoing private offers: nftId => `PrivateOffer`. mapping(uint256 => PrivateOffer) activePrivateOffers; /// The last bucket time the `countingBuckets` is updated. uint64 lastUpdatedBucket; /// Next Key Id. This should start from 1, we treat key id `SafeboxLib.SAFEBOX_KEY_NOTATION` as temporarily /// being used for activities(auction/raffle). uint64 nextKeyId; /// Active Safe Box Count. uint64 activeSafeBoxCnt; /// The number of infinite lock count. uint64 infiniteCnt; /// Next Activity Id. This should start from 1 uint64 nextActivityId; uint32 lastVaultAuctionPeriodTs; } struct UserFloorAccount { /// @notice it should be maximum of the `totalLockingCredit` across all collections uint96 minMaintCredit; /// @notice used to iterate collection accounts /// packed with `minMaintCredit` to reduce storage slot access address firstCollection; /// @notice user vip level related info /// 0 - 239 bits: store SafeBoxKey Count per vip level, per level using 24 bits /// 240 - 247 bits: store minMaintVipLevel /// 248 - 255 bits: remaining uint256 vipInfo; /// @notice Locked Credit amount which cannot be withdrawn and will be released as time goes. uint256 lockedCredit; mapping(address => CollectionAccount) accounts; mapping(address => uint256) tokenAmounts; /// Each account has safebox quota to use per period uint32 lastQuotaPeriodTs; uint16 safeboxQuotaUsed; /// [Deprecated] Each account has vault redemption waiver per period uint32 lastWaiverPeriodTs; uint96 creditWaiverUsed; } struct SafeBoxKey { /// locked credit amount of this safebox uint96 lockingCredit; /// corresponding key id of the safebox uint64 keyId; /// which vip level the safebox locked uint8 vipLevel; } struct CollectionAccount { mapping(uint256 => SafeBoxKey) keys; /// total locking credit of all `keys` in this collection uint96 totalLockingCredit; /// track next collection as linked list address next; /// tracking total locked of the collection uint32 keyCnt; /// Depositing to vault gets quota, redepmtion consumes quota uint32 vaultContQuota; /// Used to track and clear vault contribution quota when the quota is inactive for a certain duration uint32 lastVaultActiveTs; } struct Fees { FeeRate royalty; FeeRate protocol; } struct FeeConfig { RoyaltyFeeRate royalty; SafeboxFeeRate safeboxFee; VaultFeeRate vaultFee; } struct RoyaltyFeeRate { address receipt; uint16 marketlist; uint16 vault; uint16 raffle; } struct VaultFeeRate { address receipt; uint16 vaultAuction; uint16 redemptionBase; } struct SafeboxFeeRate { address receipt; uint16 auctionOwned; uint16 auctionExpired; uint16 raffle; uint16 marketlist; } struct FeeRate { address receipt; uint16 rateBips; } /// Internal Structure struct LockParam { address proxyCollection; address collection; uint256[] nftIds; uint256 expiryTs; uint8 vipLevel; uint256 maxCreditCost; address creditToken; }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "../library/CurrencyTransfer.sol"; import "../Constants.sol"; import "../Errors.sol"; import "./SafeBox.sol"; import "../library/Array.sol"; import {UserFloorAccount, CollectionAccount, SafeBoxKey, CollectionState, SafeBox} from "./Structs.sol"; library UserLib { using SafeCast for uint256; using SafeBoxLib for SafeBox; /// @notice `sender` deposit `token` into Flooring on behalf of `receiver`. `receiver`'s account will be updated. event DepositToken(address indexed sender, address indexed receiver, address indexed token, uint256 amount); /// @notice `sender` withdraw `token` from Flooring and transfer it to `receiver`. event WithdrawToken(address indexed sender, address indexed receiver, address indexed token, uint256 amount); /// @notice update the account maintain credit on behalfOf `onBehalfOf` event UpdateMaintainCredit(address indexed onBehalfOf, uint256 minMaintCredit); event RemoveExpiredKey( address indexed operator, address indexed onBehalfOf, address indexed collection, uint256[] tokenIds, uint256[] safeBoxKeys ); 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.keyCnt; account.keys[nftId] = key; } function removeSafeboxKey(CollectionAccount storage account, uint256 nftId) internal { --account.keyCnt; 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 deposit( UserFloorAccount storage account, address onBehalfOf, address token, uint256 amount, bool isLockCredit ) public { depositToken(account, token, amount); if (isLockCredit) lockCredit(account, amount); if (token == CurrencyTransfer.NATIVE) { require(amount == msg.value); } else { CurrencyTransfer.safeTransferFrom(token, msg.sender, address(this), amount); } emit DepositToken(msg.sender, onBehalfOf, token, amount); } function withdraw(UserFloorAccount storage account, address receiver, address token, uint256 amount, bool isCredit) public { withdrawToken(account, token, amount, isCredit); CurrencyTransfer.safeTransfer(token, receiver, amount); emit WithdrawToken(msg.sender, receiver, token, 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 { if (amount > 0) { 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); } function removeExpiredKeysAndRestoreCredits( UserFloorAccount storage account, CollectionState storage collectionState, address collectionId, uint256[] memory nftIds, address onBehalfOf, bool verifyLocking ) public returns (uint256 releasedCredit) { CollectionAccount storage collectionAccount = getByKey(account, collectionId); if (verifyLocking) { /// tricky logic, when verifying, we assume the keyCnt maybe incorrect and prevent it from underflow by increasing it collectionAccount.keyCnt = uint32(nftIds.length); } uint256 lockingCredit; uint256 removedCnt; uint256[] memory removedKeys = new uint256[](nftIds.length); for (uint256 i; i < nftIds.length;) { SafeBoxKey memory safeBoxKey = getByKey(collectionAccount, nftIds[i]); SafeBox memory safeBox = collectionState.safeBoxes[nftIds[i]]; if (safeBoxKey.keyId == 0) { revert Errors.InvalidParam(); } if (safeBox._isSafeBoxExpired() || !safeBox._isKeyMatchingSafeBox(safeBoxKey)) { /// reuse the array nftIds[removedCnt] = nftIds[i]; removedKeys[removedCnt] = SafeBoxLib.encodeSafeBoxKey(safeBoxKey); unchecked { ++removedCnt; releasedCredit += safeBoxKey.lockingCredit; } updateVipKeyCount(account, safeBoxKey.vipLevel, -1); removeSafeboxKey(collectionAccount, nftIds[i]); } else if (verifyLocking) { unchecked { lockingCredit += safeBoxKey.lockingCredit; } } unchecked { ++i; } } if (releasedCredit > 0) { collectionAccount.totalLockingCredit -= releasedCredit.toUint96(); } if (verifyLocking) { if (collectionAccount.totalLockingCredit != lockingCredit) revert Errors.InvalidParam(); /// when locking credit are matching, the amount of keys with credit locked can be computed directly /// the keys without credit locked can be ignored temporarily collectionAccount.keyCnt = (nftIds.length - removedCnt).toUint32(); } emit RemoveExpiredKey( msg.sender, onBehalfOf, collectionId, removedCnt == nftIds.length ? nftIds : Array.slice(nftIds, 0, removedCnt), removedCnt == nftIds.length ? removedKeys : Array.slice(removedKeys, 0, removedCnt) ); } }
{ "metadata": { "bytecodeHash": "none", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 800 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"AuctionHasNotCompleted","type":"error"},{"inputs":[],"name":"BucketLengthExceedsLimit","type":"error"},{"inputs":[],"name":"BucketValueExceedsLimit","type":"error"},{"inputs":[],"name":"ClaimableNftInsufficient","type":"error"},{"inputs":[],"name":"ERC721TransferFailed","type":"error"},{"inputs":[],"name":"InsufficientBalanceForVipLevel","type":"error"},{"inputs":[],"name":"InsufficientCredit","type":"error"},{"inputs":[],"name":"InvalidParam","type":"error"},{"inputs":[],"name":"NftHasActiveActivities","type":"error"},{"inputs":[],"name":"NoMatchingSafeBoxKey","type":"error"},{"inputs":[],"name":"PeriodQuotaExhausted","type":"error"},{"inputs":[],"name":"SafeBoxAlreadyExist","type":"error"},{"inputs":[],"name":"SafeBoxHasExpire","type":"error"},{"inputs":[],"name":"SafeBoxHasNotExpire","type":"error"},{"inputs":[],"name":"SafeBoxKeyAlreadyExist","type":"error"},{"inputs":[],"name":"SafeBoxNotExist","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[],"name":"UserQuotaExhausted","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"creditCost","type":"uint256"}],"name":"ClaimRandomNft","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"ExpiredNftToVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"safeBoxKeys","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"safeBoxExpiryTs","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minMaintCredit","type":"uint256"}],"name":"ExtendKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"FragmentNft","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"safeBoxKeys","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"safeBoxExpiryTs","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minMaintCredit","type":"uint256"},{"indexed":false,"internalType":"address","name":"proxyCollection","type":"address"}],"name":"LockNft","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"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
61400c610035600b8282823980515f1a60731461002957634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610085575f3560e01c8063de8b49b511610063578063de8b49b5146100fb578063f1cd5ef31461011a578063f51fda0d1461013a578063f681534614610159575f80fd5b80630c58df251461008957806352fa7ba8146100bb5780636ceb7d5d146100dc575b5f80fd5b818015610094575f80fd5b506100a86100a3366004613776565b610178565b6040519081526020015b60405180910390f35b8180156100c6575f80fd5b506100da6100d53660046138a7565b6103c5565b005b8180156100e7575f80fd5b506100da6100f636600461391e565b610622565b818015610106575f80fd5b506100a8610115366004613a1c565b610722565b61012d610128366004613a67565b6108ee565b6040516100b29190613a90565b818015610145575f80fd5b506100da610154366004613ad3565b610944565b818015610164575f80fd5b506100a8610173366004613b3f565b610b60565b5f8315806101895750600288015484115b156101a7576040516331ea5a2760e21b815260040160405180910390fd5b600288015460078901545f906101ce908390600160801b90046001600160401b0316613baf565b905060506101ee6101df8885613bc2565b6101e98985613bc2565b610e85565b111561020d576040516331ea5a2760e21b815260040160405180910390fd5b505087546001600160a01b03165f90815260208781526040909120600201549084901c159060609061024e908b908890600160b01b900461ffff1685610eb3565b935090508483111561027357604051638ac4bc7360e01b815260040160405180910390fd5b81156102b657335f90815260208a815260408083206001600160a01b038b168452600381019092529091206102b0906102ab86613bd5565b61109b565b506102d0565b89546102d0908a908a906001600160a01b031689876111ae565b895481516001600160a01b0390911690639dc29fac9033906102fc9069d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561033f575f80fd5b505af1158015610351573d5f803e3d5ffd5b5050505061036187308684611282565b866001600160a01b0316846001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc84876040516103b0929190613c46565b60405180910390a45050979650505050505050565b6001600160a01b0384165f9081526003870160205260408120906103eb898587856112c3565b90505f5b81518110156104335761042b82828151811061040d5761040d613c67565b6020026020010151604001515f198b6115ff9092919063ffffffff16565b6001016103ef565b50306001600160a01b0384160361052f5784515f805b828110156104ae578b60020188828151811061046757610467613c67565b60209081029190910181015182546001810184555f9384529183209091015561049a90610495908e9061172a565b611774565b6104a49083613c7b565b9150600101610449565b506001600160a01b0388165f90815260038b01602052604090206104d3905b8261109b565b876001600160a01b0316336001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef238a6040516105209190613c98565b60405180910390a450506105c0565b885485516001600160a01b0390911690639dc29fac90339061055b9069d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561059e575f80fd5b505af11580156105b0573d5f803e3d5ffd5b505050506105c087308588611282565b856001600160a01b0316836001600160a01b0316336001600160a01b03167f550bc81d80b31d12a069c70ba86563e82da48c7a5e6fedea04448763ae4b34e8888b60405161060f929190613caa565b60405180910390a4505050505050505050565b81515f5b818110156106d0575f84828151811061064157610641613c67565b602002602001015190505f61065687836117b2565b9050610661816117ef565b61067e5760405163f1a3275d60e01b815260040160405180910390fd5b61068781611821565b6106a457604051630a0619fd60e01b815260040160405180910390fd5b6106ae8783611849565b50600286018054600181810183555f9283526020909220019190915501610626565b50816001600160a01b0316336001600160a01b03167fcbd8592698d587736ff1c8073851b33b9b08ae92c8d0a71678b48eef0af4049f856040516107149190613c98565b60405180910390a350505050565b608081015160c08201515f9190829061073e90869084906118a3565b90506107578683866060015187604001515160016118e9565b845460208501516001600160601b03909116906060905f9061077a908990611a03565b905060606107938a838a604001518b606001518a611abc565b600185015491995094509091506001600160601b0316808603808911806107bd57508960a0015189115b156107db57604051638ac4bc7360e01b815260040160405180910390fd5b505f5b825181101561081d57610815818483815181106107fd576107fd613c67565b60200260200101518d6115ff9092919063ffffffff16565b6001016107de565b506108278861201f565b6108319082613cd4565b6001840180546bffffffffffffffffffffffff19166001600160601b0383169081179091559091508510156108835789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b50505085602001516001600160a01b0316336001600160a01b03167fa2901e7a2f0ce49b98e66b2473ae57e5b2bf1d28cac05c6565d848a813cb16778860400151848a60600151876040516108db9493929190613cf4565b60405180910390a3505050509392505050565b606061093c846203f4806203f47f86010460078701546001600160601b039190911690610937906001600160401b03166203f4806203f47f8801046001600160601b031661205b565b612070565b949350505050565b81516001600160a01b03821630146060811561099257600288015480841115610980576040516331ea5a2760e21b815260040160405180910390fd5b61098d89855f6001610eb3565b509150505b5f805b848110156109f857896002018782815181106109b3576109b3613c67565b60209081029190910181015182546001810184555f938452919092200155836109f0576109e36104958b5f61172a565b6109ed9083613c7b565b91505b600101610995565b5082610aa35788546001600160a01b03166340c10f1986610a238769d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610a66575f80fd5b505af1158015610a78573d5f803e3d5ffd5b50505050610a926104cd888a61107e90919063ffffffff16565b610a9e87333089611282565b610b13565b610aaf87333089611282565b610abb87303385611282565b866001600160a01b0316336001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc855f604051610b0a929190613c46565b60405180910390a45b866001600160a01b0316856001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef238960405161060f9190613c98565b5f306001600160a01b03831603610b8a57604051633494a40d60e21b815260040160405180910390fd5b825f01516001600160a01b031683602001516001600160a01b031614158015610bb65750606083015115155b15610bd457604051633494a40d60e21b815260040160405180910390fd5b5f610bf284608001518560c00151876118a39092919063ffffffff16565b9050610c0e86856080015186606001518760400151515f6118e9565b5f610c18826121c4565b90505f8560400151519050610c4982875f01516001600160a01b031688602001516001600160a01b03161415612285565b610c5c8783610c57846122b3565b6122e1565b610c7d610c7687602001518961107e90919063ffffffff16565b898361235d565b50508454604085015160208601516001600160601b03909216916060905f90610ca7908a90611a03565b9050610cbe8a82858b606001518c608001516123d7565b600183015491975092506001600160601b031680860380881180610ce557508960a0015188115b15610d0357604051638ac4bc7360e01b815260040160405180910390fd5b50610d0d8761201f565b610d179082613cd4565b6001830180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015610d695789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b505060808701518251610d7d918a916115ff565b885482516001600160a01b03909116906340c10f19908890610da99069d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610dec575f80fd5b505af1158015610dfe573d5f803e3d5ffd5b50505050610e11875f0151333085611282565b86602001516001600160a01b0316866001600160a01b0316336001600160a01b03167faa75244fec84b237a00e565f2d9ef15139490324abdb7807bd587432976b9a2085858c60600151898e5f0151604051610e71959493929190613d2c565b60405180910390a450505050949350505050565b5f815f03610e9457505f610ead565b818360640281610ea657610ea6613d77565b0460640390505b92915050565b60605f846001600160401b03811115610ece57610ece6137d4565b604051908082528060200260200182016040528015610ef7578160200160208202803683370190505b5060028701546007880154919350905f90610f23908390600160801b90046001600160401b0316613baf565b90505b8615611073575f610f378383610e85565b90508515610f5f57610f4881612755565b610f589063ffffffff1685613baf565b9350610f76565b610f698188612793565b610f739085613baf565b93505b604080514260208201524491810191909152606081018390525f908490608001604051602081830303815290604052805190602001205f1c610fb89190613d8b565b60028b0180545f199b8c019b9687019695909501949192509082908110610fe157610fe1613c67565b905f5260205f200154868a81518110610ffc57610ffc613c67565b60200260200101818152505089600201848154811061101d5761101d613c67565b905f5260205f2001548a600201828154811061103b5761103b613c67565b5f9182526020909120015560028a0180548061105957611059613d9e565b600190038181905f5260205f20015f905590555050610f26565b505094509492505050565b6001600160a01b03165f9081526003919091016020526040902090565b600282015462069780906110bc90600160401b900463ffffffff1642613bc2565b111561110d575f8160030b12156110e657604051632c1252af60e11b815260040160405180910390fd5b60028201805467ffffffff00000000191664010000000063ffffffff841602179055611179565b60028201545f9061112d908390640100000000900463ffffffff16613db2565b90505f8160030b121561115357604051632c1252af60e11b815260040160405180910390fd5b60028301805463ffffffff83166401000000000267ffffffff0000000019909116179055505b5f8160030b13156111aa576002820180546bffffffff00000000000000001916600160401b4263ffffffff16021790555b5050565b6001600160a01b038381165f9081526020869052604081206002810154905490831692811691600160b01b90910461ffff1690816111f861271069d3c21bcecceda1000000613dd9565b6112029190613bf6565b61120c9087613bf6565b9050808510156112195750835b335f90815260208a905260408082206001600160a01b0387168352912061124f9089611245858a613bc2565b849291905f612825565b6001600160a01b0384165f90815260208b905260408120611276918391908b908690612825565b50505050505050505050565b80515f5b818110156112bb576112b38686868685815181106112a6576112a6613c67565b6020026020010151612842565b600101611286565b505050505050565b60605f841180156112d357504284105b156112f157604051631d80cfbf60e21b815260040160405180910390fd5b5f83516001600160401b0381111561130b5761130b6137d4565b60405190808252806020026020018201604052801561135457816020015b604080516060810182525f80825260208083018290529282015282525f199092019101816113295790505b5090506001600160601b036203f480426203f47f0104165f606087156113b65760078901545f906113a2906001600160601b036203f4806203f47f8d010416906001600160401b031661205b565b90506113b260018b01858361289d565b9150505b5f5b8751811015611591575f8882815181106113d4576113d4613c67565b602002602001015190506113e88b826129ce565b1561140657604051636bb8676960e11b815260040160405180910390fd5b5f806114138d8b85612a45565b8054919350915061142d906001600160601b031687613baf565b8254909650600160401b900463ffffffff1661148e5760078d01805460189061146590600160c01b90046001600160401b0316613dec565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550611512565b81545f9088906114b590600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b03166114c89190613bc2565b905085518111156114d7575f80fd5b5f5b8181101561150f578681815181106114f3576114f3613c67565b60200260200101805161150590613e0d565b90526001016114d9565b50505b6040805160608101825282546001600160601b0381168252600160601b81046001600160401b03166020830152600160a01b900460ff1691810191909152885189908690811061156457611564613c67565b60200260200101819052506115798d84611849565b6115838a84612af4565b8360010193505050506113b8565b5061159b8261201f565b6001870180545f906115b79084906001600160601b0316613e22565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505f815111156115f2576115f260018a018483612b60565b5091979650505050505050565b5f8260ff1611801561161057508015155b15611725575f806116248560010154612c9e565b915091505f8312156116d85761163983613e42565b818560ff168151811061164e5761164e613c67565b602002602001018181516116629190613bc2565b90525060ff8481169083161480156116955750808460ff168151811061168a5761168a613c67565b60200260200101515f145b156116d357835b5f190160ff8116158015906116cc5750818160ff16815181106116c1576116c1613c67565b60200260200101515f145b61169c5791505b611717565b82818560ff16815181106116ee576116ee613c67565b602002602001018181516117029190613baf565b90525060ff8083169085161115611717578391505b611722858383612d0e565b50505b505050565b600282015460078301545f91908290829061175590600160801b90046001600160401b031686613baf565b61175f9190613baf565b905061176b8282610e85565b95945050505050565b5f6046821161178557506001919050565b6050821161179557506002919050565b605a82116117a557506004919050565b506008919050565b919050565b5f8181526003830160205260408120805490916001600160401b039091169003610ead57604051635672499160e01b815260040160405180910390fd5b80545f90600160401b900463ffffffff1615801590610ead5750815442600160401b90910463ffffffff161092915050565b80545f904290611842906201518090600160401b900463ffffffff16613baf565b1092915050565b5f81815260038301602052604081205560078201805460109061187b90600160801b90046001600160401b0316613dec565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505050565b6001600160a01b0381165f908152600484016020526040812054806118ca8560ff16612d66565b111561093c576040516369f131af60e11b815260040160405180910390fd5b60088460ff161015806118fd575060ff8416155b1561191b57604051633494a40d60e21b815260040160405180910390fd5b5f611926868461172a565b90505f821561194c57841561193c576064611945565b61194586612e0a565b9050611978565b61195586612e80565b9050845f03611978575f61196887612e0a565b905080821115611976578091505b505b8082111561199957604051633494a40d60e21b815260040160405180910390fd5b84156119fa575f6203f4806203f47f4201046203f4806203f47f880104036001600160601b03169050805f14806119da57506119d78760ff16612eda565b81115b156119f857604051633494a40d60e21b815260040160405180910390fd5b505b50505050505050565b6001600160a01b038082165f9081526003840160205260408120600181015491929091600160601b900416611ab5578354600160601b90046001600160a01b0316611a7b5783546001600160a01b038416600160601b9081026001600160601b03928316178655600183018054909216179055611ab5565b83546001820180546001600160a01b03600160601b93849004811684026001600160601b0392831617909255865491861690920291161784555b9392505050565b6040805160e08101909152821581526060905f9082908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8901046001600160601b0316815260028a81015463ffffffff1660208301528b015460078c0154604090920191611b409190600160801b90046001600160401b0316613baf565b81525f602082015260078b0154600160c01b90046001600160401b0316604090910152805190915015611b9d57611b8e611b7e6303b5380042613baf565b6203f4806203f47f919091010490565b6001600160601b031660408201525b5f611bb18a83602001518460400151612070565b60408051600880825261012082019092529192505f919060208201610100803683370190505090505f89516001600160401b03811115611bf357611bf36137d4565b604051908082528060200260200182016040528015611c1c578160200160208202803683370190505b5090505f5b8a51811015611f6357611c4d8d8c8381518110611c4057611c40613c67565b60200260200101516129ce565b8015611c795750611c778d8c8381518110611c6a57611c6a613c67565b6020026020010151612f52565b155b15611c9757604051636bb8676960e11b815260040160405180910390fd5b5f80611cbd8f8f8f8681518110611cb057611cb0613c67565b6020026020010151612a45565b602089015182549294509092505f91611ced90600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b0316611d009190613bc2565b90505f5b81811015611d7057878181518110611d1e57611d1e613c67565b60200260200101515f03611d4557604051633494a40d60e21b815260040160405180910390fd5b878181518110611d5757611d57613c67565b6020908102919091010180515f19019052600101611d04565b50505f611d7e87898e612fb1565b82549091506001600160601b0316811115611de6578154611da8906001600160601b031682613bc2565b8860a001818151611db99190613baf565b905250611dc58161201f565b82546bffffffffffffffffffffffff19166001600160601b03919091161782555b815460ff600160a01b9091048116908d16811015611e6f57825460ff808f16600160a01b0260ff60a01b19909216919091178455875188918316908110611e2f57611e2f613c67565b602002602001018051611e4190613e5c565b90528651879060ff8f16908110611e5a57611e5a613c67565b602002602001018051611e6c90613e70565b90525b885115611ea85783546bffffffff00000000000000001916845560c089018051611e9890613ea7565b6001600160401b03169052611ecc565b835463ffffffff8f16600160401b026bffffffff0000000000000000199091161784555b604080516060808201835285546001600160601b038116808452600160601b82046001600160401b0381166020860152600160a01b90920460ff81169490950193909352901b73ffffffffffffffff000000000000000000000000161760a09190911b60ff60a01b1617868681518110611f4857611f48613c67565b60200260200101818152505084600101945050505050611c21565b50835115611fae5760c084015160078d0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055612005565b6020840151611fc29060018e019085612b60565b60078c015460408501516001600160401b03909116101561200557604084015160078d01805467ffffffffffffffff19166001600160401b039092169190911790555b60a090930151909550935090915050955095509592505050565b5f6001600160601b03821115612057576040516306dfcc6560e41b815260606004820152602481018390526044015b60405180910390fd5b5090565b5f8183106120695781611ab5565b5090919050565b60078301546060906001600160401b03165f8184101561209257839150612097565b508083035b8185101561216d57805f036120bb576120b460018701868461289d565b92506121bb565b8482036120c88282613baf565b6001600160401b038111156120df576120df6137d4565b604051908082528060200260200182016040528015612108578160200160208202803683370190505b5093505f61211a60018901888661289d565b90505f5b828110156121655781818151811061213857612138613c67565b602002602001015186828151811061215257612152613c67565b602090810291909101015260010161211e565b5050506121bb565b6121778585613bc2565b6001600160401b0381111561218e5761218e6137d4565b6040519080825280602002602001820160405280156121b7578160200160208202803683370190505b5092505b50509392505050565b5f69065a4da25d3016c000008210156121de57505f919050565b69152d02c7e14af68000008210156121f857506001919050565b693f870857a3e0e380000082101561221257506002919050565b69d3c21bcecceda100000082101561222c57506003919050565b6a027b46536c66c8e300000082101561224757506004919050565b6a084595161401484a00000082101561226257506005919050565b6a18d0bf423c03d8de00000082101561227d57506006919050565b506007919050565b8080156122955750600360ff8316105b156111aa57604051633494a40d60e21b815260040160405180910390fd5b5f61ffff821115612057576040516306dfcc6560e41b8152601060048201526024810183905260440161204e565b5f6122eb8461306b565b90505f6122f7846130c5565b90505f6123048484613ecc565b90508161ffff168161ffff16111561232f5760405163d6516b8960e01b815260040160405180910390fd5b808660050160046101000a81548161ffff021916908361ffff1602179055506112bb86868661ffff16613140565b60028301545f906123ad9061237990849063ffffffff16613baf565b60028501546007860154859161239e91600160801b90046001600160401b0316613baf565b6123a89190613baf565b6131f4565b905060328111156123d157604051632c1252af60e11b815260040160405180910390fd5b50505050565b6040805160e08101909152821581525f906060908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8801046001600160601b0316815260028981015463ffffffff1660208301528a015460078b01546040909201916124599190600160801b90046001600160401b0316613baf565b81525f602082015260078a0154600160c01b90046001600160401b03166040909101528051909150156124a657612497611b7e6303b5380042613baf565b6001600160601b031660408201525b5f6124ba8983602001518460400151612070565b90505f87516001600160401b038111156124d6576124d66137d4565b6040519080825280602002602001820160405280156124ff578160200160208202803683370190505b5090505f5b885181101561269d575f61251984868a612fb1565b85519091501561253e578460c001805161253290613ea7565b6001600160401b031690525b5f60405180606001604052806125538461201f565b6001600160601b0316815260200161256a8f61321d565b6001600160401b031681526020018a60ff1681525090506125af8b848151811061259657612596613c67565b6020026020010151828e6132689092919063ffffffff16565b6126098d8c85815181106125c5576125c5613c67565b6020026020010151604051806060016040528085602001516001600160401b031681526020018e63ffffffff168152602001336001600160a01b0316815250613356565b8051602082015160408301516001600160601b0390921660609190911b73ffffffffffffffff000000000000000000000000161760a09190911b60ff60a01b161784848151811061265c5761265c613c67565b602002602001018181525050818660a00181815161267a9190613baf565b905250505060808401805160019081019091526060850180518201905201612504565b508251156126e85760c083015160078b0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561273f565b60208301516126fc9060018c019084612b60565b60078a015460408401516001600160401b03909116101561273f57604083015160078b01805467ffffffffffffffff19166001600160401b039092169190911790555b60a0909201519350909150509550959350505050565b5f6032821161276657506001919050565b600a612773603384613bc2565b61277d9190613dd9565b612788906001613baf565b610ead906002613fc7565b5f603283116127c8576127106127b761ffff841669d3c21bcecceda1000000613bf6565b6127c19190613dd9565b9050610ead565b5f61ffff8316600a6127db603387613bc2565b6127e59190613dd9565b6127f0906002613baf565b6127fa9190613bf6565b90506127106128138269d3c21bcecceda1000000613bf6565b61281d9190613dd9565b915050610ead565b81156117225761283785848484613442565b611722848484613525565b5f604051632142170760e11b5f5284600452836024528260445260205f60645f808a5af13d15601f3d1160015f511416171691505f60605280604052508061172257604051636ff8a60f60e11b815260040160405180910390fd5b6060818311156128c05760405163d571362560e01b815260040160405180910390fd5b82820360f08111156128e55760405163d571362560e01b815260040160405180910390fd5b5f816001600160401b038111156128fe576128fe6137d4565b604051908082528060200260200182016040528015612927578160200160208202803683370190505b5090505f808061294060f0895b06600a80820492910690565b5f82815260208c90526040902054919350915062ffffff601883021b895b898110156129be57602086019550818316601885021c868801528160181b9150600184019350600181019050600a84036129b95761299d60f082612934565b5f82815260208f905260409020549196509450925062ffffff91505b61295e565b50949a9950505050505050505050565b5f818152600483016020526040812054426001600160601b03909116101580612a1057505f8281526005840160205260409020544265ffffffffffff90911610155b80612a3557505f828152600684016020526040902054426001600160601b0390911610155b80611ab55750611ab58383612f52565b5f818152600384016020526040812080549091906001600160401b03168103612a8157604051635672499160e01b815260040160405180910390fd5b612a8a826117ef565b15612aa857604051631d80cfbf60e21b815260040160405180910390fd5b505f828152602084905260409020805482546001600160401b03908116600160601b9092041614612aec57604051633c3ca9d760e11b815260040160405180910390fd5b935093915050565b6002820180545f90612b0b9063ffffffff16613fd2565b825463ffffffff9182166101009390930a92830291909202199091161790555f9081526020919091526040902080547fffffffffffffffffffffff000000000000000000000000000000000000000000169055565b805160f0811115612b845760405163d571362560e01b815260040160405180910390fd5b612b8f815f19613bc2565b831115612baf5760405163d571362560e01b815260040160405180910390fd5b5f80612bbc60f086612934565b5f8281526020898152604090912054600190960181029592945090925062ffffff601884021b19905b85811015612c84578681015162ffffff811115612c15576040516369600fad60e11b815260040160405180910390fd5b80601886021b808486161794505060018501945062ffffff601886021b199250600a8503612c7b575f86815260208b81526040909120859055612c5e9060f09084048b01612934565b5f82815260208d905260409020549197509550935062ffffff1992505b50602001612be5565b50505f928352602096909652506040902093909355505050565b60408051600880825261012082019092525f9160609190602082016101008036833750505060f084901c925090505f5b6008811015612d08578060180284901c62ffffff16828281518110612cf557612cf5613c67565b6020908102919091010152600101612cce565b50915091565b60ff60f01b60f083901b165f5b6008811015612d585780601802838281518110612d3a57612d3a613c67565b602002602001015162ffffff16901b82179150806001019050612d1b565b506001909301929092555050565b5f60088210612d73575f80fd5b8160018114612db15760028114612dbb5760038114612dc65760048114612dd15760058114612ddc5760068114612de75760078114612df257612dfa565b6175309150612dfa565b620186a09150612dfa565b620493e09150612dfa565b620f42409150612dfa565b622dc6c09150612dfa565b629896809150612dfa565b6301c9c38091505b50670de0b6b3a764000002919050565b5f8160018114612e495760028114612e495760038114612e515760048114612e5a5760058114612e635760068114612e6c5760078114612e7557612e7a565b5f9150612e7a565b60149150612e7a565b601e9150612e7a565b60289150612e7a565b60329150612e7a565b605091505b50919050565b5f8160018114612ebf5760028114612e515760038114612e5a5760048114612e635760058114612e6c5760068114612ec85760078114612ed157612e7a565b600a9150612e7a565b603c9150612e7a565b50604692915050565b5f60088210612ee7575f80fd5b8160018114612f255760028114612f2e5760038114612e515760048114612ec85760058114612f375760068114612f405760078114612f4957612e7a565b60019150612e7a565b60059150612e7a565b60789150612e7a565b60b49150612e7a565b5060f092915050565b5f81815260068301602052604081206002810154600160a01b90046001600160401b031615801590612f8f575060028101546001600160a01b0316155b801561093c5750612fa8612fa385856117b2565b6117ef565b15949350505050565b60c08201516080830151845184515f936001600160401b03169291600101602002908490156130095760205b8281101561300357808901519150612ff782860185613558565b90950194602001612fdd565b50613048565b60205b8281101561303a5780890151915061302682860185613558565b600183018a8301529095019460200161300c565b50613045858761369f565b94505b61305f8561305a8960600151866131f4565b6136cc565b98975050505050505050565b60058101545f9062015180906130879063ffffffff1642613bc2565b116130a0575060050154640100000000900461ffff1690565b50600501805465ffffffffffff191663ffffffff620151804281900402161790555f90565b5f818015612e495760018114612f25576002811461310a5760038114613113576004811461311c5760058114613125576006811461312e576007811461313757612e7a565b60029150612e7a565b60049150612e7a565b60089150612e7a565b60109150612e7a565b60209150612e7a565b50604092915050565b5f61314a836136ff565b61ffff1690508181101561317157604051632c1252af60e11b815260040160405180910390fd5b8190035f6131828560010154612c9e565b9150505f5b60088110156112bb578181815181106131a2576131a2613c67565b602002602001015183106131d3578181815181106131c2576131c2613c67565b6020026020010151830392506131ec565b604051632c1252af60e11b815260040160405180910390fd5b600101613187565b5f815f0361320357505f610ead565b81836064028161321557613215613d77565b049050610ead565b600781018054600160401b90046001600160401b031690600861323f83613ea7565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550919050565b5f82815260208490526040902054600160601b90046001600160401b0316156132a457604051637f471e3f60e11b815260040160405180910390fd5b6002830180545f906132bb9063ffffffff16613fe7565b825463ffffffff9182166101009390930a92830291909202199091161790555f91825260209283526040918290208151815494830151939092015160ff16600160a01b0260ff60a01b196001600160401b03909416600160601b027fffffffffffffffffffffffff00000000000000000000000000000000000000009095166001600160601b03909316929092179390931791909116179055565b5f8281526003840160205260409020546001600160401b03161561338d5760405163985816ed60e01b815260040160405180910390fd5b5f8281526003840160209081526040918290208351815492850151938501516001600160a01b0316600160601b026001600160601b0363ffffffff909516600160401b026bffffffffffffffffffffffff199094166001600160401b03928316179390931793909316919091179055600784018054909160109161341991600160801b90910416613ea7565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b6001600160a01b0383165f9081526004850160205260409020548281101561347d57604051638ac4bc7360e01b815260040160405180910390fd5b81156135005782810361349f613497876001015460f01c90565b60ff16612d66565b8110806134b5575085546001600160601b031681105b806134c35750856002015481105b156134e157604051638ac4bc7360e01b815260040160405180910390fd5b6001600160a01b0385165f908152600487016020526040902055611722565b6001600160a01b0384165f908152600486016020526040902083820390555050505050565b6001600160a01b0382165f9081526004840160205260408120805483929061354e908490613baf565b9091555050505050565b5f808211613570575068410d586a20a4c00000610ead565b5f82846064028161358357613583613d77565b049050602881116135a05768410d586a20a4c00000915050610ead565b603c8110156135cf57600160288203901c68068155a43676e000000268478eae0e571ba0000001915050610ead565b60468110156135fe576001603c8203901c680d02ab486cedc0000002688f1d5c1cae3740000001915050610ead565b605081101561362d57600160468203901c681a055690d9db8000000268dd2d5fcf3bc9c0000001915050610ead565b605a81101561365d57600160508203901c68340aad21b3b7000000026901794d673456eec0000001915050610ead565b60648110156136855760056916deb1154f79eb800000605919830160011c1b04915050610ead565b69492f037764b958000000915050610ead565b5092915050565b5f60038260ff1610156136b3575081610ead565b606460ff60056001198501028203168402049392505050565b5f600a8210156136dd575081610ead565b600a836136eb846001613baf565b6136f59190613bf6565b611ab59190613dd9565b5f818015612e495760018114613113576002811461311c5760038114613125576004811461312e5760058114613744576006811461374d576007811461375657612e7a565b60409150612e7a565b60809150612e7a565b5061010092915050565b80356001600160a01b03811681146117ad575f80fd5b5f805f805f805f60e0888a03121561378c575f80fd5b8735965060208801359550604088013594506137aa60608901613760565b93506080880135925060a088013591506137c660c08901613760565b905092959891949750929550565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b038111828210171561380a5761380a6137d4565b60405290565b5f82601f83011261381f575f80fd5b813560206001600160401b038083111561383b5761383b6137d4565b8260051b604051601f19603f83011681018181108482111715613860576138606137d4565b60405293845285810183019383810192508785111561387d575f80fd5b83870191505b8482101561389c57813583529183019190830190613883565b979650505050505050565b5f805f805f805f60e0888a0312156138bd575f80fd5b87359650602088013595506138d460408901613760565b94506138e260608901613760565b935060808801356001600160401b038111156138fc575f80fd5b6139088a828b01613810565b93505060a088013591506137c660c08901613760565b5f805f60608486031215613930575f80fd5b8335925060208401356001600160401b0381111561394c575f80fd5b61395886828701613810565b92505061396760408501613760565b90509250925092565b803560ff811681146117ad575f80fd5b5f60e08284031215613990575f80fd5b6139986137e8565b90506139a382613760565b81526139b160208301613760565b602082015260408201356001600160401b038111156139ce575f80fd5b6139da84828501613810565b604083015250606082013560608201526139f660808301613970565b608082015260a082013560a0820152613a1160c08301613760565b60c082015292915050565b5f805f60608486031215613a2e575f80fd5b833592506020840135915060408401356001600160401b03811115613a51575f80fd5b613a5d86828701613980565b9150509250925092565b5f805f60608486031215613a79575f80fd5b505081359360208301359350604090920135919050565b602080825282518282018190525f9190848201906040850190845b81811015613ac757835183529284019291840191600101613aab565b50909695505050505050565b5f805f805f60a08688031215613ae7575f80fd5b8535945060208601359350613afe60408701613760565b925060608601356001600160401b03811115613b18575f80fd5b613b2488828901613810565b925050613b3360808701613760565b90509295509295909350565b5f805f8060808587031215613b52575f80fd5b843593506020850135925060408501356001600160401b03811115613b75575f80fd5b613b8187828801613980565b925050613b9060608601613760565b905092959194509250565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610ead57610ead613b9b565b81810381811115610ead57610ead613b9b565b5f8160030b637fffffff198103613bee57613bee613b9b565b5f0392915050565b8082028115828204841417610ead57610ead613b9b565b5f8151808452602080850194508084015f5b83811015613c3b57815187529582019590820190600101613c1f565b509495945050505050565b604081525f613c586040830185613c0d565b90508260208301529392505050565b634e487b7160e01b5f52603260045260245ffd5b63ffffffff81811683821601908082111561369857613698613b9b565b602081525f611ab56020830184613c0d565b604081525f613cbc6040830185613c0d565b90506001600160a01b03831660208301529392505050565b6001600160601b0381811683821601908082111561369857613698613b9b565b608081525f613d066080830187613c0d565b8281036020840152613d188187613c0d565b604084019590955250506060015292915050565b60a081525f613d3e60a0830188613c0d565b8281036020840152613d508188613c0d565b6040840196909652505060608101929092526001600160a01b031660809091015292915050565b634e487b7160e01b5f52601260045260245ffd5b5f82613d9957613d99613d77565b500690565b634e487b7160e01b5f52603160045260245ffd5b600381810b9083900b01637fffffff8113637fffffff1982121715610ead57610ead613b9b565b5f82613de757613de7613d77565b500490565b5f6001600160401b03821680613e0457613e04613b9b565b5f190192915050565b5f81613e1b57613e1b613b9b565b505f190190565b6001600160601b0382811682821603908082111561369857613698613b9b565b5f600160ff1b8201613e5657613e56613b9b565b505f0390565b5f600160ff1b8201613e1b57613e1b613b9b565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ea057613ea0613b9b565b5060010190565b5f6001600160401b03808316818103613ec257613ec2613b9b565b6001019392505050565b61ffff81811683821601908082111561369857613698613b9b565b600181815b80851115613f2157815f1904821115613f0757613f07613b9b565b80851615613f1457918102915b93841c9390800290613eec565b509250929050565b5f82613f3757506001610ead565b81613f4357505f610ead565b8160018114613f595760028114613f6357613f7f565b6001915050610ead565b60ff841115613f7457613f74613b9b565b50506001821b610ead565b5060208310610133831016604e8410600b8410161715613fa2575081810a610ead565b613fac8383613ee7565b805f1904821115613fbf57613fbf613b9b565b029392505050565b5f611ab58383613f29565b5f63ffffffff821680613e0457613e04613b9b565b5f63ffffffff808316818103613ec257613ec2613b9b56fea164736f6c6343000814000a
Deployed Bytecode
0x73a76ab0feee37f08fc15e1782c635a91ab49bf4433014608060405260043610610085575f3560e01c8063de8b49b511610063578063de8b49b5146100fb578063f1cd5ef31461011a578063f51fda0d1461013a578063f681534614610159575f80fd5b80630c58df251461008957806352fa7ba8146100bb5780636ceb7d5d146100dc575b5f80fd5b818015610094575f80fd5b506100a86100a3366004613776565b610178565b6040519081526020015b60405180910390f35b8180156100c6575f80fd5b506100da6100d53660046138a7565b6103c5565b005b8180156100e7575f80fd5b506100da6100f636600461391e565b610622565b818015610106575f80fd5b506100a8610115366004613a1c565b610722565b61012d610128366004613a67565b6108ee565b6040516100b29190613a90565b818015610145575f80fd5b506100da610154366004613ad3565b610944565b818015610164575f80fd5b506100a8610173366004613b3f565b610b60565b5f8315806101895750600288015484115b156101a7576040516331ea5a2760e21b815260040160405180910390fd5b600288015460078901545f906101ce908390600160801b90046001600160401b0316613baf565b905060506101ee6101df8885613bc2565b6101e98985613bc2565b610e85565b111561020d576040516331ea5a2760e21b815260040160405180910390fd5b505087546001600160a01b03165f90815260208781526040909120600201549084901c159060609061024e908b908890600160b01b900461ffff1685610eb3565b935090508483111561027357604051638ac4bc7360e01b815260040160405180910390fd5b81156102b657335f90815260208a815260408083206001600160a01b038b168452600381019092529091206102b0906102ab86613bd5565b61109b565b506102d0565b89546102d0908a908a906001600160a01b031689876111ae565b895481516001600160a01b0390911690639dc29fac9033906102fc9069d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561033f575f80fd5b505af1158015610351573d5f803e3d5ffd5b5050505061036187308684611282565b866001600160a01b0316846001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc84876040516103b0929190613c46565b60405180910390a45050979650505050505050565b6001600160a01b0384165f9081526003870160205260408120906103eb898587856112c3565b90505f5b81518110156104335761042b82828151811061040d5761040d613c67565b6020026020010151604001515f198b6115ff9092919063ffffffff16565b6001016103ef565b50306001600160a01b0384160361052f5784515f805b828110156104ae578b60020188828151811061046757610467613c67565b60209081029190910181015182546001810184555f9384529183209091015561049a90610495908e9061172a565b611774565b6104a49083613c7b565b9150600101610449565b506001600160a01b0388165f90815260038b01602052604090206104d3905b8261109b565b876001600160a01b0316336001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef238a6040516105209190613c98565b60405180910390a450506105c0565b885485516001600160a01b0390911690639dc29fac90339061055b9069d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b15801561059e575f80fd5b505af11580156105b0573d5f803e3d5ffd5b505050506105c087308588611282565b856001600160a01b0316836001600160a01b0316336001600160a01b03167f550bc81d80b31d12a069c70ba86563e82da48c7a5e6fedea04448763ae4b34e8888b60405161060f929190613caa565b60405180910390a4505050505050505050565b81515f5b818110156106d0575f84828151811061064157610641613c67565b602002602001015190505f61065687836117b2565b9050610661816117ef565b61067e5760405163f1a3275d60e01b815260040160405180910390fd5b61068781611821565b6106a457604051630a0619fd60e01b815260040160405180910390fd5b6106ae8783611849565b50600286018054600181810183555f9283526020909220019190915501610626565b50816001600160a01b0316336001600160a01b03167fcbd8592698d587736ff1c8073851b33b9b08ae92c8d0a71678b48eef0af4049f856040516107149190613c98565b60405180910390a350505050565b608081015160c08201515f9190829061073e90869084906118a3565b90506107578683866060015187604001515160016118e9565b845460208501516001600160601b03909116906060905f9061077a908990611a03565b905060606107938a838a604001518b606001518a611abc565b600185015491995094509091506001600160601b0316808603808911806107bd57508960a0015189115b156107db57604051638ac4bc7360e01b815260040160405180910390fd5b505f5b825181101561081d57610815818483815181106107fd576107fd613c67565b60200260200101518d6115ff9092919063ffffffff16565b6001016107de565b506108278861201f565b6108319082613cd4565b6001840180546bffffffffffffffffffffffff19166001600160601b0383169081179091559091508510156108835789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b50505085602001516001600160a01b0316336001600160a01b03167fa2901e7a2f0ce49b98e66b2473ae57e5b2bf1d28cac05c6565d848a813cb16778860400151848a60600151876040516108db9493929190613cf4565b60405180910390a3505050509392505050565b606061093c846203f4806203f47f86010460078701546001600160601b039190911690610937906001600160401b03166203f4806203f47f8801046001600160601b031661205b565b612070565b949350505050565b81516001600160a01b03821630146060811561099257600288015480841115610980576040516331ea5a2760e21b815260040160405180910390fd5b61098d89855f6001610eb3565b509150505b5f805b848110156109f857896002018782815181106109b3576109b3613c67565b60209081029190910181015182546001810184555f938452919092200155836109f0576109e36104958b5f61172a565b6109ed9083613c7b565b91505b600101610995565b5082610aa35788546001600160a01b03166340c10f1986610a238769d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610a66575f80fd5b505af1158015610a78573d5f803e3d5ffd5b50505050610a926104cd888a61107e90919063ffffffff16565b610a9e87333089611282565b610b13565b610aaf87333089611282565b610abb87303385611282565b866001600160a01b0316336001600160a01b0316336001600160a01b03167f7789ae58d852bc8d723571b920b5f13625b6c1dd9173dbda4c4e25510a34e2cc855f604051610b0a929190613c46565b60405180910390a45b866001600160a01b0316856001600160a01b0316336001600160a01b03167fdbcd17ab6e507c914af6b370e49c4e3cf5f397e860c56ffbbd4af33c4da4ef238960405161060f9190613c98565b5f306001600160a01b03831603610b8a57604051633494a40d60e21b815260040160405180910390fd5b825f01516001600160a01b031683602001516001600160a01b031614158015610bb65750606083015115155b15610bd457604051633494a40d60e21b815260040160405180910390fd5b5f610bf284608001518560c00151876118a39092919063ffffffff16565b9050610c0e86856080015186606001518760400151515f6118e9565b5f610c18826121c4565b90505f8560400151519050610c4982875f01516001600160a01b031688602001516001600160a01b03161415612285565b610c5c8783610c57846122b3565b6122e1565b610c7d610c7687602001518961107e90919063ffffffff16565b898361235d565b50508454604085015160208601516001600160601b03909216916060905f90610ca7908a90611a03565b9050610cbe8a82858b606001518c608001516123d7565b600183015491975092506001600160601b031680860380881180610ce557508960a0015188115b15610d0357604051638ac4bc7360e01b815260040160405180910390fd5b50610d0d8761201f565b610d179082613cd4565b6001830180546bffffffffffffffffffffffff19166001600160601b038316908117909155909150851015610d695789546bffffffffffffffffffffffff19166001600160601b0382169081178b5594505b505060808701518251610d7d918a916115ff565b885482516001600160a01b03909116906340c10f19908890610da99069d3c21bcecceda1000000613bf6565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015610dec575f80fd5b505af1158015610dfe573d5f803e3d5ffd5b50505050610e11875f0151333085611282565b86602001516001600160a01b0316866001600160a01b0316336001600160a01b03167faa75244fec84b237a00e565f2d9ef15139490324abdb7807bd587432976b9a2085858c60600151898e5f0151604051610e71959493929190613d2c565b60405180910390a450505050949350505050565b5f815f03610e9457505f610ead565b818360640281610ea657610ea6613d77565b0460640390505b92915050565b60605f846001600160401b03811115610ece57610ece6137d4565b604051908082528060200260200182016040528015610ef7578160200160208202803683370190505b5060028701546007880154919350905f90610f23908390600160801b90046001600160401b0316613baf565b90505b8615611073575f610f378383610e85565b90508515610f5f57610f4881612755565b610f589063ffffffff1685613baf565b9350610f76565b610f698188612793565b610f739085613baf565b93505b604080514260208201524491810191909152606081018390525f908490608001604051602081830303815290604052805190602001205f1c610fb89190613d8b565b60028b0180545f199b8c019b9687019695909501949192509082908110610fe157610fe1613c67565b905f5260205f200154868a81518110610ffc57610ffc613c67565b60200260200101818152505089600201848154811061101d5761101d613c67565b905f5260205f2001548a600201828154811061103b5761103b613c67565b5f9182526020909120015560028a0180548061105957611059613d9e565b600190038181905f5260205f20015f905590555050610f26565b505094509492505050565b6001600160a01b03165f9081526003919091016020526040902090565b600282015462069780906110bc90600160401b900463ffffffff1642613bc2565b111561110d575f8160030b12156110e657604051632c1252af60e11b815260040160405180910390fd5b60028201805467ffffffff00000000191664010000000063ffffffff841602179055611179565b60028201545f9061112d908390640100000000900463ffffffff16613db2565b90505f8160030b121561115357604051632c1252af60e11b815260040160405180910390fd5b60028301805463ffffffff83166401000000000267ffffffff0000000019909116179055505b5f8160030b13156111aa576002820180546bffffffff00000000000000001916600160401b4263ffffffff16021790555b5050565b6001600160a01b038381165f9081526020869052604081206002810154905490831692811691600160b01b90910461ffff1690816111f861271069d3c21bcecceda1000000613dd9565b6112029190613bf6565b61120c9087613bf6565b9050808510156112195750835b335f90815260208a905260408082206001600160a01b0387168352912061124f9089611245858a613bc2565b849291905f612825565b6001600160a01b0384165f90815260208b905260408120611276918391908b908690612825565b50505050505050505050565b80515f5b818110156112bb576112b38686868685815181106112a6576112a6613c67565b6020026020010151612842565b600101611286565b505050505050565b60605f841180156112d357504284105b156112f157604051631d80cfbf60e21b815260040160405180910390fd5b5f83516001600160401b0381111561130b5761130b6137d4565b60405190808252806020026020018201604052801561135457816020015b604080516060810182525f80825260208083018290529282015282525f199092019101816113295790505b5090506001600160601b036203f480426203f47f0104165f606087156113b65760078901545f906113a2906001600160601b036203f4806203f47f8d010416906001600160401b031661205b565b90506113b260018b01858361289d565b9150505b5f5b8751811015611591575f8882815181106113d4576113d4613c67565b602002602001015190506113e88b826129ce565b1561140657604051636bb8676960e11b815260040160405180910390fd5b5f806114138d8b85612a45565b8054919350915061142d906001600160601b031687613baf565b8254909650600160401b900463ffffffff1661148e5760078d01805460189061146590600160c01b90046001600160401b0316613dec565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550611512565b81545f9088906114b590600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b03166114c89190613bc2565b905085518111156114d7575f80fd5b5f5b8181101561150f578681815181106114f3576114f3613c67565b60200260200101805161150590613e0d565b90526001016114d9565b50505b6040805160608101825282546001600160601b0381168252600160601b81046001600160401b03166020830152600160a01b900460ff1691810191909152885189908690811061156457611564613c67565b60200260200101819052506115798d84611849565b6115838a84612af4565b8360010193505050506113b8565b5061159b8261201f565b6001870180545f906115b79084906001600160601b0316613e22565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505f815111156115f2576115f260018a018483612b60565b5091979650505050505050565b5f8260ff1611801561161057508015155b15611725575f806116248560010154612c9e565b915091505f8312156116d85761163983613e42565b818560ff168151811061164e5761164e613c67565b602002602001018181516116629190613bc2565b90525060ff8481169083161480156116955750808460ff168151811061168a5761168a613c67565b60200260200101515f145b156116d357835b5f190160ff8116158015906116cc5750818160ff16815181106116c1576116c1613c67565b60200260200101515f145b61169c5791505b611717565b82818560ff16815181106116ee576116ee613c67565b602002602001018181516117029190613baf565b90525060ff8083169085161115611717578391505b611722858383612d0e565b50505b505050565b600282015460078301545f91908290829061175590600160801b90046001600160401b031686613baf565b61175f9190613baf565b905061176b8282610e85565b95945050505050565b5f6046821161178557506001919050565b6050821161179557506002919050565b605a82116117a557506004919050565b506008919050565b919050565b5f8181526003830160205260408120805490916001600160401b039091169003610ead57604051635672499160e01b815260040160405180910390fd5b80545f90600160401b900463ffffffff1615801590610ead5750815442600160401b90910463ffffffff161092915050565b80545f904290611842906201518090600160401b900463ffffffff16613baf565b1092915050565b5f81815260038301602052604081205560078201805460109061187b90600160801b90046001600160401b0316613dec565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505050565b6001600160a01b0381165f908152600484016020526040812054806118ca8560ff16612d66565b111561093c576040516369f131af60e11b815260040160405180910390fd5b60088460ff161015806118fd575060ff8416155b1561191b57604051633494a40d60e21b815260040160405180910390fd5b5f611926868461172a565b90505f821561194c57841561193c576064611945565b61194586612e0a565b9050611978565b61195586612e80565b9050845f03611978575f61196887612e0a565b905080821115611976578091505b505b8082111561199957604051633494a40d60e21b815260040160405180910390fd5b84156119fa575f6203f4806203f47f4201046203f4806203f47f880104036001600160601b03169050805f14806119da57506119d78760ff16612eda565b81115b156119f857604051633494a40d60e21b815260040160405180910390fd5b505b50505050505050565b6001600160a01b038082165f9081526003840160205260408120600181015491929091600160601b900416611ab5578354600160601b90046001600160a01b0316611a7b5783546001600160a01b038416600160601b9081026001600160601b03928316178655600183018054909216179055611ab5565b83546001820180546001600160a01b03600160601b93849004811684026001600160601b0392831617909255865491861690920291161784555b9392505050565b6040805160e08101909152821581526060905f9082908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8901046001600160601b0316815260028a81015463ffffffff1660208301528b015460078c0154604090920191611b409190600160801b90046001600160401b0316613baf565b81525f602082015260078b0154600160c01b90046001600160401b0316604090910152805190915015611b9d57611b8e611b7e6303b5380042613baf565b6203f4806203f47f919091010490565b6001600160601b031660408201525b5f611bb18a83602001518460400151612070565b60408051600880825261012082019092529192505f919060208201610100803683370190505090505f89516001600160401b03811115611bf357611bf36137d4565b604051908082528060200260200182016040528015611c1c578160200160208202803683370190505b5090505f5b8a51811015611f6357611c4d8d8c8381518110611c4057611c40613c67565b60200260200101516129ce565b8015611c795750611c778d8c8381518110611c6a57611c6a613c67565b6020026020010151612f52565b155b15611c9757604051636bb8676960e11b815260040160405180910390fd5b5f80611cbd8f8f8f8681518110611cb057611cb0613c67565b6020026020010151612a45565b602089015182549294509092505f91611ced90600160401b900463ffffffff166203f4806203f47f919091010490565b6001600160601b0316611d009190613bc2565b90505f5b81811015611d7057878181518110611d1e57611d1e613c67565b60200260200101515f03611d4557604051633494a40d60e21b815260040160405180910390fd5b878181518110611d5757611d57613c67565b6020908102919091010180515f19019052600101611d04565b50505f611d7e87898e612fb1565b82549091506001600160601b0316811115611de6578154611da8906001600160601b031682613bc2565b8860a001818151611db99190613baf565b905250611dc58161201f565b82546bffffffffffffffffffffffff19166001600160601b03919091161782555b815460ff600160a01b9091048116908d16811015611e6f57825460ff808f16600160a01b0260ff60a01b19909216919091178455875188918316908110611e2f57611e2f613c67565b602002602001018051611e4190613e5c565b90528651879060ff8f16908110611e5a57611e5a613c67565b602002602001018051611e6c90613e70565b90525b885115611ea85783546bffffffff00000000000000001916845560c089018051611e9890613ea7565b6001600160401b03169052611ecc565b835463ffffffff8f16600160401b026bffffffff0000000000000000199091161784555b604080516060808201835285546001600160601b038116808452600160601b82046001600160401b0381166020860152600160a01b90920460ff81169490950193909352901b73ffffffffffffffff000000000000000000000000161760a09190911b60ff60a01b1617868681518110611f4857611f48613c67565b60200260200101818152505084600101945050505050611c21565b50835115611fae5760c084015160078d0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055612005565b6020840151611fc29060018e019085612b60565b60078c015460408501516001600160401b03909116101561200557604084015160078d01805467ffffffffffffffff19166001600160401b039092169190911790555b60a090930151909550935090915050955095509592505050565b5f6001600160601b03821115612057576040516306dfcc6560e41b815260606004820152602481018390526044015b60405180910390fd5b5090565b5f8183106120695781611ab5565b5090919050565b60078301546060906001600160401b03165f8184101561209257839150612097565b508083035b8185101561216d57805f036120bb576120b460018701868461289d565b92506121bb565b8482036120c88282613baf565b6001600160401b038111156120df576120df6137d4565b604051908082528060200260200182016040528015612108578160200160208202803683370190505b5093505f61211a60018901888661289d565b90505f5b828110156121655781818151811061213857612138613c67565b602002602001015186828151811061215257612152613c67565b602090810291909101015260010161211e565b5050506121bb565b6121778585613bc2565b6001600160401b0381111561218e5761218e6137d4565b6040519080825280602002602001820160405280156121b7578160200160208202803683370190505b5092505b50509392505050565b5f69065a4da25d3016c000008210156121de57505f919050565b69152d02c7e14af68000008210156121f857506001919050565b693f870857a3e0e380000082101561221257506002919050565b69d3c21bcecceda100000082101561222c57506003919050565b6a027b46536c66c8e300000082101561224757506004919050565b6a084595161401484a00000082101561226257506005919050565b6a18d0bf423c03d8de00000082101561227d57506006919050565b506007919050565b8080156122955750600360ff8316105b156111aa57604051633494a40d60e21b815260040160405180910390fd5b5f61ffff821115612057576040516306dfcc6560e41b8152601060048201526024810183905260440161204e565b5f6122eb8461306b565b90505f6122f7846130c5565b90505f6123048484613ecc565b90508161ffff168161ffff16111561232f5760405163d6516b8960e01b815260040160405180910390fd5b808660050160046101000a81548161ffff021916908361ffff1602179055506112bb86868661ffff16613140565b60028301545f906123ad9061237990849063ffffffff16613baf565b60028501546007860154859161239e91600160801b90046001600160401b0316613baf565b6123a89190613baf565b6131f4565b905060328111156123d157604051632c1252af60e11b815260040160405180910390fd5b50505050565b6040805160e08101909152821581525f906060908290602081016203f4806203f47f4201046001600160601b031681526020016203f4806203f47f8801046001600160601b0316815260028981015463ffffffff1660208301528a015460078b01546040909201916124599190600160801b90046001600160401b0316613baf565b81525f602082015260078a0154600160c01b90046001600160401b03166040909101528051909150156124a657612497611b7e6303b5380042613baf565b6001600160601b031660408201525b5f6124ba8983602001518460400151612070565b90505f87516001600160401b038111156124d6576124d66137d4565b6040519080825280602002602001820160405280156124ff578160200160208202803683370190505b5090505f5b885181101561269d575f61251984868a612fb1565b85519091501561253e578460c001805161253290613ea7565b6001600160401b031690525b5f60405180606001604052806125538461201f565b6001600160601b0316815260200161256a8f61321d565b6001600160401b031681526020018a60ff1681525090506125af8b848151811061259657612596613c67565b6020026020010151828e6132689092919063ffffffff16565b6126098d8c85815181106125c5576125c5613c67565b6020026020010151604051806060016040528085602001516001600160401b031681526020018e63ffffffff168152602001336001600160a01b0316815250613356565b8051602082015160408301516001600160601b0390921660609190911b73ffffffffffffffff000000000000000000000000161760a09190911b60ff60a01b161784848151811061265c5761265c613c67565b602002602001018181525050818660a00181815161267a9190613baf565b905250505060808401805160019081019091526060850180518201905201612504565b508251156126e85760c083015160078b0180546001600160401b03909216600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905561273f565b60208301516126fc9060018c019084612b60565b60078a015460408401516001600160401b03909116101561273f57604083015160078b01805467ffffffffffffffff19166001600160401b039092169190911790555b60a0909201519350909150509550959350505050565b5f6032821161276657506001919050565b600a612773603384613bc2565b61277d9190613dd9565b612788906001613baf565b610ead906002613fc7565b5f603283116127c8576127106127b761ffff841669d3c21bcecceda1000000613bf6565b6127c19190613dd9565b9050610ead565b5f61ffff8316600a6127db603387613bc2565b6127e59190613dd9565b6127f0906002613baf565b6127fa9190613bf6565b90506127106128138269d3c21bcecceda1000000613bf6565b61281d9190613dd9565b915050610ead565b81156117225761283785848484613442565b611722848484613525565b5f604051632142170760e11b5f5284600452836024528260445260205f60645f808a5af13d15601f3d1160015f511416171691505f60605280604052508061172257604051636ff8a60f60e11b815260040160405180910390fd5b6060818311156128c05760405163d571362560e01b815260040160405180910390fd5b82820360f08111156128e55760405163d571362560e01b815260040160405180910390fd5b5f816001600160401b038111156128fe576128fe6137d4565b604051908082528060200260200182016040528015612927578160200160208202803683370190505b5090505f808061294060f0895b06600a80820492910690565b5f82815260208c90526040902054919350915062ffffff601883021b895b898110156129be57602086019550818316601885021c868801528160181b9150600184019350600181019050600a84036129b95761299d60f082612934565b5f82815260208f905260409020549196509450925062ffffff91505b61295e565b50949a9950505050505050505050565b5f818152600483016020526040812054426001600160601b03909116101580612a1057505f8281526005840160205260409020544265ffffffffffff90911610155b80612a3557505f828152600684016020526040902054426001600160601b0390911610155b80611ab55750611ab58383612f52565b5f818152600384016020526040812080549091906001600160401b03168103612a8157604051635672499160e01b815260040160405180910390fd5b612a8a826117ef565b15612aa857604051631d80cfbf60e21b815260040160405180910390fd5b505f828152602084905260409020805482546001600160401b03908116600160601b9092041614612aec57604051633c3ca9d760e11b815260040160405180910390fd5b935093915050565b6002820180545f90612b0b9063ffffffff16613fd2565b825463ffffffff9182166101009390930a92830291909202199091161790555f9081526020919091526040902080547fffffffffffffffffffffff000000000000000000000000000000000000000000169055565b805160f0811115612b845760405163d571362560e01b815260040160405180910390fd5b612b8f815f19613bc2565b831115612baf5760405163d571362560e01b815260040160405180910390fd5b5f80612bbc60f086612934565b5f8281526020898152604090912054600190960181029592945090925062ffffff601884021b19905b85811015612c84578681015162ffffff811115612c15576040516369600fad60e11b815260040160405180910390fd5b80601886021b808486161794505060018501945062ffffff601886021b199250600a8503612c7b575f86815260208b81526040909120859055612c5e9060f09084048b01612934565b5f82815260208d905260409020549197509550935062ffffff1992505b50602001612be5565b50505f928352602096909652506040902093909355505050565b60408051600880825261012082019092525f9160609190602082016101008036833750505060f084901c925090505f5b6008811015612d08578060180284901c62ffffff16828281518110612cf557612cf5613c67565b6020908102919091010152600101612cce565b50915091565b60ff60f01b60f083901b165f5b6008811015612d585780601802838281518110612d3a57612d3a613c67565b602002602001015162ffffff16901b82179150806001019050612d1b565b506001909301929092555050565b5f60088210612d73575f80fd5b8160018114612db15760028114612dbb5760038114612dc65760048114612dd15760058114612ddc5760068114612de75760078114612df257612dfa565b6175309150612dfa565b620186a09150612dfa565b620493e09150612dfa565b620f42409150612dfa565b622dc6c09150612dfa565b629896809150612dfa565b6301c9c38091505b50670de0b6b3a764000002919050565b5f8160018114612e495760028114612e495760038114612e515760048114612e5a5760058114612e635760068114612e6c5760078114612e7557612e7a565b5f9150612e7a565b60149150612e7a565b601e9150612e7a565b60289150612e7a565b60329150612e7a565b605091505b50919050565b5f8160018114612ebf5760028114612e515760038114612e5a5760048114612e635760058114612e6c5760068114612ec85760078114612ed157612e7a565b600a9150612e7a565b603c9150612e7a565b50604692915050565b5f60088210612ee7575f80fd5b8160018114612f255760028114612f2e5760038114612e515760048114612ec85760058114612f375760068114612f405760078114612f4957612e7a565b60019150612e7a565b60059150612e7a565b60789150612e7a565b60b49150612e7a565b5060f092915050565b5f81815260068301602052604081206002810154600160a01b90046001600160401b031615801590612f8f575060028101546001600160a01b0316155b801561093c5750612fa8612fa385856117b2565b6117ef565b15949350505050565b60c08201516080830151845184515f936001600160401b03169291600101602002908490156130095760205b8281101561300357808901519150612ff782860185613558565b90950194602001612fdd565b50613048565b60205b8281101561303a5780890151915061302682860185613558565b600183018a8301529095019460200161300c565b50613045858761369f565b94505b61305f8561305a8960600151866131f4565b6136cc565b98975050505050505050565b60058101545f9062015180906130879063ffffffff1642613bc2565b116130a0575060050154640100000000900461ffff1690565b50600501805465ffffffffffff191663ffffffff620151804281900402161790555f90565b5f818015612e495760018114612f25576002811461310a5760038114613113576004811461311c5760058114613125576006811461312e576007811461313757612e7a565b60029150612e7a565b60049150612e7a565b60089150612e7a565b60109150612e7a565b60209150612e7a565b50604092915050565b5f61314a836136ff565b61ffff1690508181101561317157604051632c1252af60e11b815260040160405180910390fd5b8190035f6131828560010154612c9e565b9150505f5b60088110156112bb578181815181106131a2576131a2613c67565b602002602001015183106131d3578181815181106131c2576131c2613c67565b6020026020010151830392506131ec565b604051632c1252af60e11b815260040160405180910390fd5b600101613187565b5f815f0361320357505f610ead565b81836064028161321557613215613d77565b049050610ead565b600781018054600160401b90046001600160401b031690600861323f83613ea7565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550919050565b5f82815260208490526040902054600160601b90046001600160401b0316156132a457604051637f471e3f60e11b815260040160405180910390fd5b6002830180545f906132bb9063ffffffff16613fe7565b825463ffffffff9182166101009390930a92830291909202199091161790555f91825260209283526040918290208151815494830151939092015160ff16600160a01b0260ff60a01b196001600160401b03909416600160601b027fffffffffffffffffffffffff00000000000000000000000000000000000000009095166001600160601b03909316929092179390931791909116179055565b5f8281526003840160205260409020546001600160401b03161561338d5760405163985816ed60e01b815260040160405180910390fd5b5f8281526003840160209081526040918290208351815492850151938501516001600160a01b0316600160601b026001600160601b0363ffffffff909516600160401b026bffffffffffffffffffffffff199094166001600160401b03928316179390931793909316919091179055600784018054909160109161341991600160801b90910416613ea7565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b6001600160a01b0383165f9081526004850160205260409020548281101561347d57604051638ac4bc7360e01b815260040160405180910390fd5b81156135005782810361349f613497876001015460f01c90565b60ff16612d66565b8110806134b5575085546001600160601b031681105b806134c35750856002015481105b156134e157604051638ac4bc7360e01b815260040160405180910390fd5b6001600160a01b0385165f908152600487016020526040902055611722565b6001600160a01b0384165f908152600486016020526040902083820390555050505050565b6001600160a01b0382165f9081526004840160205260408120805483929061354e908490613baf565b9091555050505050565b5f808211613570575068410d586a20a4c00000610ead565b5f82846064028161358357613583613d77565b049050602881116135a05768410d586a20a4c00000915050610ead565b603c8110156135cf57600160288203901c68068155a43676e000000268478eae0e571ba0000001915050610ead565b60468110156135fe576001603c8203901c680d02ab486cedc0000002688f1d5c1cae3740000001915050610ead565b605081101561362d57600160468203901c681a055690d9db8000000268dd2d5fcf3bc9c0000001915050610ead565b605a81101561365d57600160508203901c68340aad21b3b7000000026901794d673456eec0000001915050610ead565b60648110156136855760056916deb1154f79eb800000605919830160011c1b04915050610ead565b69492f037764b958000000915050610ead565b5092915050565b5f60038260ff1610156136b3575081610ead565b606460ff60056001198501028203168402049392505050565b5f600a8210156136dd575081610ead565b600a836136eb846001613baf565b6136f59190613bf6565b611ab59190613dd9565b5f818015612e495760018114613113576002811461311c5760038114613125576004811461312e5760058114613744576006811461374d576007811461375657612e7a565b60409150612e7a565b60809150612e7a565b5061010092915050565b80356001600160a01b03811681146117ad575f80fd5b5f805f805f805f60e0888a03121561378c575f80fd5b8735965060208801359550604088013594506137aa60608901613760565b93506080880135925060a088013591506137c660c08901613760565b905092959891949750929550565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b038111828210171561380a5761380a6137d4565b60405290565b5f82601f83011261381f575f80fd5b813560206001600160401b038083111561383b5761383b6137d4565b8260051b604051601f19603f83011681018181108482111715613860576138606137d4565b60405293845285810183019383810192508785111561387d575f80fd5b83870191505b8482101561389c57813583529183019190830190613883565b979650505050505050565b5f805f805f805f60e0888a0312156138bd575f80fd5b87359650602088013595506138d460408901613760565b94506138e260608901613760565b935060808801356001600160401b038111156138fc575f80fd5b6139088a828b01613810565b93505060a088013591506137c660c08901613760565b5f805f60608486031215613930575f80fd5b8335925060208401356001600160401b0381111561394c575f80fd5b61395886828701613810565b92505061396760408501613760565b90509250925092565b803560ff811681146117ad575f80fd5b5f60e08284031215613990575f80fd5b6139986137e8565b90506139a382613760565b81526139b160208301613760565b602082015260408201356001600160401b038111156139ce575f80fd5b6139da84828501613810565b604083015250606082013560608201526139f660808301613970565b608082015260a082013560a0820152613a1160c08301613760565b60c082015292915050565b5f805f60608486031215613a2e575f80fd5b833592506020840135915060408401356001600160401b03811115613a51575f80fd5b613a5d86828701613980565b9150509250925092565b5f805f60608486031215613a79575f80fd5b505081359360208301359350604090920135919050565b602080825282518282018190525f9190848201906040850190845b81811015613ac757835183529284019291840191600101613aab565b50909695505050505050565b5f805f805f60a08688031215613ae7575f80fd5b8535945060208601359350613afe60408701613760565b925060608601356001600160401b03811115613b18575f80fd5b613b2488828901613810565b925050613b3360808701613760565b90509295509295909350565b5f805f8060808587031215613b52575f80fd5b843593506020850135925060408501356001600160401b03811115613b75575f80fd5b613b8187828801613980565b925050613b9060608601613760565b905092959194509250565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610ead57610ead613b9b565b81810381811115610ead57610ead613b9b565b5f8160030b637fffffff198103613bee57613bee613b9b565b5f0392915050565b8082028115828204841417610ead57610ead613b9b565b5f8151808452602080850194508084015f5b83811015613c3b57815187529582019590820190600101613c1f565b509495945050505050565b604081525f613c586040830185613c0d565b90508260208301529392505050565b634e487b7160e01b5f52603260045260245ffd5b63ffffffff81811683821601908082111561369857613698613b9b565b602081525f611ab56020830184613c0d565b604081525f613cbc6040830185613c0d565b90506001600160a01b03831660208301529392505050565b6001600160601b0381811683821601908082111561369857613698613b9b565b608081525f613d066080830187613c0d565b8281036020840152613d188187613c0d565b604084019590955250506060015292915050565b60a081525f613d3e60a0830188613c0d565b8281036020840152613d508188613c0d565b6040840196909652505060608101929092526001600160a01b031660809091015292915050565b634e487b7160e01b5f52601260045260245ffd5b5f82613d9957613d99613d77565b500690565b634e487b7160e01b5f52603160045260245ffd5b600381810b9083900b01637fffffff8113637fffffff1982121715610ead57610ead613b9b565b5f82613de757613de7613d77565b500490565b5f6001600160401b03821680613e0457613e04613b9b565b5f190192915050565b5f81613e1b57613e1b613b9b565b505f190190565b6001600160601b0382811682821603908082111561369857613698613b9b565b5f600160ff1b8201613e5657613e56613b9b565b505f0390565b5f600160ff1b8201613e1b57613e1b613b9b565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ea057613ea0613b9b565b5060010190565b5f6001600160401b03808316818103613ec257613ec2613b9b565b6001019392505050565b61ffff81811683821601908082111561369857613698613b9b565b600181815b80851115613f2157815f1904821115613f0757613f07613b9b565b80851615613f1457918102915b93841c9390800290613eec565b509250929050565b5f82613f3757506001610ead565b81613f4357505f610ead565b8160018114613f595760028114613f6357613f7f565b6001915050610ead565b60ff841115613f7457613f74613b9b565b50506001821b610ead565b5060208310610133831016604e8410600b8410161715613fa2575081810a610ead565b613fac8383613ee7565b805f1904821115613fbf57613fbf613b9b565b029392505050565b5f611ab58383613f29565b5f63ffffffff821680613e0457613e04613b9b565b5f63ffffffff808316818103613ec257613ec2613b9b56fea164736f6c6343000814000a
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.