Feature Tip: Add private address tag to any address under My Name Tag !
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
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
ArchetypeBurgers404
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 1 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT // Archetype v0.8.0 - BURGERS404 // // d8888 888 888 // d88888 888 888 // d88P888 888 888 // d88P 888 888d888 .d8888b 88888b. .d88b. 888888 888 888 88888b. .d88b. // d88P 888 888P" d88P" 888 "88b d8P Y8b 888 888 888 888 "88b d8P Y8b // d88P 888 888 888 888 888 88888888 888 888 888 888 888 88888888 // d8888888888 888 Y88b. 888 888 Y8b. Y88b. Y88b 888 888 d88P Y8b. // d88P 888 888 "Y8888P 888 888 "Y8888 "Y888 "Y88888 88888P" "Y8888 // 888 888 // Y8b d88P 888 // "Y88P" 888 pragma solidity ^0.8.20; import "./ArchetypeLogicBurgers404.sol"; import "dn404/src/DN420.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "solady/src/utils/LibString.sol"; import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol"; contract ArchetypeBurgers404 is DN420, Initializable, OwnableUpgradeable, ERC2981Upgradeable { // // EVENTS // event Invited(bytes32 indexed key, bytes32 indexed cid); event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints); event Withdrawal(address indexed src, address token, uint128 wad); // // VARIABLES // mapping(bytes32 => AdvancedInvite) public invites; mapping(bytes32 => uint256) public packedBonusDiscounts; mapping(address => mapping(bytes32 => uint256)) private _minted; mapping(bytes32 => uint256) private _listSupply; mapping(address => uint128) private _ownerBalance; mapping(address => mapping(address => uint128)) private _affiliateBalance; mapping(bytes32 => bytes32) public pairedListKeys; string private _name; string private _symbol; uint256 private totalErc20Mints; Config public config; PayoutConfig public payoutConfig; uint256 public flags; // bit 0: uriLocked // bit 1: maxSupplyLocked // bit 2: ownerAltPayoutLocked // // METHODS // function initialize( string memory name_, string memory symbol_, Config calldata config_, PayoutConfig calldata payoutConfig_, address _receiver ) external initializer { _name = name_; _symbol = symbol_; config = config_; _initializeDN420(0, address(0)); // check max bps not reached and min platform fee. if ( config_.affiliateFee > MAXBPS || config_.affiliateDiscount > MAXBPS || config_.affiliateSigner == address(0) || config_.maxBatchSize == 0 ) { revert InvalidConfig(); } __Ownable_init(); uint256 totalShares = payoutConfig_.ownerBps + payoutConfig_.platformBps + payoutConfig_.partnerBps + payoutConfig_.superAffiliateBps; if (payoutConfig_.platformBps < 250 || totalShares != 10000) { revert InvalidSplitShares(); } payoutConfig = payoutConfig_; setDefaultRoyalty(_receiver, config.defaultRoyalty); } // // PUBLIC // function mint( Auth calldata auth, uint256 quantity, address affiliate, bytes calldata signature ) external payable { mintTo(auth, quantity, _msgSender(), affiliate, signature); } function batchMintTo( Auth calldata auth, address[] calldata toList, uint256[] calldata quantityList, address affiliate, bytes calldata signature ) external payable { if (quantityList.length != toList.length) { revert InvalidConfig(); } AdvancedInvite storage invite = invites[auth.key]; uint256 packedDiscount = packedBonusDiscounts[auth.key]; uint256 totalQuantity; uint256 totalBonusMints; for (uint256 i; i < toList.length; ) { uint256 quantityToAdd; if (invite.unitSize > 1) { quantityToAdd = quantityList[i] * invite.unitSize; } else { quantityToAdd = quantityList[i]; } uint256 numBonusMints = ArchetypeLogicBurgers404.bonusMintsAwarded(quantityToAdd / config.erc20Ratio, packedDiscount) * config.erc20Ratio; _mintNext(toList[i], (quantityToAdd + numBonusMints) * ERC20_UNIT, ""); totalQuantity += quantityToAdd; totalBonusMints += numBonusMints; unchecked { ++i; } } validateAndCreditMint(invite, auth, totalQuantity, totalBonusMints, totalErc20Mints, affiliate, signature); } function mintTo( Auth calldata auth, uint256 quantity, address to, address affiliate, bytes calldata signature ) public payable { AdvancedInvite storage invite = invites[auth.key]; uint256 packedDiscount = packedBonusDiscounts[auth.key]; if (invite.unitSize > 1) { quantity = quantity * invite.unitSize; } uint256 numBonusMints = ArchetypeLogicBurgers404.bonusMintsAwarded(quantity / config.erc20Ratio, packedDiscount) * config.erc20Ratio; _mintNext(to, (quantity + numBonusMints) * ERC20_UNIT, ""); validateAndCreditMint(invite, auth, quantity, numBonusMints, totalErc20Mints, affiliate, signature); } function validateAndCreditMint( AdvancedInvite storage invite, Auth calldata auth, uint256 quantity, uint256 numBonusMints, uint256 curSupply, address affiliate, bytes calldata signature ) internal { uint256 totalQuantity = quantity + numBonusMints; ValidationArgs memory args; { bytes32 pairedKey = pairedListKeys[auth.key]; uint256 pairedSupply = pairedKey != 0 ? _listSupply[bytes32(uint256(pairedKey) - 1)]: 0; args = ValidationArgs({ owner: owner(), affiliate: affiliate, quantity: totalQuantity, curSupply: curSupply, listSupply: _listSupply[auth.key], pairedSupply: pairedSupply }); } uint128 cost = uint128( ArchetypeLogicBurgers404.computePrice( invite, config.affiliateDiscount, quantity, args.listSupply, args.affiliate != address(0) ) ); ArchetypeLogicBurgers404.validateMint(invite, config, auth, _minted, signature, args, cost); if (invite.limit < invite.maxSupply) { _minted[_msgSender()][auth.key] += totalQuantity; } if (invite.maxSupply < UINT32_MAX) { _listSupply[auth.key] += totalQuantity; } totalErc20Mints += totalQuantity; ArchetypeLogicBurgers404.updateBalances( invite, config, _ownerBalance, _affiliateBalance, affiliate, quantity, cost ); if (msg.value > cost) { _refund(_msgSender(), msg.value - cost); } } function burnToRemint(uint256[] calldata tokenIds) public { if(config.remintPremium == 0) { revert burnToRemintDisabled(); } if(tokenIds.length < 1) { revert invalidTokenIdLength(); } address msgSender = _msgSender(); uint256 mintQuantity = 1 * _unit(); uint256 burnQuantity = mintQuantity * config.remintPremium / 10000; uint256 msgSenderBalance = balanceOf(msgSender); uint256 change = 0; // transfer nft 1 safeTransferNFT(msgSender, 0x000000000000000000000000000000000000dEaD, tokenIds[0], ""); // if premium will make minter lose an nft, transfer nft 2 and give back change, otherwise just transfer erc20 if(msgSenderBalance % _unit() < burnQuantity) { if(tokenIds.length < 2) { revert invalidTokenIdLength(); } _safeTransferNFT(msgSender, msgSender, 0x000000000000000000000000000000000000dEaD, tokenIds[1], ""); change += _unit() - burnQuantity; } else { _transfer(msgSender, 0x000000000000000000000000000000000000dEaD, burnQuantity, ""); } // remint _mintNext(msgSender, mintQuantity + change, ""); } function withdraw() external { address[] memory tokens = new address[](1); tokens[0] = address(0); withdrawTokens(tokens); } function withdrawTokens(address[] memory tokens) public { ArchetypeLogicBurgers404.withdrawTokens(payoutConfig, _ownerBalance, owner(), tokens); } function withdrawAffiliate() external { address[] memory tokens = new address[](1); tokens[0] = address(0); withdrawTokensAffiliate(tokens); } function withdrawTokensAffiliate(address[] memory tokens) public { ArchetypeLogicBurgers404.withdrawTokensAffiliate(_affiliateBalance, tokens); } function ownerBalance() external view returns (uint128) { return _ownerBalance[address(0)]; } function ownerBalanceToken(address token) external view returns (uint128) { return _ownerBalance[token]; } function affiliateBalance(address affiliate) external view returns (uint128) { return _affiliateBalance[affiliate][address(0)]; } function affiliateBalanceToken(address affiliate, address token) external view returns (uint128) { return _affiliateBalance[affiliate][token]; } function minted(address minter, bytes32 key) external view returns (uint256) { return _minted[minter][key]; } function listSupply(bytes32 key) external view returns (uint256) { return _listSupply[key]; } function numErc20Minted() public view returns (uint256) { return totalErc20Mints; } function numNftsMinted() public view returns (uint256) { return totalErc20Mints / config.erc20Ratio; } function balanceOfNFT(address owner) public view returns (uint256) { return _balanceOfNFT(owner); } function exists(uint256 id) external view returns (bool) { return _exists(id); } function platform() external pure returns (address) { return PLATFORM; } function computePrice( bytes32 key, uint256 quantity, bool affiliateUsed ) external view returns (uint256) { AdvancedInvite storage i = invites[key]; uint256 listSupply_ = _listSupply[key]; return ArchetypeLogicBurgers404.computePrice(i, config.affiliateDiscount, quantity, listSupply_, affiliateUsed); } // // Overides // function name() public view override returns (string memory) { return _name; } function symbol() public view override returns (string memory) { return _symbol; } function uri(uint256 tokenId) public view override returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); return bytes(config.baseUri).length != 0 ? string(abi.encodePacked(config.baseUri, LibString.toString(tokenId))) : ""; } // // OWNER ONLY // function setBaseURI(string memory baseUri) external _onlyOwner { if (_getFlag(0)) { revert LockedForever(); } config.baseUri = baseUri; } /// @notice the password is "forever" function lockURI(string calldata password) external _onlyOwner { _checkPassword(password); _setFlag(0); } // max supply cannot subceed total supply. Be careful changing. function setMaxSupply(uint32 maxSupply) external _onlyOwner { if (_getFlag(1)) { revert LockedForever(); } if (maxSupply < numErc20Minted()) { revert MaxSupplyExceeded(); } config.maxSupply = maxSupply; } /// @notice the password is "forever" function lockMaxSupply(string calldata password) external _onlyOwner { _checkPassword(password); _setFlag(1); } function setAffiliateFee(uint16 affiliateFee) external _onlyOwner { if (affiliateFee > MAXBPS) { revert InvalidConfig(); } config.affiliateFee = affiliateFee; } function setAffiliateDiscount(uint16 affiliateDiscount) external _onlyOwner { if (affiliateDiscount > MAXBPS) { revert InvalidConfig(); } config.affiliateDiscount = affiliateDiscount; } function setOwnerAltPayout(address ownerAltPayout) external _onlyOwner { if (_getFlag(2)) { revert LockedForever(); } payoutConfig.ownerAltPayout = ownerAltPayout; } /// @notice the password is "forever" function lockOwnerAltPayout(string calldata password) external _onlyOwner { _checkPassword(password); _setFlag(2); } function setMaxBatchSize(uint32 maxBatchSize) external _onlyOwner { config.maxBatchSize = maxBatchSize; } function setRemintPremium(uint16 remintPremium) external _onlyOwner { config.remintPremium = remintPremium; } // Up to 8 discount tiers: [discount7][discount6][discount5][discount4][discount3][discount2][discount1][discount0] function setBonusDiscounts(bytes32 _key, BonusDiscount[] calldata _bonusDiscounts) public onlyOwner { if(_bonusDiscounts.length > 8) { revert InvalidConfig(); } uint256 packed; for (uint8 i = 0; i < _bonusDiscounts.length; i++) { if (i > 0 && _bonusDiscounts[i].numMints >= _bonusDiscounts[i - 1].numMints) { revert InvalidConfig(); } uint32 discount = (uint32(_bonusDiscounts[i].numMints) << 16) | uint32(_bonusDiscounts[i].numBonusMints); packed |= uint256(discount) << (32 * i); } packedBonusDiscounts[_key] = packed; } function setBonusInvite( bytes32 _key, bytes32 _cid, AdvancedInvite calldata _advancedInvite, BonusDiscount[] calldata _bonusDiscount ) external _onlyOwner { setBonusDiscounts(_key, _bonusDiscount); setAdvancedInvite(_key, _cid, _advancedInvite); } function setInvite( bytes32 _key, bytes32 _cid, Invite calldata _invite ) external _onlyOwner { setAdvancedInvite(_key, _cid, AdvancedInvite({ price: _invite.price, reservePrice: _invite.price, delta: 0, start: _invite.start, end: _invite.end, limit: _invite.limit, maxSupply: _invite.maxSupply, interval: 0, unitSize: _invite.unitSize, tokenAddress: _invite.tokenAddress, isBlacklist: _invite.isBlacklist })); } function setAdvancedInvite( bytes32 _key, bytes32 _cid, AdvancedInvite memory _AdvancedInvite ) public _onlyOwner { // approve token for withdrawals if erc20 list if (_AdvancedInvite.tokenAddress != address(0)) { bool success = IERC20(_AdvancedInvite.tokenAddress).approve(PAYOUTS, 2**256 - 1); if (!success) { revert NotApprovedToTransfer(); } } if (_AdvancedInvite.start < block.timestamp) { _AdvancedInvite.start = uint32(block.timestamp); } invites[_key] = _AdvancedInvite; emit Invited(_key, _cid); } // method will pair the supplies of two invite lists function setPairedInvite(bytes32 key1, bytes32 key2) external _onlyOwner { if(invites[key1].maxSupply != invites[key2].maxSupply) { revert InvalidConfig(); } pairedListKeys[key1] = bytes32(uint256(key2) + 1); pairedListKeys[key2] = bytes32(uint256(key1) + 1); } // // INTERNAL // function _unit() internal view override returns (uint256) { return ERC20_UNIT * uint256(config.erc20Ratio); } function _msgSender() internal view override returns (address) { return msg.sender == BATCH ? tx.origin : msg.sender; } modifier _onlyOwner() { if (_msgSender() != owner()) { revert NotOwner(); } _; } function _refund(address to, uint256 refund) internal { (bool success, ) = payable(to).call{ value: refund }(""); if (!success) { revert TransferFailed(); } } function _checkPassword(string calldata password) internal pure { if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) { revert WrongPassword(); } } function _setFlag(uint256 flag) internal { flags |= 1 << flag; } function _getFlag(uint256 flag) internal view returns (bool) { return (flags & (1 << flag)) != 0; } //ERC2981 ROYALTY function supportsInterface(bytes4 interfaceId) public view virtual override(DN420, ERC2981Upgradeable) returns (bool) { // Supports the following `interfaceId`s: // - IERC165: 0x01ffc9a7 // - ERC1155: 0xd9b67a26 // - ERC1155MetadataURI: 0x0e89341c // - IERC2981: 0x2a55205a return DN420.supportsInterface(interfaceId) || ERC2981Upgradeable.supportsInterface(interfaceId); } function setDefaultRoyalty(address receiver, uint16 feeNumerator) public _onlyOwner { config.defaultRoyalty = feeNumerator; _setDefaultRoyalty(receiver, feeNumerator); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165Upgradeable.sol"; /** * @dev Interface for the NFT Royalty Standard. * * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal * support for royalty payments across all NFT marketplaces and ecosystem participants. * * _Available since v4.5._ */ interface IERC2981Upgradeable is IERC165Upgradeable { /** * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of * exchange. The royalty amount is denominated and should be paid in that same unit of exchange. */ function royaltyInfo( uint256 tokenId, uint256 salePrice ) external view returns (address receiver, uint256 royaltyAmount); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/common/ERC2981.sol) pragma solidity ^0.8.0; import "../../interfaces/IERC2981Upgradeable.sol"; import "../../utils/introspection/ERC165Upgradeable.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information. * * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first. * * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the * fee is specified in basis points by default. * * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported. * * _Available since v4.5._ */ abstract contract ERC2981Upgradeable is Initializable, IERC2981Upgradeable, ERC165Upgradeable { struct RoyaltyInfo { address receiver; uint96 royaltyFraction; } RoyaltyInfo private _defaultRoyaltyInfo; mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo; function __ERC2981_init() internal onlyInitializing { } function __ERC2981_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) { return interfaceId == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc IERC2981Upgradeable */ function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) { RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId]; if (royalty.receiver == address(0)) { royalty = _defaultRoyaltyInfo; } uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator(); return (royalty.receiver, royaltyAmount); } /** * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an * override. */ function _feeDenominator() internal pure virtual returns (uint96) { return 10000; } /** * @dev Sets the royalty information that all ids in this contract will default to. * * Requirements: * * - `receiver` cannot be the zero address. * - `feeNumerator` cannot be greater than the fee denominator. */ function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual { require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice"); require(receiver != address(0), "ERC2981: invalid receiver"); _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator); } /** * @dev Removes default royalty information. */ function _deleteDefaultRoyalty() internal virtual { delete _defaultRoyaltyInfo; } /** * @dev Sets the royalty information for a specific token id, overriding the global default. * * Requirements: * * - `receiver` cannot be the zero address. * - `feeNumerator` cannot be greater than the fee denominator. */ function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual { require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice"); require(receiver != address(0), "ERC2981: Invalid parameters"); _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator); } /** * @dev Resets royalty information for the token id back to the global default. */ function _resetTokenRoyalty(uint256 tokenId) internal virtual { delete _tokenRoyaltyInfo[tokenId]; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[48] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165Upgradeable.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165Upgradeable).interfaceId; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @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 IERC165Upgradeable { /** * @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 v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // ArchetypePayouts v0.7.0 // // d8888 888 888 // d88888 888 888 // d88P888 888 888 // d88P 888 888d888 .d8888b 88888b. .d88b. 888888 888 888 88888b. .d88b. // d88P 888 888P" d88P" 888 "88b d8P Y8b 888 888 888 888 "88b d8P Y8b // d88P 888 888 888 888 888 88888888 888 888 888 888 888 88888888 // d8888888888 888 Y88b. 888 888 Y8b. Y88b. Y88b 888 888 d88P Y8b. // d88P 888 888 "Y8888P 888 888 "Y8888 "Y888 "Y88888 88888P" "Y8888 // 888 888 // Y8b d88P 888 // pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; error InvalidLength(); error InvalidSplitShares(); error TransferFailed(); error BalanceEmpty(); error NotApprovedToWithdraw(); contract ArchetypePayouts { event Withdrawal(address indexed src, address token, uint256 wad); event FundsAdded(address indexed recipient, address token, uint256 amount); mapping(address => mapping(address => uint256)) private _balance; mapping(address => mapping(address => bool)) private _approvals; function updateBalances( uint256 totalAmount, address token, address[] calldata recipients, uint16[] calldata splits ) public payable { if (recipients.length != splits.length) { revert InvalidLength(); } uint256 totalShares = 0; for (uint256 i = 0; i < splits.length; i++) { totalShares += splits[i]; } if (totalShares != 10000) { revert InvalidSplitShares(); } if (token == address(0)) { // ETH payments uint256 totalReceived = msg.value; for (uint256 i = 0; i < recipients.length; i++) { if (splits[i] > 0) { uint256 amountToAdd = (totalReceived * splits[i]) / 10000; _balance[recipients[i]][token] += amountToAdd; emit FundsAdded(recipients[i], token, amountToAdd); } } } else { // ERC20 payments IERC20 paymentToken = IERC20(token); bool success = paymentToken.transferFrom(msg.sender, address(this), totalAmount); if (!success) { revert TransferFailed(); } for (uint256 i = 0; i < recipients.length; i++) { if (splits[i] > 0) { uint256 amountToAdd = (totalAmount * splits[i]) / 10000; _balance[recipients[i]][token] += amountToAdd; emit FundsAdded(recipients[i], token, amountToAdd); } } } } function withdraw() external { address msgSender = msg.sender; _withdraw(msgSender, msgSender, address(0)); } function withdrawTokens(address[] memory tokens) external { address msgSender = msg.sender; for (uint256 i = 0; i < tokens.length; i++) { _withdraw(msgSender, msgSender, tokens[i]); } } function withdrawFrom(address from, address to) public { if (from != msg.sender && !_approvals[from][to]) { revert NotApprovedToWithdraw(); } _withdraw(from, to, address(0)); } function withdrawTokensFrom( address from, address to, address[] memory tokens ) public { if (from != msg.sender && !_approvals[from][to]) { revert NotApprovedToWithdraw(); } for (uint256 i = 0; i < tokens.length; i++) { _withdraw(from, to, tokens[i]); } } function _withdraw( address from, address to, address token ) internal { uint256 wad; wad = _balance[from][token]; _balance[from][token] = 0; if (wad == 0) { revert BalanceEmpty(); } if (token == address(0)) { bool success = false; (success, ) = to.call{ value: wad }(""); if (!success) { revert TransferFailed(); } } else { IERC20 erc20Token = IERC20(token); bool success = erc20Token.transfer(to, wad); if (!success) { revert TransferFailed(); } } emit Withdrawal(from, token, wad); } function approveWithdrawal(address delegate, bool approved) external { _approvals[msg.sender][delegate] = approved; } function isApproved(address from, address delegate) external view returns (bool) { return _approvals[from][delegate]; } function balance(address recipient) external view returns (uint256) { return _balance[recipient][address(0)]; } function balanceToken(address recipient, address token) external view returns (uint256) { return _balance[recipient][token]; } }
// SPDX-License-Identifier: MIT // ArchetypeLogic v0.8.0 - BURGERS404 // // d8888 888 888 // d88888 888 888 // d88P888 888 888 // d88P 888 888d888 .d8888b 88888b. .d88b. 888888 888 888 88888b. .d88b. // d88P 888 888P" d88P" 888 "88b d8P Y8b 888 888 888 888 "88b d8P Y8b // d88P 888 888 888 888 888 88888888 888 888 888 888 888 88888888 // d8888888888 888 Y88b. 888 888 Y8b. Y88b. Y88b 888 888 d88P Y8b. // d88P 888 888 "Y8888P 888 888 "Y8888 "Y888 "Y88888 88888P" "Y8888 // 888 888 // Y8b d88P 888 // "Y88P" 888 pragma solidity ^0.8.20; import "../ArchetypePayouts.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "solady/src/utils/MerkleProofLib.sol"; import "solady/src/utils/ECDSA.sol"; error InvalidConfig(); error MintNotYetStarted(); error MintEnded(); error WalletUnauthorizedToMint(); error InsufficientEthSent(); error ExcessiveEthSent(); error Erc20BalanceTooLow(); error MaxSupplyExceeded(); error ListMaxSupplyExceeded(); error NumberOfMintsExceeded(); error MintingPaused(); error InvalidReferral(); error InvalidSignature(); error MaxBatchSizeExceeded(); error NotTokenOwner(); error NotPlatform(); error NotOwner(); error NotShareholder(); error NotApprovedToTransfer(); error InvalidAmountOfTokens(); error WrongPassword(); error LockedForever(); error Blacklisted(); error URIQueryForNonexistentToken(); error invalidTokenIdLength(); error burnToRemintDisabled(); // // STRUCTS // struct Auth { bytes32 key; bytes32[] proof; } struct BonusDiscount { uint16 numMints; uint16 numBonusMints; } struct Config { string baseUri; address affiliateSigner; uint32 maxSupply; // in erc20 uint32 maxBatchSize; // in erc20 uint16 affiliateFee; //BPS uint16 affiliateDiscount; // BPS uint16 defaultRoyalty; //BPS uint16 remintPremium; //BPS premium for burning and reminting a new token uint16 erc20Ratio; // number of erc20 (10**18) equal to one nft } struct PayoutConfig { uint16 ownerBps; uint16 platformBps; uint16 partnerBps; uint16 superAffiliateBps; address partner; address superAffiliate; address ownerAltPayout; } struct AdvancedInvite { uint128 price; // in erc20 uint128 reservePrice; // in erc20 uint128 delta; // in erc20 uint32 maxSupply; // in erc20 uint32 limit; // in erc20 uint32 start; uint32 end; uint32 interval; uint32 unitSize; // mint 1 get x address tokenAddress; bool isBlacklist; } struct Invite { uint128 price; uint32 maxSupply; // in erc20 uint32 limit; // in erc20 uint32 start; uint32 end; uint32 unitSize; // mint 1 get x address tokenAddress; bool isBlacklist; } struct ValidationArgs { address owner; address affiliate; uint256 quantity; uint256 curSupply; uint256 listSupply; uint256 pairedSupply; } // UPDATE CONSTANTS BEFORE DEPLOY address constant PLATFORM = 0x86B82972282Dd22348374bC63fd21620F7ED847B; address constant BATCH = 0x6Bc558A6DC48dEfa0e7022713c23D65Ab26e4Fa7; address constant PAYOUTS = 0xaAfdfA4a935d8511bF285af11A0544ce7e4a1199; uint16 constant MAXBPS = 5000; // max fee or discount is 50% uint32 constant UINT32_MAX = 2**32 - 1; uint256 constant ERC20_UNIT = 10 ** 18; library ArchetypeLogicBurgers404 { // // EVENTS // event Invited(bytes32 indexed key, bytes32 indexed cid); event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints); event Withdrawal(address indexed src, address token, uint128 wad); // calculate price based on affiliate usage and mint discounts function computePrice( AdvancedInvite storage invite, uint16 affiliateDiscount, uint256 numTokens, uint256 listSupply, bool affiliateUsed ) public view returns (uint256) { uint256 price = invite.price; uint256 cost; if (invite.interval > 0 && invite.delta > 0) { // Apply dutch pricing uint256 diff = (((block.timestamp - invite.start) / invite.interval) * invite.delta); if (price > invite.reservePrice) { if (diff > price - invite.reservePrice) { price = invite.reservePrice; } else { price = price - diff; } } else if (price < invite.reservePrice) { if (diff > invite.reservePrice - price) { price = invite.reservePrice; } else { price = price + diff; } } cost = price * numTokens; } else if (invite.interval == 0 && invite.delta > 0) { // Apply linear curve uint256 lastPrice = price + invite.delta * listSupply; cost = lastPrice * numTokens + (invite.delta * numTokens * (numTokens - 1)) / 2; } else { cost = price * numTokens; } if (affiliateUsed) { cost = cost - ((cost * affiliateDiscount) / 10000); } return cost; } function bonusMintsAwarded(uint256 numNfts, uint256 packedDiscount) internal pure returns (uint256) { for (uint8 i = 0; i < 8; i++) { uint32 discount = uint32((packedDiscount >> (32 * i)) & 0xFFFFFFFF); uint16 tierNumMints = uint16(discount >> 16); uint16 tierBonusMints = uint16(discount); if (tierNumMints == 0) { break; // End of valid discounts } if (numNfts >= tierNumMints) { return (numNfts / tierNumMints) * tierBonusMints; } } return 0; } function validateMint( AdvancedInvite storage i, Config storage config, Auth calldata auth, mapping(address => mapping(bytes32 => uint256)) storage minted, bytes calldata signature, ValidationArgs memory args, uint128 cost ) public view { address msgSender = _msgSender(); if (args.affiliate != address(0)) { if ( args.affiliate == PLATFORM || args.affiliate == args.owner || args.affiliate == msgSender ) { revert InvalidReferral(); } validateAffiliate(args.affiliate, signature, config.affiliateSigner); } if (i.limit == 0) { revert MintingPaused(); } if (!i.isBlacklist) { if (!verify(auth, i.tokenAddress, msgSender)) { revert WalletUnauthorizedToMint(); } } else { if (verify(auth, i.tokenAddress, msgSender)) { revert Blacklisted(); } } if (block.timestamp < i.start) { revert MintNotYetStarted(); } if (i.end > i.start && block.timestamp > i.end) { revert MintEnded(); } if (i.limit < i.maxSupply) { uint256 totalAfterMint = minted[msgSender][auth.key] + args.quantity; if (totalAfterMint > i.limit) { revert NumberOfMintsExceeded(); } } if (i.maxSupply < config.maxSupply) { uint256 totalAfterMint = args.listSupply + args.pairedSupply + args.quantity; if (totalAfterMint > i.maxSupply) { revert ListMaxSupplyExceeded(); } } if (args.quantity > config.maxBatchSize) { revert MaxBatchSizeExceeded(); } if ((args.curSupply + args.quantity) > config.maxSupply) { revert MaxSupplyExceeded(); } if (i.tokenAddress != address(0)) { IERC20 erc20Token = IERC20(i.tokenAddress); if (erc20Token.allowance(msgSender, address(this)) < cost) { revert NotApprovedToTransfer(); } if (erc20Token.balanceOf(msgSender) < cost) { revert Erc20BalanceTooLow(); } if (msg.value != 0) { revert ExcessiveEthSent(); } } else { if (msg.value < cost) { revert InsufficientEthSent(); } } } function updateBalances( AdvancedInvite storage i, Config storage config, mapping(address => uint128) storage _ownerBalance, mapping(address => mapping(address => uint128)) storage _affiliateBalance, address affiliate, uint256 quantity, uint128 value ) public { address tokenAddress = i.tokenAddress; uint128 affiliateWad; if (affiliate != address(0)) { affiliateWad = (value * config.affiliateFee) / 10000; _affiliateBalance[affiliate][tokenAddress] += affiliateWad; emit Referral(affiliate, tokenAddress, affiliateWad, quantity); } uint128 balance = _ownerBalance[tokenAddress]; uint128 ownerWad = value - affiliateWad; _ownerBalance[tokenAddress] = balance + ownerWad; if (tokenAddress != address(0)) { IERC20 erc20Token = IERC20(tokenAddress); bool success = erc20Token.transferFrom(_msgSender(), address(this), value); if (!success) { revert TransferFailed(); } } } function withdrawTokensAffiliate( mapping(address => mapping(address => uint128)) storage _affiliateBalance, address[] calldata tokens ) public { address msgSender = _msgSender(); for (uint256 i; i < tokens.length; i++) { address tokenAddress = tokens[i]; uint128 wad = _affiliateBalance[msgSender][tokenAddress]; _affiliateBalance[msgSender][tokenAddress] = 0; if (wad == 0) { revert BalanceEmpty(); } if (tokenAddress == address(0)) { bool success = false; (success, ) = msgSender.call{ value: wad }(""); if (!success) { revert TransferFailed(); } } else { IERC20 erc20Token = IERC20(tokenAddress); bool success = erc20Token.transfer(msgSender, wad); if (!success) { revert TransferFailed(); } } emit Withdrawal(msgSender, tokenAddress, wad); } } function withdrawTokens( PayoutConfig storage payoutConfig, mapping(address => uint128) storage _ownerBalance, address owner, address[] calldata tokens ) public { address msgSender = _msgSender(); for (uint256 i; i < tokens.length; i++) { address tokenAddress = tokens[i]; uint128 wad; if ( msgSender == owner || msgSender == PLATFORM || msgSender == payoutConfig.partner || msgSender == payoutConfig.superAffiliate || msgSender == payoutConfig.ownerAltPayout ) { wad = _ownerBalance[tokenAddress]; _ownerBalance[tokenAddress] = 0; } else { revert NotShareholder(); } if (wad == 0) { revert BalanceEmpty(); } if (payoutConfig.ownerAltPayout == address(0)) { address[] memory recipients = new address[](4); recipients[0] = owner; recipients[1] = PLATFORM; recipients[2] = payoutConfig.partner; recipients[3] = payoutConfig.superAffiliate; uint16[] memory splits = new uint16[](4); splits[0] = payoutConfig.ownerBps; splits[1] = payoutConfig.platformBps; splits[2] = payoutConfig.partnerBps; splits[3] = payoutConfig.superAffiliateBps; if (tokenAddress == address(0)) { ArchetypePayouts(PAYOUTS).updateBalances{ value: wad }( wad, tokenAddress, recipients, splits ); } else { ArchetypePayouts(PAYOUTS).updateBalances(wad, tokenAddress, recipients, splits); } } else { uint256 ownerShare = (uint256(wad) * payoutConfig.ownerBps) / 10000; uint256 remainingShare = wad - ownerShare; if (tokenAddress == address(0)) { (bool success, ) = payable(payoutConfig.ownerAltPayout).call{ value: ownerShare }(""); if (!success) revert TransferFailed(); } else { IERC20(tokenAddress).transfer(payoutConfig.ownerAltPayout, ownerShare); } address[] memory recipients = new address[](3); recipients[0] = PLATFORM; recipients[1] = payoutConfig.partner; recipients[2] = payoutConfig.superAffiliate; uint16[] memory splits = new uint16[](3); uint16 remainingBps = 10000 - payoutConfig.ownerBps; splits[1] = uint16((uint256(payoutConfig.partnerBps) * 10000) / remainingBps); splits[2] = uint16((uint256(payoutConfig.superAffiliateBps) * 10000) / remainingBps); splits[0] = 10000 - splits[1] - splits[2]; if (tokenAddress == address(0)) { ArchetypePayouts(PAYOUTS).updateBalances{ value: remainingShare }( remainingShare, tokenAddress, recipients, splits ); } else { ArchetypePayouts(PAYOUTS).updateBalances( remainingShare, tokenAddress, recipients, splits ); } } emit Withdrawal(msgSender, tokenAddress, wad); } } function validateAffiliate( address affiliate, bytes calldata signature, address affiliateSigner ) public view { bytes32 signedMessagehash = ECDSA.toEthSignedMessageHash( keccak256(abi.encodePacked(affiliate)) ); address signer = ECDSA.recover(signedMessagehash, signature); if (signer != affiliateSigner) { revert InvalidSignature(); } } function verify( Auth calldata auth, address tokenAddress, address account ) public pure returns (bool) { // keys 0-255 and tokenAddress are public if (uint256(auth.key) <= 0xff || auth.key == keccak256(abi.encodePacked(tokenAddress))) { return true; } return MerkleProofLib.verify(auth.proof, auth.key, keccak256(abi.encodePacked(account))); } function _msgSender() internal view returns (address) { return msg.sender == BATCH ? tx.origin : msg.sender; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @title DN420 /// @notice DN420 is a fully standard compliant, single-contract, /// ERC20 and ERC1155 chimera implementation that mints /// and burns NFTs based on an account's ERC20 token balance. /// /// This contract has not yet been audited. USE AT YOUR OWN RISK! /// /// @author vectorized.eth (@optimizoor) /// @author Quit (@0xQuit) /// @author Michael Amadi (@AmadiMichaels) /// @author cygaar (@0xCygaar) /// @author Thomas (@0xjustadev) /// @author Harrison (@PopPunkOnChain) /// /// @dev Note: /// - On-transfer token ID burning scheme: /// * DN420: Largest token ID up to owned checkpoint (inclusive) first. /// * DN404: Most recently acquired token ID first. /// - This implementation uses bitmap scans to find ERC1155 token IDs /// to transfer / burn upon ERC20 transfers. /// - For long-term gas efficiency, please ensure that the maximum /// supply of NFTs is bounded and not too big. /// 10k is fine; it will cost less than 100k gas to bitmap scan 10k bits. /// Otherwise, users can still always call `setOwnedCheckpoint` to unblock. /// - A unit worth of ERC20 tokens equates to a deed to one NFT token. /// The skip NFT status determines if this deed is automatically exercised. /// An account can configure their skip NFT status. /// * If `getSkipNFT(owner) == true`, ERC20 mints / transfers to `owner` /// will NOT trigger NFT mints / transfers to `owner` (i.e. deeds are left unexercised). /// * If `getSkipNFT(owner) == false`, ERC20 mints / transfers to `owner` /// will trigger NFT mints / transfers to `owner`, until the NFT balance of `owner` /// is equal to its ERC20 balance divided by the unit (rounded down). /// - Invariant: `_balanceOfNFT(owner) <= balanceOf(owner) / _unit()`. /// - The gas costs for automatic minting / transferring / burning of NFTs is O(n). /// This can exceed the block gas limit. /// Applications and users may need to break up large transfers into a few transactions. /// - This implementation uses safe transfers for automatic NFT transfers, /// as all transfers require the recipient check by the ERC1155 spec. /// - The ERC20 token allowances and ERC1155 token / operator approvals are separate. /// - For MEV safety, users should NOT have concurrently open orders for the ERC20 and ERC1155. abstract contract DN420 { /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* EVENTS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Emitted when `amount` tokens is transferred from `from` to `to`. event Transfer(address indexed from, address indexed to, uint256 amount); /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`. event Approval(address indexed owner, address indexed spender, uint256 amount); /// @dev Emitted when `owner` sets their skipNFT flag to `status`. event SkipNFTSet(address indexed owner, bool status); /// @dev Emitted when `owner` sets their owned checkpoint to `id`. event OwnedCheckpointSet(address indexed owner, uint256 id); /// @dev Emitted when `amount` of token `id` is transferred /// from `from` to `to` by `operator`. event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 amount ); /// @dev Emitted when `amounts` of token `ids` are transferred /// from `from` to `to` by `operator`. event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] amounts ); /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens. event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved); /// @dev Emitted when the Uniform Resource Identifier (URI) for token `id` /// is updated to `value`. This event is not used in the base contract. /// You may need to emit this event depending on your URI logic. /// /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata event URI(string value, uint256 indexed id); /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. uint256 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. uint256 private constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /// @dev `keccak256(bytes("SkipNFTSet(address,bool)"))`. uint256 private constant _SKIP_NFT_SET_EVENT_SIGNATURE = 0xb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d669393; /// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`. uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE = 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62; /// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`. uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE = 0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb; /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`. uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE = 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CUSTOM ERRORS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Thrown when attempting to double-initialize the contract. error DNAlreadyInitialized(); /// @dev The contract has not been initialized. error DNNotInitialized(); /// @dev Thrown when attempting to transfer or burn more tokens than sender's balance. error InsufficientBalance(); /// @dev Thrown when a spender attempts to transfer tokens with an insufficient allowance. error InsufficientAllowance(); /// @dev Thrown when minting an amount of tokens that would overflow the max tokens. error TotalSupplyOverflow(); /// @dev The lengths of the input arrays are not the same. error ArrayLengthsMismatch(); /// @dev The unit must be greater than zero and less than `2**96`. error InvalidUnit(); /// @dev Thrown when attempting to transfer tokens to the zero address. error TransferToZeroAddress(); /// @dev Thrown when transferring an NFT /// and the caller is not the owner or an approved operator. error NotOwnerNorApproved(); /// @dev Thrown when transferring an NFT and the from address is not the current owner. error TransferFromIncorrectOwner(); /// @dev The amount of ERC1155 NFT transferred per token must be 1. error InvalidNFTAmount(); /// @dev The function selector is not recognized. error FnSelectorNotRecognized(); /// @dev Cannot safely transfer to a contract that does not implement /// the ERC1155Receiver interface. error TransferToNonERC1155ReceiverImplementer(); /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONSTANTS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev The flag to denote that the skip NFT flag is initialized. uint8 internal constant _ADDRESS_DATA_SKIP_NFT_INITIALIZED_FLAG = 1 << 0; /// @dev The flag to denote that the address should skip NFTs. uint8 internal constant _ADDRESS_DATA_SKIP_NFT_FLAG = 1 << 1; /// @dev The flag to denote that the address has overridden the default Permit2 allowance. uint8 internal constant _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG = 1 << 2; /// @dev The canonical Permit2 address. /// For signature-based allowance granting for single transaction ERC20 `transferFrom`. /// To enable, override `_givePermit2DefaultInfiniteAllowance()`. /// [Github](https://github.com/Uniswap/permit2) /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* STORAGE */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Struct containing an address's token data and settings. struct AddressData { // Auxiliary data. uint88 aux; // Flags for `initialized` and `skipNFT`. uint8 flags; // The index which to start scanning backwards for burnable / transferable token IDs. uint32 ownedCheckpoint; // The number of NFT tokens. uint32 ownedCount; // The token balance in wei. uint96 balance; } /// @dev A bitmap in storage. struct Bitmap { uint256 spacer; } /// @dev A struct to wrap a uint256 in storage. struct Uint256Ref { uint256 value; } /// @dev A mapping of an address pair to a Uint256Ref. struct AddressPairToUint256RefMap { uint256 spacer; } /// @dev Struct containing the base token contract storage. struct DN420Storage { // Next NFT ID to assign for a mint. uint32 nextTokenId; // This is greater than or equal to the largest NFT ID minted thus far. // A non-zero value is used to denote that the contract has been initialized. uint32 tokenIdUpTo; // Total supply of tokens. uint96 totalSupply; // Mapping of user operator approvals for NFTs. AddressPairToUint256RefMap operatorApprovals; // Bitmap of whether a NFT ID exists. Bitmap exists; // Mapping of user allowances for ERC20 spenders. AddressPairToUint256RefMap allowance; // Bitmap of NFT IDs owned by an address. mapping(address => Bitmap) owned; // Mapping of user account AddressData. mapping(address => AddressData) addressData; } /// @dev Returns a storage pointer for DN420Storage. function _getDN420Storage() internal pure virtual returns (DN420Storage storage $) { /// @solidity memory-safe-assembly assembly { // `uint72(bytes9(keccak256("DN420_STORAGE")))`. $.slot := 0xb6dffd38a260769cb2 // Truncate to 9 bytes to reduce bytecode size. } } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INITIALIZER */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Initializes the DN420 contract with an /// `initialTokenSupply` and `initialTokenOwner`. /// /// Note: The `initialSupplyOwner` will have their skip NFT status set to true. function _initializeDN420(uint256 initialTokenSupply, address initialSupplyOwner) internal virtual { DN420Storage storage $ = _getDN420Storage(); if ($.tokenIdUpTo != 0) revert DNAlreadyInitialized(); unchecked { $.tokenIdUpTo = uint32((initialTokenSupply / _unit()) | 1); if (_unit() - 1 >= 2 ** 96 - 1) revert InvalidUnit(); } $.nextTokenId = 1; if (initialTokenSupply != 0) { if (initialSupplyOwner == address(0)) revert TransferToZeroAddress(); if (_totalSupplyOverflows(initialTokenSupply)) revert TotalSupplyOverflow(); $.totalSupply = uint96(initialTokenSupply); AddressData storage initialOwnerAddressData = $.addressData[initialSupplyOwner]; initialOwnerAddressData.balance = uint96(initialTokenSupply); /// @solidity memory-safe-assembly assembly { // Emit the ERC20 {Transfer} event. mstore(0x00, initialTokenSupply) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, initialSupplyOwner))) } _setSkipNFT(initialSupplyOwner, true); } } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* BASE UNIT FUNCTION TO OVERRIDE */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Amount of token balance that is equal to one NFT. /// /// Note: The return value MUST be kept constant after `_initializeDN420` is called. function _unit() internal view virtual returns (uint256) { return 10 ** 18; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* METADATA FUNCTIONS TO OVERRIDE */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns the name of the token. function name() public view virtual returns (string memory); /// @dev Returns the symbol of the token. function symbol() public view virtual returns (string memory); /// @dev Returns the URI for token `id`. /// /// You can either return the same templated URI for all token IDs, /// (e.g. "https://example.com/api/{id}.json"), /// or return a unique URI for each `id`. /// /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata function uri(uint256 id) public view virtual returns (string memory); /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONFIGURABLES */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns if direct NFT transfers should be used during ERC20 transfers /// whenever possible, instead of burning and re-minting. function _useDirectTransfersIfPossible() internal view virtual returns (bool) { return true; } /// @dev Hook that is called after a batch of NFT transfers. /// The lengths of `from`, `to`, and `ids` are guaranteed to be the same. function _afterNFTTransfers(address[] memory from, address[] memory to, uint256[] memory ids) internal virtual {} /// @dev Override this function to return true if `_afterNFTTransfers` is used. /// This is to help the compiler avoid producing dead bytecode. function _useAfterNFTTransfers() internal virtual returns (bool) {} /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* ERC20 OPERATIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns the decimals places of the ERC20 token. Always 18. function decimals() public pure returns (uint8) { return 18; } /// @dev Returns the amount of ERC20 tokens in existence. function totalSupply() public view virtual returns (uint256) { return uint256(_getDN420Storage().totalSupply); } /// @dev Returns the amount of ERC20 tokens owned by `owner`. function balanceOf(address owner) public view virtual returns (uint256) { return _getDN420Storage().addressData[owner].balance; } /// @dev Returns the amount of ERC20 tokens that `spender` can spend on behalf of `owner`. function allowance(address owner, address spender) public view returns (uint256) { if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) { uint8 flags = _getDN420Storage().addressData[owner].flags; if ((flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG) == uint256(0)) { return type(uint256).max; } } return _ref(_getDN420Storage().allowance, owner, spender).value; } /// @dev Sets `amount` as the allowance of `spender` over the caller's ERC20 tokens. /// /// Emits an ERC20 {Approval} event. function approve(address spender, uint256 amount) public virtual returns (bool) { _approve(msg.sender, spender, amount); return true; } /// @dev Transfer `amount` ERC20 tokens from the caller to `to`. /// /// Will burn sender's ERC1155 NFTs if balance after transfer is less than /// the amount required to support the current NFT balance. /// /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is /// set to false. /// /// Requirements: /// - `from` must at least have `amount` ERC20 tokens. /// /// Emits an ERC1155 {TransferBatch} event for direct transfers (if any). /// Emits an ERC1155 {TransferBatch} event for mints (if any). /// Emits an ERC1155 {TransferBatch} event for burns (if any). /// Emits an ERC20 {Transfer} event. function transfer(address to, uint256 amount) public virtual returns (bool) { _transfer(msg.sender, to, amount, ""); return true; } /// @dev Transfers `amount` ERC20 tokens from `from` to `to`. /// /// Note: Does not update the ERC20 allowance if it is the maximum uint256 value. /// /// Will burn sender ERC1155 NFTs if balance after transfer is less than /// the amount required to support the current ERC1155 NFT balance. /// /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is /// set to false. /// /// Requirements: /// - `from` must at least have `amount` ERC20 tokens. /// - The caller must have at least `amount` of ERC20 allowance to transfer the tokens of `from`. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { Uint256Ref storage a = _ref(_getDN420Storage().allowance, from, msg.sender); uint256 allowed = _givePermit2DefaultInfiniteAllowance() && msg.sender == _PERMIT2 && (_getDN420Storage().addressData[from].flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG) == uint256(0) ? type(uint256).max : a.value; if (allowed != type(uint256).max) { if (amount > allowed) revert InsufficientAllowance(); unchecked { a.value = allowed - amount; } } _transfer(from, to, amount, ""); return true; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* PERMIT2 */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Whether Permit2 has infinite ERC20 allowances by default for all owners. /// For signature-based allowance granting for single transaction ERC20 `transferFrom`. /// To enable, override this function to return true. function _givePermit2DefaultInfiniteAllowance() internal view virtual returns (bool) { return false; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INTERNAL MINT FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Mints `amount` ERC20 tokens to `to`, increasing the total supply. /// /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is set to false. /// /// Emits an ERC1155 {TransferBatch} event for mints (if any). /// Emits an ERC20 {Transfer} event. function _mint(address to, uint256 amount, bytes memory data) internal virtual { if (to == address(0)) revert TransferToZeroAddress(); DN420Storage storage $ = _getDN420Storage(); if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized(); AddressData storage toAddressData = $.addressData[to]; _DNMintTemps memory t; unchecked { { uint256 toBalance = uint256(toAddressData.balance) + amount; toAddressData.balance = uint96(toBalance); t.toEnd = toBalance / _unit(); } uint256 maxId; { uint256 totalSupply_ = uint256($.totalSupply) + amount; $.totalSupply = uint96(totalSupply_); uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_)); if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow(); maxId = totalSupply_ / _unit(); $.tokenIdUpTo = uint32(_max($.tokenIdUpTo, maxId)); } if (!getSkipNFT(to)) { t.mintIds = _idsMalloc(_zeroFloorSub(t.toEnd, toAddressData.ownedCount)); if (t.mintIds.length != 0) { Bitmap storage toOwned = $.owned[to]; uint256 ownedCheckpoint = toAddressData.ownedCheckpoint; uint256 id = _wrapNFTId($.nextTokenId, maxId); // Mint loop. for (uint256 n = t.mintIds.length;;) { while (_get($.exists, id)) { id = _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId); } _set($.exists, id, true); _set(toOwned, id, true); ownedCheckpoint = _max(ownedCheckpoint, id); _idsAppend(t.mintIds, id); id = _wrapNFTId(id + 1, maxId); if (--n == uint256(0)) break; } toAddressData.ownedCheckpoint = uint32(ownedCheckpoint); toAddressData.ownedCount = uint32(t.toEnd); $.nextTokenId = uint32(id); _batchTransferEmit(address(0), to, t.mintIds); } } } /// @solidity memory-safe-assembly assembly { // Emit the ERC20 {Transfer} event. mstore(0x00, amount) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to))) } if (_useAfterNFTTransfers()) { _afterNFTTransfers( _zeroAddresses(t.mintIds.length), _filled(t.mintIds.length, to), t.mintIds ); } if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, t.mintIds, data); } /// @dev Mints `amount` tokens to `to`, increasing the total supply. /// This variant mints NFT tokens starting from ID `preTotalSupply / _unit() + 1`. /// The `nextTokenId` will not be changed. /// /// Will mint NFTs to `to` if the recipient's new balance supports /// additional NFTs ***AND*** the `to` address's skipNFT flag is set to false. /// /// Note: /// - May mint more NFTs than `amount / _unit()`. /// The number of NFTs minted is what is needed to make `to`'s NFT balance whole. /// - Token IDs may wrap around `totalSupply / _unit()` back to 1. /// /// Emits an ERC1155 {TransferBatch} event for mints (if any). /// Emits an ERC20 {Transfer} event. function _mintNext(address to, uint256 amount, bytes memory data) internal virtual { if (to == address(0)) revert TransferToZeroAddress(); DN420Storage storage $ = _getDN420Storage(); if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized(); AddressData storage toAddressData = $.addressData[to]; _DNMintTemps memory t; unchecked { { uint256 toBalance = uint256(toAddressData.balance) + amount; toAddressData.balance = uint96(toBalance); t.toEnd = toBalance / _unit(); } uint256 id; uint256 maxId; { uint256 preTotalSupply = uint256($.totalSupply); uint256 newTotalSupply = uint256(preTotalSupply) + amount; $.totalSupply = uint96(newTotalSupply); uint256 overflows = _toUint(_totalSupplyOverflows(newTotalSupply)); if (overflows | _toUint(newTotalSupply < amount) != 0) revert TotalSupplyOverflow(); maxId = newTotalSupply / _unit(); id = _wrapNFTId(preTotalSupply / _unit() + 1, maxId); $.tokenIdUpTo = uint32(_max($.tokenIdUpTo, maxId)); } if (!getSkipNFT(to)) { t.mintIds = _idsMalloc(_zeroFloorSub(t.toEnd, toAddressData.ownedCount)); if (t.mintIds.length != 0) { Bitmap storage toOwned = $.owned[to]; uint256 ownedCheckpoint = toAddressData.ownedCheckpoint; // Mint loop. for (uint256 n = t.mintIds.length;;) { while (_get($.exists, id)) { id = _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId); } _set($.exists, id, true); _set(toOwned, id, true); ownedCheckpoint = _max(ownedCheckpoint, id); _idsAppend(t.mintIds, id); id = _wrapNFTId(id + 1, maxId); if (--n == uint256(0)) break; } toAddressData.ownedCheckpoint = uint32(ownedCheckpoint); toAddressData.ownedCount = uint32(t.toEnd); _batchTransferEmit(address(0), to, t.mintIds); } } } /// @solidity memory-safe-assembly assembly { // Emit the ERC20 {Transfer} event. mstore(0x00, amount) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to))) } if (_useAfterNFTTransfers()) { _afterNFTTransfers( _zeroAddresses(t.mintIds.length), _filled(t.mintIds.length, to), t.mintIds ); } if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, t.mintIds, data); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INTERNAL BURN FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Burns `amount` ERC20 tokens from `from`, reducing the total supply. /// /// Will burn sender's ERC1155 NFTs if balance after transfer is less than /// the amount required to support the current ERC1155 NFT balance. /// /// Emits an ERC1155 {TransferBatch} event for burns (if any). /// Emits an ERC20 {Transfer} event. function _burn(address from, uint256 amount) internal virtual { DN420Storage storage $ = _getDN420Storage(); if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized(); AddressData storage fromAddressData = $.addressData[from]; uint256[] memory ids; unchecked { uint256 fromBalance = fromAddressData.balance; if (amount > fromBalance) revert InsufficientBalance(); fromAddressData.balance = uint96(fromBalance -= amount); $.totalSupply -= uint96(amount); Bitmap storage fromOwned = $.owned[from]; uint256 fromIndex = fromAddressData.ownedCount; uint256 numNFTBurns = _zeroFloorSub(fromIndex, fromBalance / _unit()); if (numNFTBurns != 0) { ids = _idsMalloc(numNFTBurns); fromAddressData.ownedCount = uint32(fromIndex - numNFTBurns); uint256 id = fromAddressData.ownedCheckpoint; // Burn loop. while (true) { id = _findLastSet(fromOwned, id); if (id == uint256(0)) id = _findLastSet(fromOwned, $.tokenIdUpTo); _set(fromOwned, id, false); _set($.exists, id, false); _idsAppend(ids, id); if (--numNFTBurns == uint256(0)) break; } fromAddressData.ownedCheckpoint = uint32(id); _batchTransferEmit(from, address(0), ids); } } /// @solidity memory-safe-assembly assembly { // Emit the ERC20 {Transfer} event. mstore(0x00, amount) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0) } if (_useAfterNFTTransfers()) { _afterNFTTransfers(_filled(ids.length, from), _zeroAddresses(ids.length), ids); } } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INTERNAL TRANSFER FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Moves `amount` of ERC20 tokens from `from` to `to`. /// /// Will burn sender ERC1155 NFTs if balance after transfer is less than /// the amount required to support the current ERC1155 NFT balance. /// /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is /// set to false. ///. /// Emits an ERC1155 {TransferBatch} event for direct transfers (if any). /// Emits an ERC1155 {TransferBatch} event for mints (if any). /// Emits an ERC1155 {TransferBatch} event for burns (if any). /// Emits an ERC20 {Transfer} event function _transfer(address from, address to, uint256 amount, bytes memory data) internal virtual { if (to == address(0)) revert TransferToZeroAddress(); DN420Storage storage $ = _getDN420Storage(); if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized(); AddressData storage fromAddressData = $.addressData[from]; AddressData storage toAddressData = $.addressData[to]; _DNTransferTemps memory t; t.fromOwnedCount = fromAddressData.ownedCount; t.toOwnedCount = toAddressData.ownedCount; unchecked { uint256 toBalance; uint256 fromBalance = fromAddressData.balance; if (amount > fromBalance) revert InsufficientBalance(); fromAddressData.balance = uint96(fromBalance -= amount); toAddressData.balance = uint96(toBalance = uint256(toAddressData.balance) + amount); t.numNFTBurns = _zeroFloorSub(t.fromOwnedCount, fromBalance / _unit()); if (!getSkipNFT(to)) { if (from == to) t.toOwnedCount = t.fromOwnedCount - t.numNFTBurns; t.numNFTMints = _zeroFloorSub(toBalance / _unit(), t.toOwnedCount); } } unchecked { while (_useDirectTransfersIfPossible()) { uint256 n = _min(t.fromOwnedCount, _min(t.numNFTBurns, t.numNFTMints)); if (n == uint256(0)) break; t.numNFTBurns -= n; t.numNFTMints -= n; if (from == to) { t.toOwnedCount += n; break; } t.directIds = _idsMalloc(n); Bitmap storage fromOwned = $.owned[from]; Bitmap storage toOwned = $.owned[to]; uint256 id = fromAddressData.ownedCheckpoint; fromAddressData.ownedCount = uint32(t.fromOwnedCount -= n); toAddressData.ownedCheckpoint = uint32(_max(toAddressData.ownedCheckpoint, id)); toAddressData.ownedCount = uint32(t.toOwnedCount += n); // Direct transfer loop. while (true) { id = _findLastSet(fromOwned, id); if (id == uint256(0)) id = _findLastSet(fromOwned, $.tokenIdUpTo); _set(fromOwned, id, false); _set(toOwned, id, true); _idsAppend(t.directIds, id); if (--n == uint256(0)) break; } fromAddressData.ownedCheckpoint = uint32(id); _batchTransferEmit(from, to, t.directIds); break; } if (t.numNFTBurns != 0) { uint256 n = t.numNFTBurns; t.burnIds = _idsMalloc(n); Bitmap storage fromOwned = $.owned[from]; fromAddressData.ownedCount = uint32(t.fromOwnedCount - n); uint256 id = fromAddressData.ownedCheckpoint; // Burn loop. while (true) { id = _findLastSet(fromOwned, id); if (id == uint256(0)) id = _findLastSet(fromOwned, $.tokenIdUpTo); _set(fromOwned, id, false); _set($.exists, id, false); _idsAppend(t.burnIds, id); if (--n == uint256(0)) break; } fromAddressData.ownedCheckpoint = uint32(id); _batchTransferEmit(from, address(0), t.burnIds); } if (t.numNFTMints != 0) { uint256 n = t.numNFTMints; t.mintIds = _idsMalloc(n); Bitmap storage toOwned = $.owned[to]; toAddressData.ownedCount = uint32(t.toOwnedCount + n); uint256 maxId = $.totalSupply / _unit(); uint256 id = _wrapNFTId($.nextTokenId, maxId); uint256 ownedCheckpoint = toAddressData.ownedCheckpoint; // Mint loop. while (true) { while (_get($.exists, id)) { id = _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId); } _set($.exists, id, true); _set(toOwned, id, true); ownedCheckpoint = _max(ownedCheckpoint, id); _idsAppend(t.mintIds, id); id = _wrapNFTId(id + 1, maxId); if (--n == uint256(0)) break; } toAddressData.ownedCheckpoint = uint32(ownedCheckpoint); $.nextTokenId = uint32(id); _batchTransferEmit(address(0), to, t.mintIds); } } /// @solidity memory-safe-assembly assembly { // Emit the ERC20 {Transfer} event. mstore(0x00, amount) // forgefmt: disable-next-item log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to))) } if (_useAfterNFTTransfers()) { uint256[] memory ids = t.directIds; unchecked { _afterNFTTransfers( _concat( _filled(ids.length + t.numNFTBurns, from), _zeroAddresses(t.numNFTMints) ), _concat( _concat(_filled(ids.length, to), _zeroAddresses(t.numNFTBurns)), _filled(t.numNFTMints, to) ), _concat(_concat(ids, t.burnIds), t.mintIds) ); } } if (_hasCode(to)) { _checkOnERC1155BatchReceived(from, to, t.directIds, data); _checkOnERC1155BatchReceived(address(0), to, t.mintIds, data); } } /// @dev Transfers ERC1155 `id` from `from` to `to`. /// /// Requirements: /// - `to` cannot be the zero address. /// - `from` must have `id`. /// - If `by` is not the zero address, it must be either `from`, /// or approved to manage the ERC1155 tokens of `from`. /// - If `to` refers to a smart contract, it must implement /// {ERC1155-onERC1155Reveived}, which is called upon a batch transfer. /// /// Emits an ERC1155 {TransferSingle} event. /// Emits an ERC20 {Transfer} event. function _safeTransferNFT(address by, address from, address to, uint256 id, bytes memory data) internal virtual { if (to == address(0)) revert TransferToZeroAddress(); DN420Storage storage $ = _getDN420Storage(); if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized(); if (_toUint(by == address(0)) | _toUint(by == from) == uint256(0)) { if (!isApprovedForAll(from, by)) revert NotOwnerNorApproved(); } Bitmap storage fromOwned = $.owned[from]; if (!_owns(fromOwned, id)) revert TransferFromIncorrectOwner(); _set(fromOwned, id, false); _set($.owned[to], id, true); uint256 unit = _unit(); AddressData storage fromAddressData = $.addressData[from]; AddressData storage toAddressData = $.addressData[to]; /// @solidity memory-safe-assembly assembly { let diff := shl(128, or(shl(32, unit), 1)) sstore(fromAddressData.slot, sub(sload(fromAddressData.slot), diff)) let toPacked := sload(toAddressData.slot) let toCheckpoint := and(0xffffffff, shr(96, toPacked)) // forgefmt: disable-next-item sstore(toAddressData.slot, add(diff, xor(toPacked, shl(96, mul(gt(id, toCheckpoint), xor(id, toCheckpoint)))))) } /// @solidity memory-safe-assembly assembly { from := shr(96, shl(96, from)) to := shr(96, shl(96, to)) // Emit the ERC1155 {TransferSingle} event. mstore(0x00, id) mstore(0x20, 1) log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to) // Emit the ERC20 {Transfer} event. mstore(0x00, unit) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, from, to) } if (_useAfterNFTTransfers()) { _afterNFTTransfers(_filled(1, from), _filled(1, to), _filled(1, id)); } if (_hasCode(to)) _checkOnERC1155Received(from, to, id, data); } /// @dev Transfers `id` from `from` to `to`. /// /// Requirements: /// - `to` cannot be the zero address. /// - `from` must have `ids`. /// - If `by` is not the zero address, it must be either `from`, /// or approved to manage the ERC1155 tokens of `from`. /// - If `to` refers to a smart contract, it must implement /// {ERC1155-onERC1155Reveived}, which is called upon a batch transfer. /// /// Emits an ERC1155 {TransferBatch} event. /// Emits an ERC20 {Transfer} event. function _safeBatchTransferNFTs( address by, address from, address to, uint256[] memory ids, bytes memory data ) internal virtual { if (to == address(0)) revert TransferToZeroAddress(); DN420Storage storage $ = _getDN420Storage(); if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized(); if (_toUint(by == address(0)) | _toUint(by == from) == uint256(0)) { if (!isApprovedForAll(from, by)) revert NotOwnerNorApproved(); } uint256 amount; uint256 upTo; AddressData storage fromAddressData = $.addressData[from]; AddressData storage toAddressData = $.addressData[to]; unchecked { uint256 n = ids.length; amount = n * _unit(); Bitmap storage fromOwned = $.owned[from]; Bitmap storage toOwned = $.owned[to]; while (n != 0) { uint256 id = _get(ids, --n); if (!_owns(fromOwned, id)) revert TransferFromIncorrectOwner(); _set(fromOwned, id, false); _set(toOwned, id, true); upTo = _max(upTo, id); } } /// @solidity memory-safe-assembly assembly { let diff := shl(128, or(shl(32, amount), mload(ids))) sstore(fromAddressData.slot, sub(sload(fromAddressData.slot), diff)) let toPacked := sload(toAddressData.slot) let toCheckpoint := and(0xffffffff, shr(96, toPacked)) // forgefmt: disable-next-item sstore(toAddressData.slot, add(diff, xor(toPacked, shl(96, mul(gt(upTo, toCheckpoint), xor(upTo, toCheckpoint)))))) } _batchTransferEmit(from, to, ids); /// @solidity memory-safe-assembly assembly { // Emit the ERC20 {Transfer} event. mstore(0x00, amount) // forgefmt: disable-next-item log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to))) } if (_useAfterNFTTransfers()) { _afterNFTTransfers(_filled(ids.length, from), _filled(ids.length, to), ids); } if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, data); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INTERNAL APPROVE FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`. /// /// Emits a {Approval} event. function _approve(address owner, address spender, uint256 amount) internal virtual { if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) { _getDN420Storage().addressData[owner].flags |= _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG; } _ref(_getDN420Storage().allowance, owner, spender).value = amount; /// @solidity memory-safe-assembly assembly { // Emit the {Approval} event. mstore(0x00, amount) // forgefmt: disable-next-item log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, shl(96, owner)), shr(96, shl(96, spender))) } } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* DATA HITCHHIKING FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns the auxiliary data for `owner`. /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. /// Auxiliary data can be set for any address, even if it does not have any tokens. function _getAux(address owner) internal view virtual returns (uint88) { return _getDN420Storage().addressData[owner].aux; } /// @dev Set the auxiliary data for `owner` to `value`. /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. /// Auxiliary data can be set for any address, even if it does not have any tokens. function _setAux(address owner, uint88 value) internal virtual { _getDN420Storage().addressData[owner].aux = value; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* SKIP NFT FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns true if minting and transferring ERC20s to `owner` will skip minting NFTs. /// Returns false otherwise. function getSkipNFT(address owner) public view virtual returns (bool result) { uint8 flags = _getDN420Storage().addressData[owner].flags; /// @solidity memory-safe-assembly assembly { result := iszero(iszero(and(flags, _ADDRESS_DATA_SKIP_NFT_FLAG))) if iszero(and(flags, _ADDRESS_DATA_SKIP_NFT_INITIALIZED_FLAG)) { result := iszero(iszero(extcodesize(owner))) } } } /// @dev Sets the caller's skipNFT flag to `skipNFT`. Returns true. /// /// Emits a {SkipNFTSet} event. function setSkipNFT(bool skipNFT) public virtual returns (bool) { _setSkipNFT(msg.sender, skipNFT); return true; } /// @dev Internal function to set account `owner` skipNFT flag to `state` /// /// Initializes account `owner` AddressData if it is not currently initialized. /// /// Emits a {SkipNFTSet} event. function _setSkipNFT(address owner, bool state) internal virtual { AddressData storage d = _getDN420Storage().addressData[owner]; uint8 flags = d.flags; /// @solidity memory-safe-assembly assembly { let s := xor(iszero(and(flags, _ADDRESS_DATA_SKIP_NFT_FLAG)), iszero(state)) flags := xor(mul(_ADDRESS_DATA_SKIP_NFT_FLAG, s), flags) flags := or(_ADDRESS_DATA_SKIP_NFT_INITIALIZED_FLAG, flags) mstore(0x00, iszero(iszero(state))) log2(0x00, 0x20, _SKIP_NFT_SET_EVENT_SIGNATURE, shr(96, shl(96, owner))) } d.flags = flags; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* OWNED CHECKPOINT FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns the owned checkpoint of `owner`. function getOwnedCheckpoint(address owner) public view virtual returns (uint256) { return _getDN420Storage().addressData[owner].ownedCheckpoint; } /// @dev Just in case the collection gets too large and the caller needs /// to set their owned checkpoint manually to skip large bitmap scans /// for automatic ERC1155 NFT burns upon ERC20 transfers. function setOwnedCheckpoint(uint256 id) public virtual { _setOwnedCheckpoint(msg.sender, id); } /// @dev Sets the owned checkpoint of `owner` to `id`. /// `id` will be clamped to `[1..tokenIdUpTo]`. function _setOwnedCheckpoint(address owner, uint256 id) internal virtual { DN420Storage storage $ = _getDN420Storage(); id = _min(_max(1, id), $.tokenIdUpTo); $.addressData[owner].ownedCheckpoint = uint32(id); emit OwnedCheckpointSet(owner, id); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* ERC1155 OPERATIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns if `owner` owns ERC1155 `id`. function owns(address owner, uint256 id) public view virtual returns (bool) { return _owns(_getDN420Storage().owned[owner], id); } /// @dev Returns if the ERC1155 `id` is set in `owned`. function _owns(Bitmap storage owned, uint256 id) internal view virtual returns (bool) { return _get(owned, _restrictNFTId(id)); } /// @dev Returns whether `operator` is approved to manage the ERC1155 tokens of `owner`. function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { return _ref(_getDN420Storage().operatorApprovals, owner, operator).value != 0; } /// @dev Sets whether `operator` is approved to manage the ERC1155 tokens of the caller. /// /// Emits a {ApprovalForAll} event. function setApprovalForAll(address operator, bool isApproved) public virtual { _setApprovalForAll(msg.sender, operator, isApproved); } /// @dev Sets whether `operator` is approved to manage the ERC1155 tokens of the caller. /// /// Emits a {ApprovalForAll} event. function _setApprovalForAll(address owner, address operator, bool isApproved) internal virtual { _ref(_getDN420Storage().operatorApprovals, owner, operator).value = _toUint(isApproved); /// @solidity memory-safe-assembly assembly { // Emit the {ApprovalForAll} event. mstore(0x00, isApproved) // forgefmt: disable-next-item log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, shr(96, shl(96, owner)), shr(96, shl(96, operator))) } } /// @dev Transfers the ERC1155 NFT at `id` from `from` to `to`. function safeTransferNFT(address from, address to, uint256 id, bytes memory data) public virtual { _safeTransferNFT(msg.sender, from, to, id, data); } /// @dev Transfers the ERC1155 NFTs at `ids` from `from` to `to`. function safeBatchTransferNFTs( address from, address to, uint256[] memory ids, bytes memory data ) public virtual { _safeBatchTransferNFTs(msg.sender, from, to, ids, data); } /// @dev Returns true if this contract implements the interface defined by `interfaceId`. /// See: https://eips.ethereum.org/EIPS/eip-165 /// This function call must use less than 30000 gas. function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { let s := shr(224, interfaceId) // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c. result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c)) } } /// @dev Returns `owner`'s ERC1155 NFT balance. function _balanceOfNFT(address owner) internal view virtual returns (uint256) { return _getDN420Storage().addressData[owner].ownedCount; } /// @dev Returns if the ERC1155 token `id` exists. function _exists(uint256 id) internal view virtual returns (bool) { return _get(_getDN420Storage().exists, _restrictNFTId(id)); } /// @dev Returns the ERC1155 NFT IDs of `owner` in range `[lower, upper)`. /// Optimized for smaller bytecode size, as this function is intended for off-chain calling. function _findOwnedIds(address owner, uint256 lower, uint256 upper) internal view virtual returns (uint256[] memory ids) { unchecked { DN420Storage storage $ = _getDN420Storage(); Bitmap storage owned = $.owned[owner]; upper = _min(uint256($.tokenIdUpTo) + 1, upper); /// @solidity memory-safe-assembly assembly { ids := mload(0x40) let n := 0 let s := shl(96, owned.slot) for { let id := lower } lt(id, upper) { id := add(1, id) } { if and(1, shr(and(0xff, id), sload(add(s, shr(8, id))))) { mstore(add(add(ids, 0x20), shl(5, n)), id) n := add(1, n) } } mstore(ids, n) mstore(0x40, add(shl(5, n), add(0x20, ids))) } } } /// @dev Fallback modifier for the regular ERC1155 functions and other functions. modifier dn420Fallback() virtual { uint256 fnSelector = _calldataload(0x00) >> 224; // We hide the regular ERC1155 functions that has variable amounts // in the fallback for ABI aesthetic purposes. // `safeTransferFrom(address,address,uint256,uint256,bytes)`. if (fnSelector == 0xf242432a) { if (_calldataload(0x64) != 1) revert InvalidNFTAmount(); _safeTransferNFT( msg.sender, // `by`. address(uint160(_calldataload(0x04))), // `from`. address(uint160(_calldataload(0x24))), // `to`. _calldataload(0x44), // `id`. _calldataBytes(0x84) // `data`. ); _return(1); } // `safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)`. if (fnSelector == 0x2eb2c2d6) { uint256[] memory ids = _calldataUint256Array(0x44); unchecked { uint256[] memory amounts = _calldataUint256Array(0x64); uint256 n = ids.length; if (n != amounts.length) revert ArrayLengthsMismatch(); while (n-- != 0) if (_get(amounts, n) != 1) revert InvalidNFTAmount(); } _safeBatchTransferNFTs( msg.sender, address(uint160(_calldataload(0x04))), // `from`. address(uint160(_calldataload(0x24))), // `to`. ids, _calldataBytes(0x84) // `data. ); _return(1); } // `balanceOfBatch(address[],uint256[])`. if (fnSelector == 0x4e1273f4) { uint256[] memory owners = _calldataUint256Array(0x04); uint256[] memory ids = _calldataUint256Array(0x24); unchecked { uint256 n = ids.length; if (owners.length != n) revert ArrayLengthsMismatch(); uint256[] memory result = _idsMalloc(n); while (n-- != 0) { address owner = address(uint160(_get(owners, n))); _set(result, n, _toUint(owns(owner, _get(ids, n)))); } /// @solidity memory-safe-assembly assembly { mstore(sub(result, 0x20), 0x20) return(sub(result, 0x20), add(0x40, shl(5, mload(result)))) } } } // `balanceOf(address,uint256)`. if (fnSelector == 0x00fdd58e) { bool result = owns( address(uint160(_calldataload(0x04))), // `owner`. _calldataload(0x24) // `id`. ); _return(_toUint(result)); } // `implementsDN420()`. if (fnSelector == 0x0e0b0984) { _return(1); } _; } /// @dev Fallback function for regular ERC1155 functions and other functions. /// Override this if you need to implement your custom /// fallback with utilities like Solady's `LibZip.cdFallback()`. /// And always remember to always wrap the fallback with `dn420Fallback`. fallback() external payable virtual dn420Fallback { revert FnSelectorNotRecognized(); // Not mandatory. Just for quality of life. } /// @dev This is to silence the compiler warning. /// Override and remove the revert if you want your contract to receive ETH via receive. receive() external payable virtual { if (msg.value != 0) revert(); } /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`. /// Reverts if the target does not support the function correctly. function _checkOnERC1155Received(address from, address to, uint256 id, bytes memory data) private { /// @solidity memory-safe-assembly assembly { // Prepare the calldata. let m := mload(0x40) // `onERC1155Received(address,address,uint256,uint256,bytes)`. mstore(m, 0xf23a6e61) mstore(add(m, 0x20), caller()) mstore(add(m, 0x40), shr(96, shl(96, from))) mstore(add(m, 0x60), id) mstore(add(m, 0x80), 1) mstore(add(m, 0xa0), 0xa0) let n := mload(data) mstore(add(m, 0xc0), n) if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xe0), n)) } // Revert if the call reverts. if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) { if returndatasize() { // Bubble up the revert if the call reverts. returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } } // Load the returndata and compare it with the function selector. if iszero(eq(mload(m), shl(224, 0xf23a6e61))) { mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`. revert(0x1c, 0x04) } } } /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`. /// Reverts if the target does not support the function correctly. function _checkOnERC1155BatchReceived( address from, address to, uint256[] memory ids, bytes memory data ) private { if (ids.length == uint256(0)) return; /// @solidity memory-safe-assembly assembly { // Prepare the calldata. let m := mload(0x40) // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. mstore(m, 0xbc197c81) mstore(add(m, 0x20), caller()) mstore(add(m, 0x40), shr(96, shl(96, from))) // Copy the `ids`. mstore(add(m, 0x60), 0xa0) let o := add(m, 0xc0) { let n := add(0x20, shl(5, mload(ids))) pop(staticcall(gas(), 4, ids, n, o, n)) } // Copy the `amounts`. mstore(add(m, 0x80), add(0xa0, returndatasize())) mstore(add(m, 0xa0), add(returndatasize(), add(0xa0, returndatasize()))) o := add(o, returndatasize()) mstore(o, mload(ids)) let end := add(o, returndatasize()) for { o := add(o, 0x20) } iszero(eq(o, end)) { o := add(0x20, o) } { mstore(o, 1) } // Copy the `data`. { let n := add(0x20, mload(data)) pop(staticcall(gas(), 4, data, n, end, n)) } // Revert if the call reverts. // forgefmt: disable-next-item if iszero(call(gas(), to, 0, add(m, 0x1c), sub(add(end, returndatasize()), add(m, 0x1c)), m, 0x20)) { if returndatasize() { // Bubble up the revert if the call reverts. returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } } // Load the returndata and compare it with the function selector. if iszero(eq(mload(m), shl(224, 0xbc197c81))) { mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`. revert(0x1c, 0x04) } } } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INTERNAL / PRIVATE HELPERS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns the boolean value of the bit at `index` in `bitmap`. function _get(Bitmap storage bitmap, uint256 index) internal view returns (bool result) { /// @solidity memory-safe-assembly assembly { let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot. result := and(1, shr(and(0xff, index), sload(s))) } } /// @dev Updates the bit at `index` in `bitmap` to `value`. function _set(Bitmap storage bitmap, uint256 index, bool value) internal { /// @solidity memory-safe-assembly assembly { let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot. let o := and(0xff, index) // Storage slot offset (bits). sstore(s, or(and(sload(s), not(shl(o, 1))), shl(o, iszero(iszero(value))))) } } /// @dev Returns the index of the least significant unset bit in `[begin..upTo]`. /// If no unset bit is found, returns `type(uint256).max`. function _findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo) internal view returns (uint256 unsetBitIndex) { /// @solidity memory-safe-assembly assembly { unsetBitIndex := not(0) // Initialize to `type(uint256).max`. let s := shl(96, bitmap.slot) // Storage offset of the bitmap. let bucket := add(s, shr(8, begin)) let negBits := shl(and(0xff, begin), shr(and(0xff, begin), not(sload(bucket)))) if iszero(negBits) { let lastBucket := add(s, shr(8, upTo)) for {} 1 {} { bucket := add(bucket, 1) negBits := not(sload(bucket)) if or(negBits, gt(bucket, lastBucket)) { break } } if gt(bucket, lastBucket) { negBits := shr(and(0xff, not(upTo)), shl(and(0xff, not(upTo)), negBits)) } } if negBits { // Find-first-set routine. // From: https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol let b := and(negBits, add(not(negBits), 1)) // Isolate the least significant bit. // For the upper 3 bits of the result, use a De Bruijn-like lookup. // Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/ // forgefmt: disable-next-item let r := shl(5, shr(252, shl(shl(2, shr(250, mul(b, 0x2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff))), 0x1412563212c14164235266736f7425221143267a45243675267677))) // For the lower 5 bits of the result, use a De Bruijn lookup. // forgefmt: disable-next-item r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f), 0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405)) r := or(shl(8, sub(bucket, s)), r) unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin)))) } } } /// @dev Returns the index of the most significant set bit in `[0..upTo]`. /// If no set bit is found, returns zero. function _findLastSet(Bitmap storage bitmap, uint256 upTo) internal view returns (uint256 setBitIndex) { /// @solidity memory-safe-assembly assembly { let s := shl(96, bitmap.slot) // Storage offset of the bitmap. let bucket := add(s, shr(8, upTo)) let bits := shr(and(0xff, not(upTo)), shl(and(0xff, not(upTo)), sload(bucket))) if iszero(or(bits, eq(bucket, s))) { for {} 1 {} { bucket := sub(bucket, 1) mstore(0x00, bucket) bits := sload(bucket) if or(bits, eq(bucket, s)) { break } } } if bits { // Find-last-set routine. let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, bits)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, bits)))) r := or(r, shl(5, lt(0xffffffff, shr(r, bits)))) r := or(r, shl(4, lt(0xffff, shr(r, bits)))) r := or(r, shl(3, lt(0xff, shr(r, bits)))) // forgefmt: disable-next-item r := or(r, byte(and(0x1f, shr(shr(r, bits), 0x8421084210842108cc6318c6db6d54be)), 0x0706060506020504060203020504030106050205030304010505030400000000)) r := or(shl(8, sub(bucket, s)), r) setBitIndex := mul(r, iszero(gt(r, upTo))) } } } /// @dev Returns a storage reference to the value at (`a0`, `a1`) in `map`. function _ref(AddressPairToUint256RefMap storage map, address a0, address a1) internal pure returns (Uint256Ref storage ref) { /// @solidity memory-safe-assembly assembly { mstore(0x28, a1) mstore(0x14, a0) mstore(0x00, map.slot) ref.slot := keccak256(0x00, 0x48) // Clear the part of the free memory pointer that was overwritten. mstore(0x28, 0x00) } } /// @dev Wraps the NFT ID. function _wrapNFTId(uint256 id, uint256 maxId) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := or(mul(iszero(gt(id, maxId)), id), gt(id, maxId)) } } /// @dev Returns `id > type(uint32).max ? 0 : id`. function _restrictNFTId(uint256 id) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := mul(id, lt(id, 0x100000000)) } } /// @dev Returns whether `amount` is a valid `totalSupply`. function _totalSupplyOverflows(uint256 amount) internal view returns (bool result) { uint256 unit = _unit(); /// @solidity memory-safe-assembly assembly { result := iszero(iszero(or(shr(96, amount), lt(0xfffffffe, div(amount, unit))))) } } /// @dev Returns `max(0, x - y)`. function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(gt(x, y), sub(x, y)) } } /// @dev Returns `x < y ? x : y`. function _min(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), lt(y, x))) } } /// @dev Returns `x > y ? x : y`. function _max(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), gt(y, x))) } } /// @dev Returns `b ? 1 : 0`. function _toUint(bool b) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := iszero(iszero(b)) } } /// @dev Creates an array with length `n` that is suitable for `_idsAppend`. function _idsMalloc(uint256 n) private pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { result := add(0x20, mload(0x40)) let offset := add(result, 0x20) mstore(sub(result, 0x20), offset) mstore(result, n) mstore(0x40, add(offset, shl(5, n))) } } /// @dev Appends `id` to `a`. `a` must be created via `_idsMalloc`. function _idsAppend(uint256[] memory a, uint256 id) private pure { /// @solidity memory-safe-assembly assembly { let offset := mload(sub(a, 0x20)) mstore(offset, id) mstore(sub(a, 0x20), add(offset, 0x20)) } } /// @dev Emits the ERC1155 {TransferBatch} event with `from`, `to`, and `ids`. function _batchTransferEmit(address from, address to, uint256[] memory ids) private { if (ids.length == uint256(0)) return; /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(m, 0x40) let o := add(m, 0x40) // We have to copy the `ids`, as it might not be from `_idsMalloc`. // See: `_safeBatchTransferNFTs`. { let n := add(0x20, shl(5, mload(ids))) pop(staticcall(gas(), 4, ids, n, o, n)) } mstore(add(m, 0x20), add(0x40, returndatasize())) o := add(o, returndatasize()) // Store the length of `amounts`. mstore(o, mload(ids)) let end := add(o, returndatasize()) for { o := add(o, 0x20) } iszero(eq(o, end)) { o := add(0x20, o) } { mstore(o, 1) } // Emit a {TransferBatch} event. // forgefmt: disable-next-item log4(m, sub(o, m), _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, shl(96, from)), shr(96, shl(96, to))) } } /// @dev Returns an array of zero addresses. function _zeroAddresses(uint256 n) private pure returns (address[] memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) mstore(0x40, add(add(result, 0x20), shl(5, n))) mstore(result, n) codecopy(add(result, 0x20), codesize(), shl(5, n)) } } /// @dev Returns an array each set to `value`. function _filled(uint256 n, uint256 value) private pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let o := add(result, 0x20) let end := add(o, shl(5, n)) mstore(0x40, end) mstore(result, n) for {} iszero(eq(o, end)) { o := add(o, 0x20) } { mstore(o, value) } } } /// @dev Returns an array each set to `value`. function _filled(uint256 n, address value) private pure returns (address[] memory result) { result = _toAddresses(_filled(n, uint160(value))); } /// @dev Concatenates the arrays. function _concat(uint256[] memory a, uint256[] memory b) private view returns (uint256[] memory result) { uint256 aN = a.length; uint256 bN = b.length; if (aN == uint256(0)) return b; if (bN == uint256(0)) return a; /// @solidity memory-safe-assembly assembly { let n := add(aN, bN) if n { result := mload(0x40) mstore(result, n) let o := add(result, 0x20) mstore(0x40, add(o, shl(5, n))) let aL := shl(5, aN) pop(staticcall(gas(), 4, add(a, 0x20), aL, o, aL)) pop(staticcall(gas(), 4, add(b, 0x20), shl(5, bN), add(o, aL), shl(5, bN))) } } } /// @dev Concatenates the arrays. function _concat(address[] memory a, address[] memory b) private view returns (address[] memory result) { result = _toAddresses(_concat(_toUints(a), _toUints(b))); } /// @dev Reinterpret cast to an uint array. function _toUints(address[] memory a) private pure returns (uint256[] memory casted) { /// @solidity memory-safe-assembly assembly { casted := a } } /// @dev Reinterpret cast to an address array. function _toAddresses(uint256[] memory a) private pure returns (address[] memory casted) { /// @solidity memory-safe-assembly assembly { casted := a } } /// @dev Struct of temporary variables for mints. struct _DNMintTemps { uint256 toEnd; uint256[] mintIds; } /// @dev Struct of temporary variables for transfers. struct _DNTransferTemps { uint256 numNFTBurns; uint256 numNFTMints; uint256 fromOwnedCount; uint256 toOwnedCount; uint256 ownedCheckpoint; uint256[] directIds; uint256[] burnIds; uint256[] mintIds; } /// @dev Returns if `a` has bytecode of non-zero length. function _hasCode(address a) private view returns (bool result) { /// @solidity memory-safe-assembly assembly { result := extcodesize(a) // Can handle dirty upper bits. } } /// @dev Returns a `uint256[] calldata` at `offset` in calldata as `uint256[] memory`. function _calldataUint256Array(uint256 offset) private pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let o := add(0x04, calldataload(offset)) let n := calldataload(o) mstore(result, n) calldatacopy(add(0x20, result), add(o, 0x20), shl(5, n)) mstore(0x40, add(add(0x20, result), shl(5, n))) } } /// @dev Returns a `bytes calldata` at `offset` in calldata as `bytes memory`. function _calldataBytes(uint256 offset) private pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let o := add(0x04, calldataload(offset)) let n := calldataload(o) mstore(result, n) calldatacopy(add(0x20, result), add(o, 0x20), n) o := add(add(0x20, result), n) mstore(o, 0) // Zeroize the slot after the last word. mstore(0x40, add(0x20, o)) } } /// @dev Returns `a[i]` without bounds check. function _get(uint256[] memory a, uint256 i) private pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := mload(add(add(0x20, a), shl(5, i))) } } /// @dev Sets `a[i]` to `value`, without bounds check. function _set(uint256[] memory a, uint256 i, uint256 value) private pure { /// @solidity memory-safe-assembly assembly { mstore(add(add(0x20, a), shl(5, i)), value) } } /// @dev Returns the calldata value at `offset`. function _calldataload(uint256 offset) private pure returns (uint256 value) { /// @solidity memory-safe-assembly assembly { value := calldataload(offset) } } /// @dev Executes a return opcode to return `x` and end the current call frame. function _return(uint256 x) private pure { /// @solidity memory-safe-assembly assembly { mstore(0x00, x) return(0x00, 0x20) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Gas optimized ECDSA wrapper. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) /// /// @dev Note: /// - The recovery functions use the ecrecover precompile (0x1). /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure. /// This is for more safety by default. /// Use the `tryRecover` variants if you need to get the zero address back /// upon recovery failure instead. /// - As of Solady version 0.0.134, all `bytes signature` variants accept both /// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. /// See: https://eips.ethereum.org/EIPS/eip-2098 /// This is for calldata efficiency on smart accounts prevalent on L2s. /// /// WARNING! Do NOT directly use signatures as unique identifiers: /// - The recovery operations do NOT check if a signature is non-malleable. /// - Use a nonce in the digest to prevent replay attacks on the same contract. /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. /// EIP-712 also enables readable signing of typed data for better user safety. /// - If you need a unique hash from a signature, please use the `canonicalHash` functions. library ECDSA { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The order of the secp256k1 elliptic curve. uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; /// @dev `N/2 + 1`. Used for checking the malleability of the signature. uint256 private constant _HALF_N_PLUS_1 = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The signature is invalid. error InvalidSignature(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RECOVERY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. for {} 1 {} { mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. if eq(mload(signature), 64) { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(mload(signature), 65) { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. break } result := 0 break } result := mload( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) for {} 1 {} { if eq(signature.length, 64) { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(signature.length, 65) { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. break } result := 0 break } result := mload( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. result := mload( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) result := mload( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* TRY-RECOVER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // WARNING! // These functions will NOT revert upon recovery failure. // Instead, they will return the zero address upon recovery failure. // It is critical that the returned address is NEVER compared against // a zero address (e.g. an uninitialized address variable). /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. for {} 1 {} { mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. if eq(mload(signature), 64) { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(mload(signature), 65) { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. break } result := 0 break } pop( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) for {} 1 {} { if eq(signature.length, 64) { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(signature.length, 65) { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. break } result := 0 break } pop( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. pop( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) pop( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HASHING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an Ethereum Signed Message, created from a `hash`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, hash) // Store into scratch space for keccak256. mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. } } /// @dev Returns an Ethereum Signed Message, created from `s`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. /// Note: Supports lengths of `s` up to 999999 bytes. function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let sLength := mload(s) let o := 0x20 mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. mstore(0x00, 0x00) // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. for { let temp := sLength } 1 {} { o := sub(o, 1) mstore8(o, add(48, mod(temp, 10))) temp := div(temp, 10) if iszero(temp) { break } } let n := sub(0x3a, o) // Header length: `26 + 32 - o`. // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) mstore(s, sLength) // Restore the length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CANONICAL HASH FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // The following functions returns the hash of the signature in it's canonicalized format, // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28. // If `s` is greater than `N / 2` then it will be converted to `N - s` // and the `v` value will be flipped. // If the signature has an invalid length, or if `v` is invalid, // a uniquely corrupt hash will be returned. // These functions are useful for "poor-mans-VRF". /// @dev Returns the canonical hash of `signature`. function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { let l := mload(signature) for {} 1 {} { mstore(0x00, mload(add(signature, 0x20))) // `r`. let s := mload(add(signature, 0x40)) let v := mload(add(signature, 0x41)) if eq(l, 64) { v := add(shr(255, s), 27) s := shr(1, shl(1, s)) } if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. break } // If the length is neither 64 nor 65, return a uniquely corrupted hash. if iszero(lt(sub(l, 64), 2)) { // `bytes4(keccak256("InvalidSignatureLength"))`. result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2) } } } /// @dev Returns the canonical hash of `signature`. function canonicalHashCalldata(bytes calldata signature) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { let l := signature.length for {} 1 {} { mstore(0x00, calldataload(signature.offset)) // `r`. let s := calldataload(add(signature.offset, 0x20)) let v := calldataload(add(signature.offset, 0x21)) if eq(l, 64) { v := add(shr(255, s), 27) s := shr(1, shl(1, s)) } if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. break } // If the length is neither 64 nor 65, return a uniquely corrupted hash. if iszero(lt(sub(l, 64), 2)) { calldatacopy(mload(0x40), signature.offset, l) // `bytes4(keccak256("InvalidSignatureLength"))`. result := xor(keccak256(mload(0x40), l), 0xd62f1ab2) } } } /// @dev Returns the canonical hash of `signature`. function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { mstore(0x00, r) // `r`. let v := add(shr(255, vs), 27) let s := shr(1, shl(1, vs)) mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the canonical hash of `signature`. function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { mstore(0x00, r) // `r`. if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes. function emptySignature() internal pure returns (bytes calldata signature) { /// @solidity memory-safe-assembly assembly { signature.length := 0 } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) /// /// @dev Note: /// For performance and bytecode compactness, most of the string operations are restricted to /// byte strings (7-bit ASCII), except where otherwise specified. /// Usage of byte string operations on charsets with runes spanning two or more bytes /// can lead to undefined behavior. library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The length of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /// @dev The length of the string is more than 32 bytes. error TooBigForSmallString(); /// @dev The input string must be a 7-bit ASCII. error StringNot7BitASCII(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'. uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000; /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'. uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000; /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'. uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000; /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000; /// @dev Lookup for '0123456789'. uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000; /// @dev Lookup for '0123456789abcdefABCDEF'. uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000; /// @dev Lookup for '01234567'. uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000; /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'. uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00; /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'. uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000; /// @dev Lookup for ' \t\n\r\x0b\x0c'. uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // The maximum value of a uint256 contains 78 digits (1 byte per digit), but // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. // We will need 1 word for the trailing zeros padding, 1 word for the length, // and 3 words for a maximum of 78 digits. result := add(mload(0x40), 0x80) mstore(0x40, add(result, 0x20)) // Allocate memory. mstore(result, 0) // Zeroize the slot after the string. let end := result // Cache the end of the memory to calculate the length later. let w := not(0) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { result := add(result, w) // `sub(result, 1)`. // Store the character to the pointer. // The ASCII index of the '0' character is 48. mstore8(result, add(48, mod(temp, 10))) temp := div(temp, 10) // Keep dividing `temp` until zero. if iszero(temp) { break } } let n := sub(end, result) result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length. mstore(result, n) // Store the length. } } /// @dev Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory result) { if (value >= 0) return toString(uint256(value)); unchecked { result = toString(~uint256(value) + 1); } /// @solidity memory-safe-assembly assembly { // We still have some spare memory space on the left, // as we have allocated 3 words (96 bytes) for up to 78 digits. let n := mload(result) // Load the string length. mstore(result, 0x2d) // Store the '-' character. result := sub(result, 1) // Move back the string pointer by a byte. mstore(result, add(n, 1)) // Update the string length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HEXADECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory result) { result = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. // We add 0x20 to the total and round down to a multiple of 0x20. // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. result := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) mstore(0x40, add(result, 0x20)) // Allocate memory. mstore(result, 0) // Zeroize the slot after the string. let end := result // Cache the end to calculate the length later. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let start := sub(result, add(length, length)) let w := not(1) // Tsk. let temp := value // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for {} 1 {} { result := add(result, w) // `sub(result, 2)`. mstore8(add(result, 1), mload(and(temp, 15))) mstore8(result, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(result, start)) { break } } if temp { mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`. revert(0x1c, 0x04) } let n := sub(end, result) result := sub(result, 0x20) mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2 + 2` bytes. function toHexString(uint256 value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x". /// The output excludes leading "0" from the `toHexString` output. /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. function toMinimalHexString(uint256 value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present. let n := add(mload(result), 2) // Compute the length. mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero. result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero. mstore(result, sub(n, o)) // Store the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output excludes leading "0" from the `toHexStringNoPrefix` output. /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present. let n := mload(result) // Get the length. result := add(result, o) // Move the pointer, accounting for leading zero. mstore(result, sub(n, o)) // Store the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2` bytes. function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x40 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. result := add(mload(0x40), 0x80) mstore(0x40, add(result, 0x20)) // Allocate memory. mstore(result, 0) // Zeroize the slot after the string. let end := result // Cache the end to calculate the length later. mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. let w := not(1) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { result := add(result, w) // `sub(result, 2)`. mstore8(add(result, 1), mload(and(temp, 15))) mstore8(result, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } let n := sub(end, result) result := sub(result, 0x20) mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory result) { result = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(result, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 {} { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory result) { result = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Allocate memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(result, 0x80)) mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. result := add(result, 2) mstore(result, 40) // Store the length. let o := add(result, 0x20) mstore(add(o, 40), 0) // Zeroize the slot after the string. value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 {} { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory result) { result = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let n := add(mload(result), 2) // Compute the length. mstore(result, 0x3078) // Store the "0x" prefix. result := sub(result, 2) // Move the pointer. mstore(result, n) // Store the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let n := mload(raw) result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(result, add(n, n)) // Store the length of the output. mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. let o := add(result, 0x20) let end := add(raw, n) for {} iszero(eq(raw, end)) {} { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RUNE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the number of UTF characters in the string. function runeCount(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { mstore(0x00, div(not(0), 255)) mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) let o := add(s, 0x20) let end := add(o, mload(s)) for { result := 1 } 1 { result := add(result, 1) } { o := add(o, byte(0, mload(shr(250, mload(o))))) if iszero(lt(o, end)) { break } } } } } /// @dev Returns if this string is a 7-bit ASCII string. /// (i.e. all characters codes are in [0..127]) function is7BitASCII(string memory s) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := 1 let mask := shl(7, div(not(0), 255)) let n := mload(s) if n { let o := add(s, 0x20) let end := add(o, n) let last := mload(end) mstore(end, 0) for {} 1 {} { if and(mask, mload(o)) { result := 0 break } o := add(o, 0x20) if iszero(lt(o, end)) { break } } mstore(end, last) } } } /// @dev Returns if this string is a 7-bit ASCII string, /// AND all characters are in the `allowed` lookup. /// Note: If `s` is empty, returns true regardless of `allowed`. function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := 1 if mload(s) { let allowed_ := shr(128, shl(128, allowed)) let o := add(s, 0x20) for { let end := add(o, mload(s)) } 1 {} { result := and(result, shr(byte(0, mload(o)), allowed_)) o := add(o, 1) if iszero(and(result, lt(o, end))) { break } } } } } /// @dev Converts the bytes in the 7-bit ASCII string `s` to /// an allowed lookup for use in `is7BitASCII(s, allowed)`. /// To save runtime gas, you can cache the result in an immutable variable. function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { let o := add(s, 0x20) for { let end := add(o, mload(s)) } 1 {} { result := or(result, shl(byte(0, mload(o)), 1)) o := add(o, 1) if iszero(lt(o, end)) { break } } if shr(128, result) { mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`. revert(0x1c, 0x04) } } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, byte string operations are restricted // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets. // Usage of byte string operations on charsets with runes spanning two or more bytes // can lead to undefined behavior. /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`. function replace(string memory subject, string memory needle, string memory replacement) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let needleLen := mload(needle) let replacementLen := mload(replacement) let d := sub(result, subject) // Memory difference. let i := add(subject, 0x20) // Subject bytes pointer. let end := add(i, mload(subject)) if iszero(gt(needleLen, mload(subject))) { let subjectSearchEnd := add(sub(end, needleLen), 1) let h := 0 // The hash of `needle`. if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) } let s := mload(add(needle, 0x20)) for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} { let t := mload(i) // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(i, needleLen), h)) { mstore(add(i, d), t) i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let j := 0 } 1 {} { mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j))) j := add(j, 0x20) if iszero(lt(j, replacementLen)) { break } } d := sub(add(d, replacementLen), needleLen) if needleLen { i := add(i, needleLen) if iszero(lt(i, subjectSearchEnd)) { break } continue } } mstore(add(i, d), t) i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } } } let n := add(sub(d, add(result, 0x20)), end) // Copy the rest of the string one word at a time. for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) } let o := add(i, d) mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, n) // Store the length. } } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function indexOf(string memory subject, string memory needle, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := not(0) // Initialize to `NOT_FOUND`. for { let subjectLen := mload(subject) } 1 {} { if iszero(mload(needle)) { result := from if iszero(gt(from, subjectLen)) { break } result := subjectLen break } let needleLen := mload(needle) let subjectStart := add(subject, 0x20) subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLen), needleLen), 1) let m := shl(3, sub(0x20, and(needleLen, 0x1f))) let s := mload(add(needle, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLen))) { break } if iszero(lt(needleLen, 0x20)) { for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, needleLen), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function indexOf(string memory subject, string memory needle) internal pure returns (uint256 result) { result = indexOf(subject, needle, 0); } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function lastIndexOf(string memory subject, string memory needle, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let needleLen := mload(needle) if gt(needleLen, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), needleLen) if iszero(gt(fromMax, from)) { from := fromMax } let end := add(add(subject, 0x20), w) subject := add(add(subject, 0x20), from) if iszero(gt(subject, end)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} { if eq(keccak256(subject, needleLen), h) { result := sub(subject, add(end, 1)) break } subject := add(subject, w) // `sub(subject, 1)`. if iszero(gt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function lastIndexOf(string memory subject, string memory needle) internal pure returns (uint256 result) { result = lastIndexOf(subject, needle, type(uint256).max); } /// @dev Returns true if `needle` is found in `subject`, false otherwise. function contains(string memory subject, string memory needle) internal pure returns (bool) { return indexOf(subject, needle) != NOT_FOUND; } /// @dev Returns whether `subject` starts with `needle`. function startsWith(string memory subject, string memory needle) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let needleLen := mload(needle) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( iszero(gt(needleLen, mload(subject))), eq( keccak256(add(subject, 0x20), needleLen), keccak256(add(needle, 0x20), needleLen) ) ) } } /// @dev Returns whether `subject` ends with `needle`. function endsWith(string memory subject, string memory needle) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let needleLen := mload(needle) // Whether `needle` is not longer than `subject`. let inRange := iszero(gt(needleLen, mload(subject))) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( eq( keccak256( // `subject + 0x20 + max(subjectLen - needleLen, 0)`. add(add(subject, 0x20), mul(inRange, sub(mload(subject), needleLen))), needleLen ), keccak256(add(needle, 0x20), needleLen) ), inRange ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLen := mload(subject) if iszero(or(iszero(times), iszero(subjectLen))) { result := mload(0x40) subject := add(subject, 0x20) let o := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let j := 0 } 1 {} { mstore(add(o, j), mload(add(subject, j))) j := add(j, 0x20) if iszero(lt(j, subjectLen)) { break } } o := add(o, subjectLen) times := sub(times, 1) if iszero(times) { break } } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, sub(o, add(result, 0x20))) // Store the length. } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLen := mload(subject) if iszero(gt(subjectLen, end)) { end := subjectLen } if iszero(gt(subjectLen, start)) { start := subjectLen } if lt(start, end) { result := mload(0x40) let n := sub(end, start) let i := add(subject, start) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let j := and(add(n, 0x1f), w) } 1 {} { mstore(add(result, j), mload(add(i, j))) j := add(j, w) // `sub(j, 0x20)`. if iszero(j) { break } } let o := add(add(result, 0x20), n) mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, n) // Store the length. } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. /// `start` is a byte offset. function slice(string memory subject, uint256 start) internal pure returns (string memory result) { result = slice(subject, start, type(uint256).max); } /// @dev Returns all the indices of `needle` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory needle) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let searchLen := mload(needle) if iszero(gt(searchLen, mload(subject))) { result := mload(0x40) let i := add(subject, 0x20) let o := add(result, 0x20) let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1) let h := 0 // The hash of `needle`. if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) } let s := mload(add(needle, 0x20)) for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} { let t := mload(i) // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(i, searchLen), h)) { i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } continue } } mstore(o, sub(i, add(subject, 0x20))) // Append to `result`. o := add(o, 0x20) i := add(i, searchLen) // Advance `i` by `searchLen`. if searchLen { if iszero(lt(i, subjectSearchEnd)) { break } continue } } i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } } mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`. // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(o, 0x20)) } } } /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { uint256[] memory indices = indicesOf(subject, delimiter); /// @solidity memory-safe-assembly assembly { let w := not(0x1f) let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(add(indicesEnd, w), mload(subject)) mstore(indices, add(mload(indices), 1)) for { let prevIndex := 0 } 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let l := sub(index, prevIndex) mstore(element, l) // Store the length of the element. // Copy the `subject` one word at a time, backwards. for { let o := and(add(l, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the string. // Allocate memory for the length and the bytes, rounded up to a multiple of 32. mstore(0x40, add(element, and(add(l, 0x3f), w))) mstore(indexPtr, element) // Store the `element` into the array. } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) if iszero(lt(indexPtr, indicesEnd)) { break } } result := indices if iszero(mload(delimiter)) { result := add(indices, 0x20) mstore(result, sub(mload(indices), 2)) } } } /// @dev Returns a concatenated string of `a` and `b`. /// Cheaper than `string.concat()` and does not de-align the free memory pointer. function concat(string memory a, string memory b) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let w := not(0x1f) let aLen := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(aLen, 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLen := mload(b) let output := add(result, aLen) // Copy `b` one word at a time, backwards. for { let o := and(add(bLen, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLen := add(aLen, bLen) let last := add(add(result, 0x20), totalLen) mstore(last, 0) // Zeroize the slot after the string. mstore(result, totalLen) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate memory. } } /// @dev Returns a copy of the string in either lowercase or UPPERCASE. /// WARNING! This function is only compatible with 7-bit ASCII strings. function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let n := mload(subject) if n { result := mload(0x40) let o := add(result, 0x20) let d := sub(subject, result) let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) for { let end := add(o, n) } 1 {} { let b := byte(0, mload(add(d, o))) mstore8(o, xor(and(shr(b, flags), 0x20), b)) o := add(o, 1) if eq(o, end) { break } } mstore(result, n) // Store the length. mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. } } } /// @dev Returns a string from a small bytes32 string. /// `s` must be null-terminated, or behavior will be undefined. function fromSmallString(bytes32 s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let n := 0 for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'. mstore(result, n) // Store the length. let o := add(result, 0x20) mstore(o, s) // Store the bytes of the string. mstore(add(o, n), 0) // Zeroize the slot after the string. mstore(0x40, add(result, 0x40)) // Allocate memory. } } /// @dev Returns the small string, with all bytes after the first null byte zeroized. function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'. mstore(0x00, s) mstore(result, 0x00) result := mload(0x00) } } /// @dev Returns the string as a normalized null-terminated small string. function toSmallString(string memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := mload(s) if iszero(lt(result, 33)) { mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`. revert(0x1c, 0x04) } result := shl(shl(3, sub(32, result)), mload(add(s, result))) } } /// @dev Returns a lowercased copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function lower(string memory subject) internal pure returns (string memory result) { result = toCase(subject, false); } /// @dev Returns an UPPERCASED copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function upper(string memory subject) internal pure returns (string memory result) { result = toCase(subject, true); } /// @dev Escapes the string to be used within HTML tags. function escapeHTML(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let end := add(s, mload(s)) let o := add(result, 0x20) // Store the bytes of the packed offsets and strides into the scratch space. // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. mstore(0x1f, 0x900094) mstore(0x08, 0xc0000000a6ab) // Store ""&'<>" into the scratch space. mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(o, c) o := add(o, 1) continue } let t := shr(248, mload(c)) mstore(o, mload(and(t, 0x1f))) o := add(o, shr(5, t)) } mstore(o, 0) // Zeroize the slot after the string. mstore(result, sub(o, add(result, 0x20))) // Store the length. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. function escapeJSON(string memory s, bool addDoubleQuotes) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let o := add(result, 0x20) if addDoubleQuotes { mstore8(o, 34) o := add(1, o) } // Store "\\u0000" in scratch space. // Store "0123456789abcdef" in scratch space. // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. // into the scratch space. mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) // Bitmask for detecting `["\"","\\"]`. let e := or(shl(0x22, 1), shl(0x5c, 1)) for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) if iszero(lt(c, 0x20)) { if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`. mstore8(o, c) o := add(o, 1) continue } mstore8(o, 0x5c) // "\\". mstore8(add(o, 1), c) o := add(o, 2) continue } if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`. mstore8(0x1d, mload(shr(4, c))) // Hex value. mstore8(0x1e, mload(and(c, 15))) // Hex value. mstore(o, mload(0x19)) // "\\u00XX". o := add(o, 6) continue } mstore8(o, 0x5c) // "\\". mstore8(add(o, 1), mload(add(c, 8))) o := add(o, 2) } if addDoubleQuotes { mstore8(o, 34) o := add(1, o) } mstore(o, 0) // Zeroize the slot after the string. mstore(result, sub(o, add(result, 0x20))) // Store the length. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. function escapeJSON(string memory s) internal pure returns (string memory result) { result = escapeJSON(s, false); } /// @dev Encodes `s` so that it can be safely used in a URI, /// just like `encodeURIComponent` in JavaScript. /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent /// See: https://datatracker.ietf.org/doc/html/rfc2396 /// See: https://datatracker.ietf.org/doc/html/rfc3986 function encodeURIComponent(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Store "0123456789ABCDEF" in scratch space. // Uppercased to be consistent with JavaScript's implementation. mstore(0x0f, 0x30313233343536373839414243444546) let o := add(result, 0x20) for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // If not in `[0-9A-Z-a-z-_.!~*'()]`. if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) { mstore8(o, 0x25) // '%'. mstore8(add(o, 1), mload(and(shr(4, c), 15))) mstore8(add(o, 2), mload(and(c, 15))) o := add(o, 3) continue } mstore8(o, c) o := add(o, 1) } mstore(result, sub(o, add(result, 0x20))) // Store the length. mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate memory. } } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string. function eqs(string memory a, bytes32 b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { // These should be evaluated on compile time, as far as possible. let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`. let x := not(or(m, or(b, add(m, and(b, m))))) let r := shl(7, iszero(iszero(shr(128, x)))) r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // forgefmt: disable-next-item result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))), xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20))))) } } /// @dev Packs a single string with its length into a single word. /// Returns `bytes32(0)` if the length is zero or greater than 31. function packOne(string memory a) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { // We don't need to zero right pad the string, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes. mload(add(a, 0x1f)), // `length != 0 && length < 32`. Abuses underflow. // Assumes that the length is valid and within the block gas limit. lt(sub(mload(a), 1), 0x1f) ) } } /// @dev Unpacks a string packed using {packOne}. /// Returns the empty string if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packOne}, the output behavior is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Grab the free memory pointer. mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(result, 0) // Zeroize the length slot. mstore(add(result, 0x1f), packed) // Store the length and bytes. mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes. } } /// @dev Packs two strings with their lengths into a single word. /// Returns `bytes32(0)` if combined length is zero or greater than 30. function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let aLen := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( or( // Load the length and the bytes of `a` and `b`. shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))), // `totalLen != 0 && totalLen < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLen, mload(b)), 1), 0x1e) ) } } /// @dev Unpacks strings packed using {packTwo}. /// Returns the empty strings if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packTwo}, the output behavior is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly assembly { resultA := mload(0x40) // Grab the free memory pointer. resultB := add(resultA, 0x40) // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. mstore(0x40, add(resultB, 0x40)) // Zeroize the length slots. mstore(resultA, 0) mstore(resultB, 0) // Store the lengths and bytes. mstore(add(resultA, 0x1f), packed) mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) // Right pad with zeroes. mstore(add(add(resultA, 0x20), mload(resultA)), 0) mstore(add(add(resultB, 0x20), mload(resultB)), 0) } } /// @dev Directly returns `a` without copying. function directReturn(string memory a) internal pure { assembly { // Assumes that the string does not start from the scratch space. let retStart := sub(a, 0x20) let retUnpaddedSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, retUnpaddedSize), 0) mstore(retStart, 0x20) // Store the return offset. // End the transaction, returning the string. return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize))) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) library MerkleProofLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MERKLE PROOF VERIFICATION OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if mload(proof) { // Initialize `offset` to the offset of `proof` elements in memory. let offset := add(proof, 0x20) // Left shift by 5 is equivalent to multiplying by 0x20. let end := add(offset, shl(5, mload(proof))) // Iterate over proof elements to compute root hash. for {} 1 {} { // Slot of `leaf` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(leaf, mload(offset))) // Store elements to hash contiguously in scratch space. // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. mstore(scratch, leaf) mstore(xor(scratch, 0x20), mload(offset)) // Reuse `leaf` to store the hash to reduce stack operations. leaf := keccak256(0x00, 0x40) offset := add(offset, 0x20) if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) } } /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if proof.length { // Left shift by 5 is equivalent to multiplying by 0x20. let end := add(proof.offset, shl(5, proof.length)) // Initialize `offset` to the offset of `proof` in the calldata. let offset := proof.offset // Iterate over proof elements to compute root hash. for {} 1 {} { // Slot of `leaf` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(leaf, calldataload(offset))) // Store elements to hash contiguously in scratch space. // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. mstore(scratch, leaf) mstore(xor(scratch, 0x20), calldataload(offset)) // Reuse `leaf` to store the hash to reduce stack operations. leaf := keccak256(0x00, 0x40) offset := add(offset, 0x20) if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) } } /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` /// will always return false. /// - The sum of the lengths of `proof` and `leaves` must never overflow. /// - Any non-zero word in the `flags` array is treated as true. /// - The memory offset of `proof` must be non-zero /// (i.e. `proof` is not pointing to the scratch space). function verifyMultiProof( bytes32[] memory proof, bytes32 root, bytes32[] memory leaves, bool[] memory flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. // The queue starts with the `leaves` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // // The `flags` array denotes whether the sibling // should be popped from the queue (`flag == true`), or // should be popped from the `proof` (`flag == false`). /// @solidity memory-safe-assembly assembly { // Cache the lengths of the arrays. let leavesLength := mload(leaves) let proofLength := mload(proof) let flagsLength := mload(flags) // Advance the pointers of the arrays to point to the data. leaves := add(0x20, leaves) proof := add(0x20, proof) flags := add(0x20, flags) // If the number of flags is correct. for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} { // For the case where `proof.length + leaves.length == 1`. if iszero(flagsLength) { // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root) break } // The required final proof offset if `flagsLength` is not zero, otherwise zero. let proofEnd := add(proof, shl(5, proofLength)) // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) // Copy the leaves into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. leavesLength := shl(5, leavesLength) for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } { mstore(add(hashesFront, i), mload(add(leaves, i))) } // Compute the back of the hashes. let hashesBack := add(hashesFront, leavesLength) // This is the end of the memory for the queue. // We recycle `flagsLength` to save on stack variables (sometimes save gas). flagsLength := add(hashesBack, shl(5, flagsLength)) for {} 1 {} { // Pop from `hashes`. let a := mload(hashesFront) // Pop from `hashes`. let b := mload(add(hashesFront, 0x20)) hashesFront := add(hashesFront, 0x40) // If the flag is false, load the next proof, // else, pops from the queue. if iszero(mload(flags)) { // Loads the next proof. b := mload(proof) proof := add(proof, 0x20) // Unpop from `hashes`. hashesFront := sub(hashesFront, 0x20) } // Advance to the next flag. flags := add(flags, 0x20) // Slot of `a` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(a, b)) // Hash the scratch space and push the result onto the queue. mstore(scratch, a) mstore(xor(scratch, 0x20), b) mstore(hashesBack, keccak256(0x00, 0x40)) hashesBack := add(hashesBack, 0x20) if iszero(lt(hashesBack, flagsLength)) { break } } isValid := and( // Checks if the last value in the queue is same as the root. eq(mload(sub(hashesBack, 0x20)), root), // And whether all the proofs are used, if required. eq(proofEnd, proof) ) break } } } /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` /// will always return false. /// - Any non-zero word in the `flags` array is treated as true. /// - The calldata offset of `proof` must be non-zero /// (i.e. `proof` is from a regular Solidity function with a 4-byte selector). function verifyMultiProofCalldata( bytes32[] calldata proof, bytes32 root, bytes32[] calldata leaves, bool[] calldata flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. // The queue starts with the `leaves` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // // The `flags` array denotes whether the sibling // should be popped from the queue (`flag == true`), or // should be popped from the `proof` (`flag == false`). /// @solidity memory-safe-assembly assembly { // If the number of flags is correct. for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} { // For the case where `proof.length + leaves.length == 1`. if iszero(flags.length) { // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. // forgefmt: disable-next-item isValid := eq( calldataload( xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length)) ), root ) break } // The required final proof offset if `flagsLength` is not zero, otherwise zero. let proofEnd := add(proof.offset, shl(5, proof.length)) // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) // Copy the leaves into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length)) // Compute the back of the hashes. let hashesBack := add(hashesFront, shl(5, leaves.length)) // This is the end of the memory for the queue. // We recycle `flagsLength` to save on stack variables (sometimes save gas). flags.length := add(hashesBack, shl(5, flags.length)) // We don't need to make a copy of `proof.offset` or `flags.offset`, // as they are pass-by-value (this trick may not always save gas). for {} 1 {} { // Pop from `hashes`. let a := mload(hashesFront) // Pop from `hashes`. let b := mload(add(hashesFront, 0x20)) hashesFront := add(hashesFront, 0x40) // If the flag is false, load the next proof, // else, pops from the queue. if iszero(calldataload(flags.offset)) { // Loads the next proof. b := calldataload(proof.offset) proof.offset := add(proof.offset, 0x20) // Unpop from `hashes`. hashesFront := sub(hashesFront, 0x20) } // Advance to the next flag offset. flags.offset := add(flags.offset, 0x20) // Slot of `a` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(a, b)) // Hash the scratch space and push the result onto the queue. mstore(scratch, a) mstore(xor(scratch, 0x20), b) mstore(hashesBack, keccak256(0x00, 0x40)) hashesBack := add(hashesBack, 0x20) if iszero(lt(hashesBack, flags.length)) { break } } isValid := and( // Checks if the last value in the queue is same as the root. eq(mload(sub(hashesBack, 0x20)), root), // And whether all the proofs are used, if required. eq(proofEnd, proof.offset) ) break } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes32 array. function emptyProof() internal pure returns (bytes32[] calldata proof) { /// @solidity memory-safe-assembly assembly { proof.length := 0 } } /// @dev Returns an empty calldata bytes32 array. function emptyLeaves() internal pure returns (bytes32[] calldata leaves) { /// @solidity memory-safe-assembly assembly { leaves.length := 0 } } /// @dev Returns an empty calldata bool array. function emptyFlags() internal pure returns (bool[] calldata flags) { /// @solidity memory-safe-assembly assembly { flags.length := 0 } } }
{ "optimizer": { "enabled": true, "runs": 1 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/BURGERS404/ArchetypeLogicBurgers404.sol": { "ArchetypeLogicBurgers404": "0xd4e182124131fe5f3bde4cdef00975fb97f5b3d8" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"ArrayLengthsMismatch","type":"error"},{"inputs":[],"name":"DNAlreadyInitialized","type":"error"},{"inputs":[],"name":"DNNotInitialized","type":"error"},{"inputs":[],"name":"FnSelectorNotRecognized","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidConfig","type":"error"},{"inputs":[],"name":"InvalidNFTAmount","type":"error"},{"inputs":[],"name":"InvalidSplitShares","type":"error"},{"inputs":[],"name":"InvalidUnit","type":"error"},{"inputs":[],"name":"LockedForever","type":"error"},{"inputs":[],"name":"MaxSupplyExceeded","type":"error"},{"inputs":[],"name":"NotApprovedToTransfer","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC1155ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"WrongPassword","type":"error"},{"inputs":[],"name":"burnToRemintDisabled","type":"error"},{"inputs":[],"name":"invalidTokenIdLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"cid","type":"bytes32"}],"name":"Invited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"OwnedCheckpointSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"affiliate","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint128","name":"wad","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"numMints","type":"uint256"}],"name":"Referral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"SkipNFTSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint128","name":"wad","type":"uint128"}],"name":"Withdrawal","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"affiliate","type":"address"}],"name":"affiliateBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"affiliateBalanceToken","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct Auth","name":"auth","type":"tuple"},{"internalType":"address[]","name":"toList","type":"address[]"},{"internalType":"uint256[]","name":"quantityList","type":"uint256[]"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"batchMintTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"burnToRemint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"affiliateUsed","type":"bool"}],"name":"computePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"config","outputs":[{"internalType":"string","name":"baseUri","type":"string"},{"internalType":"address","name":"affiliateSigner","type":"address"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"maxBatchSize","type":"uint32"},{"internalType":"uint16","name":"affiliateFee","type":"uint16"},{"internalType":"uint16","name":"affiliateDiscount","type":"uint16"},{"internalType":"uint16","name":"defaultRoyalty","type":"uint16"},{"internalType":"uint16","name":"remintPremium","type":"uint16"},{"internalType":"uint16","name":"erc20Ratio","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flags","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getOwnedCheckpoint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getSkipNFT","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"components":[{"internalType":"string","name":"baseUri","type":"string"},{"internalType":"address","name":"affiliateSigner","type":"address"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"maxBatchSize","type":"uint32"},{"internalType":"uint16","name":"affiliateFee","type":"uint16"},{"internalType":"uint16","name":"affiliateDiscount","type":"uint16"},{"internalType":"uint16","name":"defaultRoyalty","type":"uint16"},{"internalType":"uint16","name":"remintPremium","type":"uint16"},{"internalType":"uint16","name":"erc20Ratio","type":"uint16"}],"internalType":"struct Config","name":"config_","type":"tuple"},{"components":[{"internalType":"uint16","name":"ownerBps","type":"uint16"},{"internalType":"uint16","name":"platformBps","type":"uint16"},{"internalType":"uint16","name":"partnerBps","type":"uint16"},{"internalType":"uint16","name":"superAffiliateBps","type":"uint16"},{"internalType":"address","name":"partner","type":"address"},{"internalType":"address","name":"superAffiliate","type":"address"},{"internalType":"address","name":"ownerAltPayout","type":"address"}],"internalType":"struct PayoutConfig","name":"payoutConfig_","type":"tuple"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"invites","outputs":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint128","name":"reservePrice","type":"uint128"},{"internalType":"uint128","name":"delta","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"listSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"password","type":"string"}],"name":"lockMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"password","type":"string"}],"name":"lockOwnerAltPayout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"password","type":"string"}],"name":"lockURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct Auth","name":"auth","type":"tuple"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct Auth","name":"auth","type":"tuple"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"minted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numErc20Minted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numNftsMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"ownerBalanceToken","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"owns","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"packedBonusDiscounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"pairedListKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payoutConfig","outputs":[{"internalType":"uint16","name":"ownerBps","type":"uint16"},{"internalType":"uint16","name":"platformBps","type":"uint16"},{"internalType":"uint16","name":"partnerBps","type":"uint16"},{"internalType":"uint16","name":"superAffiliateBps","type":"uint16"},{"internalType":"address","name":"partner","type":"address"},{"internalType":"address","name":"superAffiliate","type":"address"},{"internalType":"address","name":"ownerAltPayout","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platform","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"bytes32","name":"_cid","type":"bytes32"},{"components":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint128","name":"reservePrice","type":"uint128"},{"internalType":"uint128","name":"delta","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","type":"bool"}],"internalType":"struct AdvancedInvite","name":"_AdvancedInvite","type":"tuple"}],"name":"setAdvancedInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"affiliateDiscount","type":"uint16"}],"name":"setAffiliateDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"affiliateFee","type":"uint16"}],"name":"setAffiliateFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseUri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"components":[{"internalType":"uint16","name":"numMints","type":"uint16"},{"internalType":"uint16","name":"numBonusMints","type":"uint16"}],"internalType":"struct BonusDiscount[]","name":"_bonusDiscounts","type":"tuple[]"}],"name":"setBonusDiscounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"bytes32","name":"_cid","type":"bytes32"},{"components":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint128","name":"reservePrice","type":"uint128"},{"internalType":"uint128","name":"delta","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","type":"bool"}],"internalType":"struct AdvancedInvite","name":"_advancedInvite","type":"tuple"},{"components":[{"internalType":"uint16","name":"numMints","type":"uint16"},{"internalType":"uint16","name":"numBonusMints","type":"uint16"}],"internalType":"struct BonusDiscount[]","name":"_bonusDiscount","type":"tuple[]"}],"name":"setBonusInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint16","name":"feeNumerator","type":"uint16"}],"name":"setDefaultRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"bytes32","name":"_cid","type":"bytes32"},{"components":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","type":"bool"}],"internalType":"struct Invite","name":"_invite","type":"tuple"}],"name":"setInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"maxBatchSize","type":"uint32"}],"name":"setMaxBatchSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"maxSupply","type":"uint32"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"setOwnedCheckpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ownerAltPayout","type":"address"}],"name":"setOwnerAltPayout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key1","type":"bytes32"},{"internalType":"bytes32","name":"key2","type":"bytes32"}],"name":"setPairedInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"remintPremium","type":"uint16"}],"name":"setRemintPremium","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"skipNFT","type":"bool"}],"name":"setSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAffiliate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"withdrawTokensAffiliate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
608060405234801561001057600080fd5b50615f9980620000216000396000f3fe6080604052600436106102f85760003560e01c806301d88f5d1461053f57806301ffc9a71461055f57806306fdde0314610594578063095ea7b3146105b65780630c6f910b146105d65780630e89341c146105f657806315ec67201461061657806318160ddd1461062b57806321d5bf241461064e57806323b872dd146106b5578063274e430b146106d557806327a59437146106f557806329a96532146107345780632a55205a146107545780632a6a935d14610782578063313ce567146107a257806332f7c6d4146107be5780633ccfd60b146107de5780633e100f6e146107f35780634331f639146108135780634a21a2df146108335780634bde38c8146108465780634f558e791461088157806351305a82146108a157806355f804b3146108ce5780635ecb16cd146108ee5780635fb8ecfb1461090e57806363dbc71f1461092e57806364cc4aa51461094e5780636e884900146109645780636f33659f1461098457806370a08231146109a4578063715018a6146109c457806379502c55146109d95780637c5d0a0814610a03578063818d4b5d14610a235780638b4795d614610a3e5780638da5cb5b14610a5e5780639564e8e414610a7357806395d89b4114610a8657806396a0924e14610a9b578063978a450914610abb5780639a7a973c14610b055780639c8770b714610b32578063a22cb46514610b52578063a3edb86a14610b72578063a5aa4aa414610bb5578063a9059cbb14610cd1578063aab6a17b14610cf1578063ac17811c14610d11578063b68836fa14610d26578063bcb40d5414610d53578063bcc1ed0714610d68578063bedcf00314610d7b578063caf3e53214610dc1578063d404844114610de1578063db8a560014610e89578063db8f6ec414610ea9578063dd62ed3e14610ec9578063de6cd0db14610ee9578063e2f2379a14610f09578063e985e9c514610f29578063ead0055314610f49578063f2fde38b14610f69578063f588eb5014610f89578063f5b100ea14610fa9578063f9da322414610fc95761030a565b3661030a57341561030857600080fd5b005b60003560e01c63f242432a8190036103655760643560011461033f57604051631ec9b93b60e21b815260040160405180910390fd5b61035b336004356024356044356103566084610fe9565b61101e565b6103656001611245565b80632eb2c2d60361041f57600061037c604461124f565b9050600061038a606461124f565b825181519192509081146103b157604051631dc0052360e11b815260040160405180910390fd5b600019810190156103f1576103cc828260051b016020015190565b6001146103ec57604051631ec9b93b60e21b815260040160405180910390fd5b6103b1565b50506104133361040060043590565b6024358461040e6084610fe9565b61127e565b61041d6001611245565b505b80634e1273f4036104e8576000610436600461124f565b90506000610444602461124f565b8051835191925090811461046b57604051631dc0052360e11b815260040160405180910390fd5b600061047682611467565b90505b600019820191156104d4576000610496858460051b016020015190565b90506104ce82846104be6104b9856104b48a8a60051b016020015190565b611485565b151590565b808260051b846020010152505050565b50610479565b602080820352805160051b60400160208203f35b8062fdd58e03610511576000610502600435602435611485565b905061050f811515611245565b505b80630e0b098403610526576105266001611245565b604051631e085ca760e11b815260040160405180910390fd5b34801561054b57600080fd5b5061030861055a366004614b07565b6114bd565b34801561056b57600080fd5b5061057f61057a366004614b3e565b61173f565b60405190151581526020015b60405180910390f35b3480156105a057600080fd5b506105a9611776565b60405161058b9190614bbf565b3480156105c257600080fd5b5061057f6105d1366004614bd2565b611808565b3480156105e257600080fd5b506103086105f1366004614c0e565b61181e565b34801561060257600080fd5b506105a9610611366004614c2b565b6118a7565b34801561062257600080fd5b5061030861192b565b34801561063757600080fd5b5061064061198e565b60405190815260200161058b565b34801561065a57600080fd5b5061069d610669366004614c44565b6001600160a01b03918216600090815260ce602090815260408083209390941682529190915220546001600160801b031690565b6040516001600160801b03909116815260200161058b565b3480156106c157600080fd5b5061057f6106d0366004614c7d565b6119ae565b3480156106e157600080fd5b5061057f6106f0366004614cbe565b611a22565b34801561070157600080fd5b5061069d610710366004614cbe565b6001600160a01b0316600090815260cd60205260409020546001600160801b031690565b34801561074057600080fd5b5061030861074f366004614c2b565b611a73565b34801561076057600080fd5b5061077461076f366004614cdb565b611a7d565b60405161058b929190614cfd565b34801561078e57600080fd5b5061057f61079d366004614d16565b611b2b565b3480156107ae57600080fd5b506040516012815260200161058b565b3480156107ca57600080fd5b506103086107d9366004614d74565b611b3f565b3480156107ea57600080fd5b50610308611b93565b3480156107ff57600080fd5b5061064061080e366004614cbe565b611bf3565b34801561081f57600080fd5b5061030861082e366004614db5565b611c2c565b610308610841366004614df5565b611c8b565b34801561085257600080fd5b507386b82972282dd22348374bc63fd21620f7ed847b5b6040516001600160a01b03909116815260200161058b565b34801561088d57600080fd5b5061057f61089c366004614c2b565b611ca7565b3480156108ad57600080fd5b506106406108bc366004614c2b565b60cf6020526000908152604090205481565b3480156108da57600080fd5b506103086108e9366004614eec565b611cb2565b3480156108fa57600080fd5b50610308610909366004614f4b565b611d22565b34801561091a57600080fd5b5061030861092936600461502d565b611d94565b34801561093a57600080fd5b50610308610949366004614cdb565b611df3565b34801561095a57600080fd5b5061064060d95481565b34801561097057600080fd5b5061030861097f366004615092565b611ebf565b34801561099057600080fd5b5061030861099f3660046150d5565b612027565b3480156109b057600080fd5b506106406109bf366004614cbe565b61208d565b3480156109d057600080fd5b506103086120c9565b3480156109e557600080fd5b506109ee6120dd565b60405161058b999897969594939291906150f2565b348015610a0f57600080fd5b50610308610a1e366004614d74565b6121c9565b348015610a2f57600080fd5b5061057f6104b4366004614bd2565b348015610a4a57600080fd5b50610308610a59366004614c0e565b61221d565b348015610a6a57600080fd5b506108696122a7565b610308610a8136600461515f565b6122b6565b348015610a9257600080fd5b506105a961238f565b348015610aa757600080fd5b50610308610ab6366004614c0e565b61239e565b348015610ac757600080fd5b5061069d610ad6366004614cbe565b6001600160a01b0316600090815260ce602090815260408083208380529091529020546001600160801b031690565b348015610b1157600080fd5b50610640610b20366004614c2b565b600090815260cc602052604090205490565b348015610b3e57600080fd5b50610308610b4d36600461523f565b6123fe565b348015610b5e57600080fd5b50610308610b6d366004615274565b6125aa565b348015610b7e57600080fd5b50610640610b8d366004614bd2565b6001600160a01b0391909116600090815260cb60209081526040808320938352929052205490565b348015610bc157600080fd5b50610c5a610bd0366004614c2b565b60c9602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216939181169263ffffffff928204831692600160a01b8304811692600160c01b8104821692600160e01b9182900483169281811692600160201b8304909116916001600160a01b03600160401b8204169160ff9104168b565b604080516001600160801b039c8d1681529a8c1660208c015298909a169789019790975263ffffffff9586166060890152938516608088015291841660a0870152831660c0860152821660e0850152166101008301526001600160a01b03166101208201529015156101408201526101600161058b565b348015610cdd57600080fd5b5061057f610cec366004614bd2565b6125b5565b348015610cfd57600080fd5b50610308610d0c3660046152a2565b6125d2565b348015610d1d57600080fd5b506106406125e5565b348015610d3257600080fd5b50610640610d41366004614c2b565b60ca6020526000908152604090205481565b348015610d5f57600080fd5b5060d254610640565b610308610d7636600461530d565b612609565b348015610d8757600080fd5b506000805260cd6020527fcd565b10a72538d86f6d352f37ebc5dff31587960b12c0afe00fd03947a6932a546001600160801b031661069d565b348015610dcd57600080fd5b50610640610ddc3660046153df565b61277a565b348015610ded57600080fd5b5060d65460d75460d854610e3a9261ffff80821693620100008304821693600160201b8404831693600160301b8104909316926001600160a01b03600160401b9091048116928116911687565b6040805161ffff98891681529688166020880152948716948601949094529190941660608401526001600160a01b039384166080840152831660a08301529190911660c082015260e00161058b565b348015610e9557600080fd5b50610308610ea4366004615418565b61282c565b348015610eb557600080fd5b50610308610ec43660046154eb565b612839565b348015610ed557600080fd5b50610640610ee4366004614c44565b6129b9565b348015610ef557600080fd5b50610308610f04366004614d74565b6129d8565b348015610f1557600080fd5b50610308610f24366004615536565b612a2c565b348015610f3557600080fd5b5061057f610f44366004614c44565b612cd9565b348015610f5557600080fd5b50610308610f64366004614cbe565b612cfa565b348015610f7557600080fd5b50610308610f84366004614cbe565b612d80565b348015610f9557600080fd5b50610308610fa4366004614f4b565b612df6565b348015610fb557600080fd5b50610640610fc4366004614cbe565b612e30565b348015610fd557600080fd5b50610308610fe43660046150d5565b612e3b565b606060405190508135600401803580835280602083018460200137808360200101915050600081528060200160405250919050565b6001600160a01b03831661104557604051633a954ecd60e21b815260040160405180910390fd5b600061104f612eee565b8054909150600160201b900463ffffffff1661107e5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03868116801591871614176110bb5761109e8587612cd9565b6110bb5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b038516600090815260048201602052604090206110df8185612efb565b6110fb5760405162a1148160e81b815260040160405180910390fd5b61110781856000612f0f565b6001600160a01b0385166000908152600483016020526040902061112d90856001612f0f565b6000611137612f33565b90506000836005016000896001600160a01b03166001600160a01b0316815260200190815260200160002090506000846005016000896001600160a01b03166001600160a01b03168152602001908152602001600020905060018360201b1760801b80835403835581548060601c63ffffffff16808a18818b110260601b8218830184555050508860601b60601c98508760601b60601c97508660005260016020528789337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260406000a4826000528789600080516020615f4483398151915260206000a3611226565b505050565b873b156112395761123989898989612f73565b50505050505050505050565b8060005260206000f35b60606040519050813560040180358083528060051b60208301846020013760051b820160200160405250919050565b6001600160a01b0383166112a557604051633a954ecd60e21b815260040160405180910390fd5b60006112af612eee565b8054909150600160201b900463ffffffff166112de5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b038681168015918716141761131b576112fe8587612cd9565b61131b5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b0380861660009081526005830160205260408082209287168252812085519192839290919061134f612f33565b6001600160a01b03808c166000908152600489016020526040808220928d16825290209183029650905b82156113e8576000199092019160006113988b8560051b016020015190565b90506113a48382612efb565b6113c05760405162a1148160e81b815260040160405180910390fd5b6113cc83826000612f0f565b6113d882826001612f0f565b8681189087100290951894611379565b50505086518460201b1760801b80835403835581548060601c63ffffffff168086188187110260601b821883018455505050611425898989613001565b836000528760601b60601c8960601b60601c600080516020615f4483398151915260206000a3611454565b5050565b873b156112395761123989898989613098565b6040805180820181526020810183815260059390931b018101905290565b60006114b4611492612eee565b6001600160a01b03851660009081526004919091016020526040902083612efb565b90505b92915050565b6114c56122a7565b6001600160a01b03166114d661316b565b6001600160a01b0316146114fd576040516330cd747160e01b815260040160405180910390fd5b6101208101516001600160a01b0316156115c25761012081015160405163095ea7b360e01b81526000916001600160a01b03169063095ea7b39061155d9073aafdfa4a935d8511bf285af11a0544ce7e4a11999060001990600401614cfd565b6020604051808303816000875af115801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a091906155f3565b9050806115c0576040516302df483560e21b815260040160405180910390fd5b505b428160a0015163ffffffff1610156115e15763ffffffff421660a08201525b600083815260c9602090815260408083208451928501516001600160801b03938416600160801b918516820217825585830151600183018054606089015160808a015160a08b015160c08c0151959099166001600160a01b03199093169290921763ffffffff91821690950294909417600160a01b600160e01b031916600160a01b9185169190910263ffffffff60c01b191617600160c01b96841696909602959095176001600160e01b0316600160e01b91831682021790945560e0860151600290920180546101008801516101208901516101408a01519585166001600160401b031990931692909217600160201b919094160292909217600160401b600160e81b031916600160401b6001600160a01b039093169290920260ff60e01b1916919091179115159093021790915551839185917fe9a0c17645ed78ccc9996259f00297ffc75e6b9d22cd605ccc9992cc8ca3f4c19190a3505050565b6000611767826301ffc9a760e09190911c90811463d9b67a26821417630e89341c9091141790565b806114b757506114b782613192565b606060d0805461178590615610565b80601f01602080910402602001604051908101604052809291908181526020018280546117b190615610565b80156117fe5780601f106117d3576101008083540402835291602001916117fe565b820191906000526020600020905b8154815290600101906020018083116117e157829003601f168201915b5050505050905090565b60006118153384846131c7565b50600192915050565b6118266122a7565b6001600160a01b031661183761316b565b6001600160a01b03161461185e576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115611885576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160e01b0261ffff60e01b19909216919091179055565b60606118b28261321e565b6118cf57604051630a14c4b560e41b815260040160405180910390fd5b60d380546118dc90615610565b90506000036118fa57604051806020016040528060008152506114b7565b60d36119058361323c565b604051602001611916929190615644565b60405160208183030381529060405292915050565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611962576119626156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81612df6565b50565b6000611998612eee565b54600160401b90046001600160601b0316919050565b6000806119c66119bc612eee565b6003018633613280565b805490915060001981146119fb57808411156119f5576040516313be252b60e01b815260040160405180910390fd5b83810382555b611a168686866040518060200160405280600081525061329a565b50600195945050505050565b600080611a2d612eee565b6001600160a01b03841660009081526005919091016020526040902054600160581b9004600281161515925060ff81169150600116611a6d57823b151591505b50919050565b61198b3382613912565b60008281526098602090815260408083208151808301909252546001600160a01b038116808352600160a01b9091046001600160601b0316928201929092528291611af25750604080518082019091526097546001600160a01b0381168252600160a01b90046001600160601b031660208201525b602081015160009061271090611b11906001600160601b0316876156f7565b611b1b9190615724565b91519350909150505b9250929050565b6000611b3733836139b9565b506001919050565b611b476122a7565b6001600160a01b0316611b5861316b565b6001600160a01b031614611b7f576040516330cd747160e01b815260040160405180910390fd5b611b898282613a49565b6114506002613abc565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611bca57611bca6156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81611d22565b6000611bfd612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160601b900463ffffffff1690565b611c346122a7565b6001600160a01b0316611c4561316b565b6001600160a01b031614611c6c576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff191661ffff8316908117909155611450908390613acc565b611ca08585611c9861316b565b8686866122b6565b5050505050565b60006114b78261321e565b611cba6122a7565b6001600160a01b0316611ccb61316b565b6001600160a01b031614611cf2576040516330cd747160e01b815260040160405180910390fd5b60d95460011615611d165760405163249fab5d60e01b815260040160405180910390fd5b60d36114508282615793565b73d4e182124131fe5f3bde4cdef00975fb97f5b3d8638e1ab86660d660cd611d486122a7565b856040518563ffffffff1660e01b8152600401611d689493929190615890565b60006040518083038186803b158015611d8057600080fd5b505af4158015611ca0573d6000803e3d6000fd5b611d9c6122a7565b6001600160a01b0316611dad61316b565b6001600160a01b031614611dd4576040516330cd747160e01b815260040160405180910390fd5b611ddf858383612839565b611ca0858561055a368790038701876158bd565b611dfb6122a7565b6001600160a01b0316611e0c61316b565b6001600160a01b031614611e33576040516330cd747160e01b815260040160405180910390fd5b600081815260c960205260408082206001908101548584529190922090910154600160801b9182900463ffffffff908116929091041614611e87576040516306b7c75960e31b815260040160405180910390fd5b611e928160016158da565b600083815260cf6020526040902055611eac8260016158da565b600091825260cf60205260409091205550565b611ec76122a7565b6001600160a01b0316611ed861316b565b6001600160a01b031614611eff576040516330cd747160e01b815260040160405180910390fd5b6112218383604051806101600160405280856000016020810190611f2391906158ed565b6001600160801b03168152602090810190611f40908701876158ed565b6001600160801b0316815260200160006001600160801b03168152602001856020016020810190611f7191906150d5565b63ffffffff168152602001611f8c60608701604088016150d5565b63ffffffff168152602001611fa760808701606088016150d5565b63ffffffff168152602001611fc260a08701608088016150d5565b63ffffffff16815260006020820152604001611fe460c0870160a088016150d5565b63ffffffff168152602001611fff60e0870160c08801614cbe565b6001600160a01b0316815260200161201e610100870160e08801614d16565b151590526114bd565b61202f6122a7565b6001600160a01b031661204061316b565b6001600160a01b031614612067576040516330cd747160e01b815260040160405180910390fd5b60d4805463ffffffff909216600160c01b0263ffffffff60c01b19909216919091179055565b6000612097612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160a01b90046001600160601b031690565b6120d1613bc5565b6120db6000613c34565b565b60d3805481906120ec90615610565b80601f016020809104026020016040519081016040528092919081815260200182805461211890615610565b80156121655780601f1061213a57610100808354040283529160200191612165565b820191906000526020600020905b81548152906001019060200180831161214857829003601f168201915b505050600184015460029094015492936001600160a01b0381169363ffffffff600160a01b830481169450600160c01b830416925061ffff600160e01b8304811692600160f01b900481169180821691620100008204811691600160201b90041689565b6121d16122a7565b6001600160a01b03166121e261316b565b6001600160a01b031614612209576040516330cd747160e01b815260040160405180910390fd5b6122138282613a49565b6114506001613abc565b6122256122a7565b6001600160a01b031661223661316b565b6001600160a01b03161461225d576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115612284576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160f01b026001600160f01b03909216919091179055565b6033546001600160a01b031690565b8535600090815260c96020908152604080832060ca9092529091205460028201546001600160201b90910463ffffffff16111561230c57600282015461230990600160201b900463ffffffff16886156f7565b96505b60d554600090600160201b900461ffff1661233061232a828b615724565b84613c86565b61233a91906156f7565b905061237287670de0b6b3a7640000612353848c6158da565b61235d91906156f7565b60405180602001604052806000815250613d20565b612384838a8a8460d2548b8b8b61405b565b505050505050505050565b606060d1805461178590615610565b6123a66122a7565b6001600160a01b03166123b761316b565b6001600160a01b0316146123de576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff909216620100000263ffff000019909216919091179055565b60d55462010000900461ffff1660000361242b5760405163017dfd0760e71b815260040160405180910390fd5b600181101561244d57604051633ccf184f60e11b815260040160405180910390fd5b600061245761316b565b90506000612463612f33565b61246e9060016156f7565b60d5549091506000906127109061248f9062010000900461ffff16846156f7565b6124999190615724565b905060006124a68461208d565b905060006124e08561dead898960008181106124c4576124c46156cb565b90506020020135604051806020016040528060008152506125d2565b826124e9612f33565b6124f39084615908565b101561257657600286101561251b57604051633ccf184f60e11b815260040160405180910390fd5b612552858661dead8a8a6001818110612536576125366156cb565b905060200201356040518060200160405280600081525061101e565b8261255b612f33565b612565919061591c565b61256f90826158da565b9050612593565b6125938561dead856040518060200160405280600081525061329a565b6125a18561235d83876158da565b50505050505050565b611450338383614435565b60006118153384846040518060200160405280600081525061329a565b6125df338585858561101e565b50505050565b60d55460d25460009161260491600160201b90910461ffff1690615724565b905090565b838614612629576040516306b7c75960e31b815260040160405180910390fd5b8735600090815260c96020908152604080832060ca909252822054909180805b8a8110156127595760028501546000906001600160201b90910463ffffffff1611156126ab576002860154600160201b900463ffffffff168b8b84818110612693576126936156cb565b905060200201356126a491906156f7565b90506126c7565b8a8a838181106126bd576126bd6156cb565b9050602002013590505b60d554600090600160201b900461ffff166126eb6126e58285615724565b88613c86565b6126f591906156f7565b90506127348e8e8581811061270c5761270c6156cb565b90506020020160208101906127219190614cbe565b670de0b6b3a764000061235384866158da565b61273e82866158da565b945061274a81856158da565b93508260010192505050612649565b5061276c848d848460d2548c8c8c61405b565b505050505050505050505050565b600083815260c96020908152604080832060cc9092528083205460d4549151631c130e3f60e21b8152909173d4e182124131fe5f3bde4cdef00975fb97f5b3d89163704c38fc916127e1918691600160f01b900461ffff16908a9087908b9060040161592f565b602060405180830381865af41580156127fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128229190615958565b9695505050505050565b6125df338585858561127e565b612841613bc5565b6008811115612863576040516306b7c75960e31b815260040160405180910390fd5b6000805b60ff81168311156129a15760008160ff161180156128eb5750838361288d600184615971565b60ff1681811061289f5761289f6156cb565b6128b59260206040909202019081019150614c0e565b61ffff1684848360ff168181106128ce576128ce6156cb565b6128e49260206040909202019081019150614c0e565b61ffff1610155b15612909576040516306b7c75960e31b815260040160405180910390fd5b600084848360ff16818110612920576129206156cb565b90506040020160200160208101906129389190614c0e565b61ffff16601086868560ff16818110612953576129536156cb565b6129699260206040909202019081019150614c0e565b61ffff16901b17905061297d82602061598a565b60ff168163ffffffff16901b83179250508080612999906159ad565b915050612867565b50600093845260ca6020526040909320929092555050565b60006129d06129c6612eee565b6003018484613280565b549392505050565b6129e06122a7565b6001600160a01b03166129f161316b565b6001600160a01b031614612a18576040516330cd747160e01b815260040160405180910390fd5b612a228282613a49565b6114506000613abc565b600054610100900460ff1615808015612a4c5750600054600160ff909116105b80612a665750303b158015612a66575060005460ff166001145b612ace5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff191660011790558015612af1576000805461ff0019166101001790555b60d0612afd8782615793565b5060d1612b0a8682615793565b508360d3612b188282615b07565b905050612b2660008061448e565b611388612b3960a0860160808701614c0e565b61ffff161180612b5d5750611388612b5760c0860160a08701614c0e565b61ffff16115b80612b8057506000612b756040860160208701614cbe565b6001600160a01b0316145b80612b9e5750612b9660808501606086016150d5565b63ffffffff16155b15612bbc576040516306b7c75960e31b815260040160405180910390fd5b612bc4614613565b6000612bd66080850160608601614c0e565b612be66060860160408701614c0e565b612bf66040870160208801614c0e565b612c036020880188614c0e565b612c0d9190615c88565b612c179190615c88565b612c219190615c88565b61ffff16905060fa612c396040860160208701614c0e565b61ffff161080612c4b57508061271014155b15612c6957604051632429608560e11b815260040160405180910390fd5b8360d6612c768282615ca3565b505060d554612c8a90849061ffff16611c2c565b508015612cd1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b6000612cf0612ce6612eee565b6001018484613280565b5415159392505050565b612d026122a7565b6001600160a01b0316612d1361316b565b6001600160a01b031614612d3a576040516330cd747160e01b815260040160405180910390fd5b60d95460041615612d5e5760405163249fab5d60e01b815260040160405180910390fd5b60d880546001600160a01b0319166001600160a01b0392909216919091179055565b612d88613bc5565b6001600160a01b038116612ded5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401612ac5565b61198b81613c34565b60405163140fbc8560e01b815273d4e182124131fe5f3bde4cdef00975fb97f5b3d89063140fbc8590611d689060ce908590600401615d81565b60006114b782614642565b612e436122a7565b6001600160a01b0316612e5461316b565b6001600160a01b031614612e7b576040516330cd747160e01b815260040160405180910390fd5b60d95460021615612e9f5760405163249fab5d60e01b815260040160405180910390fd5b60d2548163ffffffff161015612ec857604051638a164f6360e01b815260040160405180910390fd5b60d4805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b68b6dffd38a260769cb290565b60006114b483600160201b8410840261467b565b8160081c8360601b018260ff16821515811b6001821b198354161782555050505050565b60d55460009061260490600160201b900461ffff16670de0b6b3a76400006156f7565b808214612f6b57838252602082019150612f56565b505092915050565b60405163f23a6e6181523360208201528460601b60601c60408201528260608201526001608082015260a08082015281518060c08301528015612fc0578060e08301826020860160045afa505b6020828260c401601c85016000895af1612fe3573d15612fe3573d6000833e3d82fd5b50805163f23a6e6160e01b14611ca057639c05499b6000526004601cfd5b805161300c57505050565b6040516040815260408101825160051b6020018082828660045afa50503d60400160208301523d81019050825181523d81016020820191505b80821461305b5760018252816020019150613045565b506001600160a01b03808516908616337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb85850386a45050505050565b8151156125df5760405163bc197c8181523360208201528460601b60601c604082015260a0606082015260c08101835160051b6020018082828760045afa50503d60a00160808301523d60a0013d0160a08301523d81019050835181523d81016020820191505b80821461311557600182528160200191506130ff565b835160200191508181838660045afa50602083601c85013d840103601c860160008a5af161314c573d1561314c573d6000843e3d83fd5b5050805163bc197c8160e01b14611ca057639c05499b6000526004601cfd5b600033736bc558a6dc48defa0e7022713c23d65ab26e4fa71461318d57503390565b503290565b60006001600160e01b0319821663152a902d60e11b14806114b757506301ffc9a760e01b6001600160e01b03198316146114b7565b806131dd6131d3612eee565b6003018585613280565b5560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b60006114b761322b612eee565b600201600160201b8410840261467b565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480613257575050819003601f19909101908152919050565b602890815260149190915260009182526048822091905290565b6001600160a01b0383166132c157604051633a954ecd60e21b815260040160405180910390fd5b60006132cb612eee565b8054909150600160201b900463ffffffff166132fa5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b0385811660009081526005830160209081526040808320938816835280832081516101008101835284815292830184905260808301849052606060a0840181905260c0840181905260e084018190528554600160801b80820463ffffffff90811695870195909552835404909316908401529290600160a01b90046001600160601b0316808811156133a657604051631e9acf1760e31b815260040160405180910390fd5b84546001600160601b0391899003828116600160a01b9081026001600160a01b03938416178855865481810485168c019485169091029216919091178555604084015191925090613411906133f9612f33565b83816134075761340761570e565b0480821191030290565b835261341c89611a22565b61347557886001600160a01b03168a6001600160a01b03160361344757825160408401510360608401525b61346f613452612f33565b83816134605761346061570e565b04846060015180821191030290565b60208401525b505061347f600190565b156136445760006134b182604001516134a684600001518560200151808218908211021890565b808218908211021890565b9050806134be5750613644565b8151819003825260208201805182900390526001600160a01b03808916908a16036134f3576060820180519091019052613644565b6134fc81611467565b60a08301526001600160a01b038981166000908152600487016020526040808220928b16825290819020865491850180518590039081905263ffffffff60801b198316600160801b63ffffffff9283160217885586549192600160601b9081900482169204168181108183180218865460608701805187019081905263ffffffff908116600160801b0263ffffffff60801b1993909116600160601b0292909216600160601b600160a01b0319909116171786555b6135bb8382614694565b9050806135df5787546135dc908490600160201b900463ffffffff16614694565b90505b6135eb83826000612f0f565b6135f782826001612f0f565b6136058560a001518261476c565b60001990930192836135b157865463ffffffff60601b1916600160601b63ffffffff83160217875560a085015161363f908d908d90613001565b505050505b80511561373d57805161365681611467565b60c08301526001600160a01b03891660009081526004860160205260409081902090830151855463ffffffff918490038216600160801b0263ffffffff60801b1990911617808755600160601b9004165b6136b18282614694565b9050806136d55786546136d2908390600160201b900463ffffffff16614694565b90505b6136e182826000612f0f565b6136f087600201826000612f0f565b6136fe8460c001518261476c565b60001990920191826136a757855463ffffffff60601b1916600160601b63ffffffff83160217865560c0840151613739908c90600090613001565b5050505b6020810151156138ba57602081015161375581611467565b60e08301526001600160a01b038816600090815260048601602052604081206060840151855463ffffffff60801b1916600160801b91850163ffffffff1691909102178555906137a3612f33565b8754600160401b90046001600160601b0316816137c2576137c261570e565b8854875492909104925063ffffffff90811683811180159091021791600160601b9004165b6137f4896002018361467b565b156138205761381961380d8a6002018460010186614781565b84811180159091021790565b91506137e7565b61382f89600201836001612f0f565b61383b84836001612f0f565b81811082821802811890506138548660e001518361476c565b60018201838111801590910217600019909501949150846137e757865463ffffffff60601b1916600160601b63ffffffff83811691909102919091178855895463ffffffff191690831617895560e08601516138b4906000908e90613001565b50505050505b856000528660601b60601c8860601b60601c600080516020615f4483398151915260206000a3863b15613908576138f788888360a0015188613098565b6139086000888360e0015188613098565b5050505050505050565b600061391c612eee565b9050613945600183811084821802188254600160201b900463ffffffff16808211908218021890565b6001600160a01b0384166000818152600584016020908152604091829020805463ffffffff60601b1916600160601b63ffffffff871602179055905183815292945090917ff2e9b90ef293d49024cb7f7f9fc4b87a50aca7b77895c750060efc5067161d61910160405180910390a2505050565b60006139c3612eee565b6001600160a01b0384166000818152600592909201602090815260408320805486158015865291955060ff600160581b9091049081166002918216159092180218600117927f0b5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6693939190a2815460ff909116600160581b0260ff60581b199091161790555050565b604051663337b932bb32b960c91b6020820152602701604051602081830303815290604052805190602001208282604051602001613a88929190615d9a565b604051602081830303815290604052805190602001201461145057604051635ee88f9760e01b815260040160405180910390fd5b60d98054600190921b9091179055565b6127106001600160601b0382161115613b3a5760405162461bcd60e51b815260206004820152602a60248201527f455243323938313a20726f79616c7479206665652077696c6c206578636565646044820152692073616c65507269636560b01b6064820152608401612ac5565b6001600160a01b038216613b8c5760405162461bcd60e51b815260206004820152601960248201527822a921991c9c189d1034b73b30b634b2103932b1b2b4bb32b960391b6044820152606401612ac5565b604080518082019091526001600160a01b039092168083526001600160601b039091166020909201829052600160a01b90910217609755565b613bcd61316b565b6001600160a01b0316613bde6122a7565b6001600160a01b0316146120db5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401612ac5565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000805b60088160ff161015613d16576000613ca382602061598a565b60ff1684901c63ffffffff8116915060101c61ffff16816000829003613ccb57505050613d16565b8161ffff168710613d00578061ffff168261ffff1688613ceb9190615724565b613cf591906156f7565b9450505050506114b7565b5050508080613d0e906159ad565b915050613c8a565b5060009392505050565b6001600160a01b038316613d4757604051633a954ecd60e21b815260040160405180910390fd5b6000613d51612eee565b8054909150600160201b900463ffffffff16613d805760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03841660009081526005820160209081526040808320815180830190925292815260609181019190915281546001600160601b03600160a01b80830482168801918216026001600160a01b03909216919091178355613de4612f33565b8181613df257613df261570e565b0482525082546001600160601b03600160401b8083048216888101928316909102600160401b600160a01b0319909316929092178555600091829182613e3a6104b98361487c565b9050898210811715613e5f5760405163e5cfe95760e01b815260040160405180910390fd5b613e67612f33565b8281613e7557613e7561570e565b049350613ea1613e83612f33565b8481613e9157613e9161570e565b0460010185811180159091021790565b8854600160201b80820463ffffffff9081168881108982180218160263ffffffff60201b199091161789559450613edd92508a9150611a229050565b61401e5782518454613f0291600160801b90910463ffffffff16808203911102611467565b60208401819052511561401e576001600160a01b03881660009081526004860160209081526040909120855491850151519091600160601b900463ffffffff16905b613f51886002018661467b565b15613f7d57613f76613f6a896002018760010187614781565b85811180159091021790565b9450613f44565b613f8c88600201866001612f0f565b613f9883866001612f0f565b8482108583180282189150613fb186602001518661476c565b6001850184811180159091021794506000190180613f4457508554855163ffffffff908116600160801b0263ffffffff60801b19918416600160601b0291909116600160601b600160a01b031990921691909117178655602085015161401b906000908c90613001565b50505b505060008581526001600160a01b03871690600080516020615f44833981519152602082a3853b15612cd157612cd1600087836020015187613098565b600061406786886158da565b90506140b46040518060c0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b8835600090815260cf6020526040812054908181036140d45760006140f6565b60cc60006140e360018561591c565b60001b8152602001908152602001600020545b90506040518060c0016040528061410b6122a7565b6001600160a01b03168152602001886001600160a01b0316815260200185815260200189815260200160cc60008e6000013581526020019081526020016000205481526020018281525092505050600073d4e182124131fe5f3bde4cdef00975fb97f5b3d863704c38fc8c60d3600101601e9054906101000a900461ffff168c866080015160006001600160a01b031688602001516001600160a01b031614156040518663ffffffff1660e01b81526004016141cb95949392919061592f565b602060405180830381865af41580156141e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061420c9190615958565b604051631306dbef60e21b815290915073d4e182124131fe5f3bde4cdef00975fb97f5b3d890634c1b6fbc90614256908e9060d3908f9060cb908c908c908b908b90600401615dd3565b60006040518083038186803b15801561426e57600080fd5b505af4158015614282573d6000803e3d6000fd5b50505060018c015463ffffffff600160801b82048116600160a01b9092041610159050614300578260cb60006142b661316b565b6001600160a01b03166001600160a01b0316815260200190815260200160002060008c60000135815260200190815260200160002060008282546142fa91906158da565b90915550505b60018b015463ffffffff600160801b90910481161015614340578935600090815260cc60205260408120805485929061433a9084906158da565b90915550505b8260d2600082825461435291906158da565b909155505060405163032cef8f60e41b8152600481018c905260d3602482015260cd604482015260ce60648201526001600160a01b038716608482015260a481018a90526001600160801b03821660c482015273d4e182124131fe5f3bde4cdef00975fb97f5b3d8906332cef8f09060e40160006040518083038186803b1580156143dc57600080fd5b505af41580156143f0573d6000803e3d6000fd5b50505050806001600160801b03163411156144285761442861441061316b565b6144236001600160801b0384163461591c565b6148a0565b5050505050505050505050565b80151561444d614443612eee565b6001018585613280565b5560008181526001600160a01b0380841691908516907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190602090a3505050565b6000614498612eee565b8054909150600160201b900463ffffffff16156144c857604051633ab534b960e21b815260040160405180910390fd5b6144d0612f33565b83816144de576144de61570e565b825463ffffffff60201b1916600160201b92909104600190811763ffffffff16929092021782556001600160601b0390614516612f33565b03106145355760405163265f13bd60e21b815260040160405180910390fd5b805463ffffffff191660011781558215611221576001600160a01b03821661457057604051633a954ecd60e21b815260040160405180910390fd5b6145798361487c565b156145975760405163e5cfe95760e01b815260040160405180910390fd5b8054600160401b600160a01b031916600160401b6001600160601b0385169081029190911782556001600160a01b038381166000818152600585016020908152604082208054909416600160a01b9095029490941783558681529192909190600080516020615f448339815191529082a36125df8360016139b9565b600054610100900460ff1661463a5760405162461bcd60e51b8152600401612ac590615ef8565b6120db614914565b600061464c612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160801b900463ffffffff1690565b60609190911b600882901c0154600160ff9092161c1690565b60008260601b8260081c81018054841960ff161b841960ff161c82821481176146cf575b5060001901600081905280548282148117156146b8575b8015614763577b01c1818141808141018080c0814100c04181408140c0c100414140c160221b6f8421084210842108cc6318c6db6d54be6001600160801b03831160071b83811c6001600160401b031060061b1783811c63ffffffff1060051b1783811c61ffff1060041b1783811c60ff1060031b1783811c9190911c601f169190911a1783830360081b17858111150293505b50505092915050565b601f1990910180519182526020919091019052565b6000801990508360601b8360081c81018054198560ff161c8560ff161b806147d6578460081c83015b60018301925082541991508083118217156147aa57808311156147d45760ff86191691821b90911c905b505b80156148725782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166101e07a1412563212c14164235266736f7425221143267a4524367526767760fc7b2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff840260f81c161b60f71c1690811c63d76453e004601f169190911a1717858111878210176000031793505b5050509392505050565b600080614887612f33565b606084901c930463fffffffe1092909217151592915050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146148ed576040519150601f19603f3d011682016040523d82523d6000602084013e6148f2565b606091505b5050905080611221576040516312171d8360e31b815260040160405180910390fd5b600054610100900460ff1661493b5760405162461bcd60e51b8152600401612ac590615ef8565b6120db61494661316b565b613c34565b634e487b7160e01b600052604160045260246000fd5b60405161016081016001600160401b03811182821017156149845761498461494b565b60405290565b604051601f8201601f191681016001600160401b03811182821017156149b2576149b261494b565b604052919050565b80356001600160801b03811681146149d157600080fd5b919050565b63ffffffff8116811461198b57600080fd5b80356149d1816149d6565b6001600160a01b038116811461198b57600080fd5b80356149d1816149f3565b801515811461198b57600080fd5b80356149d181614a13565b60006101608284031215614a3f57600080fd5b614a47614961565b9050614a52826149ba565b8152614a60602083016149ba565b6020820152614a71604083016149ba565b6040820152614a82606083016149e8565b6060820152614a93608083016149e8565b6080820152614aa460a083016149e8565b60a0820152614ab560c083016149e8565b60c0820152614ac660e083016149e8565b60e0820152610100614ad98184016149e8565b90820152610120614aeb838201614a08565b90820152610140614afd838201614a21565b9082015292915050565b60008060006101a08486031215614b1d57600080fd5b8335925060208401359150614b358560408601614a2c565b90509250925092565b600060208284031215614b5057600080fd5b81356001600160e01b031981168114614b6857600080fd5b9392505050565b60005b83811015614b8a578181015183820152602001614b72565b50506000910152565b60008151808452614bab816020860160208601614b6f565b601f01601f19169290920160200192915050565b6020815260006114b46020830184614b93565b60008060408385031215614be557600080fd5b8235614bf0816149f3565b946020939093013593505050565b61ffff8116811461198b57600080fd5b600060208284031215614c2057600080fd5b8135614b6881614bfe565b600060208284031215614c3d57600080fd5b5035919050565b60008060408385031215614c5757600080fd5b8235614c62816149f3565b91506020830135614c72816149f3565b809150509250929050565b600080600060608486031215614c9257600080fd5b8335614c9d816149f3565b92506020840135614cad816149f3565b929592945050506040919091013590565b600060208284031215614cd057600080fd5b8135614b68816149f3565b60008060408385031215614cee57600080fd5b50508035926020909101359150565b6001600160a01b03929092168252602082015260400190565b600060208284031215614d2857600080fd5b8135614b6881614a13565b60008083601f840112614d4557600080fd5b5081356001600160401b03811115614d5c57600080fd5b602083019150836020828501011115611b2457600080fd5b60008060208385031215614d8757600080fd5b82356001600160401b03811115614d9d57600080fd5b614da985828601614d33565b90969095509350505050565b60008060408385031215614dc857600080fd5b8235614dd3816149f3565b91506020830135614c7281614bfe565b600060408284031215611a6d57600080fd5b600080600080600060808688031215614e0d57600080fd5b85356001600160401b0380821115614e2457600080fd5b614e3089838a01614de3565b96506020880135955060408801359150614e49826149f3565b90935060608701359080821115614e5f57600080fd5b50614e6c88828901614d33565b969995985093965092949392505050565b600082601f830112614e8e57600080fd5b81356001600160401b03811115614ea757614ea761494b565b614eba601f8201601f191660200161498a565b818152846020838601011115614ecf57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614efe57600080fd5b81356001600160401b03811115614f1457600080fd5b614f2084828501614e7d565b949350505050565b60006001600160401b03821115614f4157614f4161494b565b5060051b60200190565b60006020808385031215614f5e57600080fd5b82356001600160401b03811115614f7457600080fd5b8301601f81018513614f8557600080fd5b8035614f98614f9382614f28565b61498a565b81815260059190911b82018301908381019087831115614fb757600080fd5b928401925b82841015614fde578335614fcf816149f3565b82529284019290840190614fbc565b979650505050505050565b60008083601f840112614ffb57600080fd5b5081356001600160401b0381111561501257600080fd5b6020830191508360208260061b8501011115611b2457600080fd5b60008060008060008587036101c081121561504757600080fd5b8635955060208701359450610160603f198201121561506557600080fd5b506040860192506101a08601356001600160401b0381111561508657600080fd5b614e6c88828901614fe9565b60008060008385036101408112156150a957600080fd5b8435935060208501359250610100603f19820112156150c757600080fd5b506040840190509250925092565b6000602082840312156150e757600080fd5b8135614b68816149d6565b60006101208083526151068184018d614b93565b6001600160a01b039b909b166020840152505063ffffffff978816604082015295909616606086015261ffff938416608086015291831660a0850152821660c0840152811660e083015290911661010090910152919050565b60008060008060008060a0878903121561517857600080fd5b86356001600160401b038082111561518f57600080fd5b61519b8a838b01614de3565b975060208901359650604089013591506151b4826149f3565b9094506060880135906151c6826149f3565b909350608088013590808211156151dc57600080fd5b506151e989828a01614d33565b979a9699509497509295939492505050565b60008083601f84011261520d57600080fd5b5081356001600160401b0381111561522457600080fd5b6020830191508360208260051b8501011115611b2457600080fd5b6000806020838503121561525257600080fd5b82356001600160401b0381111561526857600080fd5b614da9858286016151fb565b6000806040838503121561528757600080fd5b8235615292816149f3565b91506020830135614c7281614a13565b600080600080608085870312156152b857600080fd5b84356152c3816149f3565b935060208501356152d3816149f3565b92506040850135915060608501356001600160401b038111156152f557600080fd5b61530187828801614e7d565b91505092959194509250565b60008060008060008060008060a0898b03121561532957600080fd5b88356001600160401b038082111561534057600080fd5b61534c8c838d01614de3565b995060208b013591508082111561536257600080fd5b61536e8c838d016151fb565b909950975060408b013591508082111561538757600080fd5b6153938c838d016151fb565b909750955060608b013591506153a8826149f3565b90935060808a013590808211156153be57600080fd5b506153cb8b828c01614d33565b999c989b5096995094979396929594505050565b6000806000606084860312156153f457600080fd5b8335925060208401359150604084013561540d81614a13565b809150509250925092565b6000806000806080858703121561542e57600080fd5b8435615439816149f3565b935060208581013561544a816149f3565b935060408601356001600160401b038082111561546657600080fd5b818801915088601f83011261547a57600080fd5b8135615488614f9382614f28565b81815260059190911b8301840190848101908b8311156154a757600080fd5b938501935b828510156154c5578435825293850193908501906154ac565b9650505060608801359250808311156154dd57600080fd5b505061530187828801614e7d565b60008060006040848603121561550057600080fd5b8335925060208401356001600160401b0381111561551d57600080fd5b61552986828701614fe9565b9497909650939450505050565b600080600080600085870361016081121561555057600080fd5b86356001600160401b038082111561556757600080fd5b6155738a838b01614e7d565b9750602089013591508082111561558957600080fd5b6155958a838b01614e7d565b965060408901359150808211156155ab57600080fd5b508701610120818a0312156155bf57600080fd5b935060e0605f19820112156155d357600080fd5b506060860191506155e76101408701614a08565b90509295509295909350565b60006020828403121561560557600080fd5b8151614b6881614a13565b600181811c9082168061562457607f821691505b602082108103611a6d57634e487b7160e01b600052602260045260246000fd5b600080845461565281615610565b6001828116801561566a576001811461567f576156ae565b60ff19841687528215158302870194506156ae565b8860005260208060002060005b858110156156a55781548a82015290840190820161568c565b50505082870194505b5050505083516156c2818360208801614b6f565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176114b7576114b76156e1565b634e487b7160e01b600052601260045260246000fd5b6000826157335761573361570e565b500490565b601f82111561122157600081815260208120601f850160051c8101602086101561575f5750805b601f850160051c820191505b81811015612cd15782815560010161576b565b600019600383901b1c191660019190911b1790565b81516001600160401b038111156157ac576157ac61494b565b6157c0816157ba8454615610565b84615738565b602080601f8311600181146157ef57600084156157dd5750858301515b6157e7858261577e565b865550612cd1565b600085815260208120601f198616915b8281101561581e578886015182559484019460019091019084016157ff565b508582101561583c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600081518084526020808501945080840160005b838110156158855781516001600160a01b031687529582019590820190600101615860565b509495945050505050565b84815283602082015260018060a01b0383166040820152608060608201526000612822608083018461584c565b600061016082840312156158d057600080fd5b6114b48383614a2c565b808201808211156114b7576114b76156e1565b6000602082840312156158ff57600080fd5b6114b4826149ba565b6000826159175761591761570e565b500690565b818103818111156114b7576114b76156e1565b94855261ffff939093166020850152604084019190915260608301521515608082015260a00190565b60006020828403121561596a57600080fd5b5051919050565b60ff82811682821603908111156114b7576114b76156e1565b60ff81811683821602908116908181146159a6576159a66156e1565b5092915050565b600060ff821660ff81036159c3576159c36156e1565b60010192915050565b6001600160401b038311156159e3576159e361494b565b6159f7836159f18354615610565b83615738565b6000601f841160018114615a255760008515615a135750838201355b615a1d868261577e565b845550611ca0565b600083815260209020601f19861690835b82811015615a565786850135825560209485019460019092019101615a36565b5086821015615a735760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b600081356114b7816149f3565b80546001600160a01b0319166001600160a01b0392909216919091179055565b600081356114b7816149d6565b600081356114b781614bfe565b805463ffff00008360101b1663ffff0000198216178255505050565b805461ffff60201b191660209290921b61ffff60201b16919091179055565b8135601e19833603018112615b1b57600080fd5b820180356001600160401b03811115615b3357600080fd5b602082019150803603821315615b4857600080fd5b615b538183856159cc565b505060018101615b6e615b6860208501615a85565b82615a92565b615ba1615b7d60408501615ab2565b82805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b615bd4615bb060608501615ab2565b82805463ffffffff60c01b191660c09290921b63ffffffff60c01b16919091179055565b615c03615be360808501615abf565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b615c36615c1260a08501615abf565b8280546001600160f01b031660f09290921b6001600160f01b031916919091179055565b5060028101615c5d615c4a60c08501615abf565b825461ffff191661ffff91909116178255565b615c72615c6c60e08501615abf565b82615acc565b611221615c826101008501615abf565b82615ae8565b61ffff8181168382160190808211156159a6576159a66156e1565b8135615cae81614bfe565b815461ffff191661ffff8216178255506020820135615ccc81614bfe565b615cd68183615acc565b506040820135615ce581614bfe565b615cef8183615ae8565b506060820135615cfe81614bfe565b815461ffff60301b19811660309290921b61ffff60301b1691821783556080840135615d29816149f3565b600160301b600160e01b03199190911690911760409190911b600160401b600160e01b0316178155615d69615d6060a08401615a85565b60018301615a92565b611450615d7860c08401615a85565b60028301615a92565b828152604060208201526000614f20604083018461584c565b8183823760009101908152919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006101808a8352896020840152806040840152883581840152506020880135601e19893603018112615e0557600080fd5b88016020810190356001600160401b03811115615e2157600080fd5b8060051b803603831315615e3457600080fd5b60406101a08601526101c085018290526101e06001600160fb1b03831115615e5b57600080fd5b81848288013781860193508a606087015280868503016080870152615e838185018a8c615daa565b88516001600160a01b0390811660a08981019190915260208b015190911660c089015260408a015160e089015260608a015161010089015260808a01516101208901528901516101408801529450615edb9350505050565b6001600160801b0383166101608301529998505050505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122021f93e85066bebf74ccd8699a0d7668cf79e75e8b6518bcf716582c3f584590664736f6c63430008140033
Deployed Bytecode
0x6080604052600436106102f85760003560e01c806301d88f5d1461053f57806301ffc9a71461055f57806306fdde0314610594578063095ea7b3146105b65780630c6f910b146105d65780630e89341c146105f657806315ec67201461061657806318160ddd1461062b57806321d5bf241461064e57806323b872dd146106b5578063274e430b146106d557806327a59437146106f557806329a96532146107345780632a55205a146107545780632a6a935d14610782578063313ce567146107a257806332f7c6d4146107be5780633ccfd60b146107de5780633e100f6e146107f35780634331f639146108135780634a21a2df146108335780634bde38c8146108465780634f558e791461088157806351305a82146108a157806355f804b3146108ce5780635ecb16cd146108ee5780635fb8ecfb1461090e57806363dbc71f1461092e57806364cc4aa51461094e5780636e884900146109645780636f33659f1461098457806370a08231146109a4578063715018a6146109c457806379502c55146109d95780637c5d0a0814610a03578063818d4b5d14610a235780638b4795d614610a3e5780638da5cb5b14610a5e5780639564e8e414610a7357806395d89b4114610a8657806396a0924e14610a9b578063978a450914610abb5780639a7a973c14610b055780639c8770b714610b32578063a22cb46514610b52578063a3edb86a14610b72578063a5aa4aa414610bb5578063a9059cbb14610cd1578063aab6a17b14610cf1578063ac17811c14610d11578063b68836fa14610d26578063bcb40d5414610d53578063bcc1ed0714610d68578063bedcf00314610d7b578063caf3e53214610dc1578063d404844114610de1578063db8a560014610e89578063db8f6ec414610ea9578063dd62ed3e14610ec9578063de6cd0db14610ee9578063e2f2379a14610f09578063e985e9c514610f29578063ead0055314610f49578063f2fde38b14610f69578063f588eb5014610f89578063f5b100ea14610fa9578063f9da322414610fc95761030a565b3661030a57341561030857600080fd5b005b60003560e01c63f242432a8190036103655760643560011461033f57604051631ec9b93b60e21b815260040160405180910390fd5b61035b336004356024356044356103566084610fe9565b61101e565b6103656001611245565b80632eb2c2d60361041f57600061037c604461124f565b9050600061038a606461124f565b825181519192509081146103b157604051631dc0052360e11b815260040160405180910390fd5b600019810190156103f1576103cc828260051b016020015190565b6001146103ec57604051631ec9b93b60e21b815260040160405180910390fd5b6103b1565b50506104133361040060043590565b6024358461040e6084610fe9565b61127e565b61041d6001611245565b505b80634e1273f4036104e8576000610436600461124f565b90506000610444602461124f565b8051835191925090811461046b57604051631dc0052360e11b815260040160405180910390fd5b600061047682611467565b90505b600019820191156104d4576000610496858460051b016020015190565b90506104ce82846104be6104b9856104b48a8a60051b016020015190565b611485565b151590565b808260051b846020010152505050565b50610479565b602080820352805160051b60400160208203f35b8062fdd58e03610511576000610502600435602435611485565b905061050f811515611245565b505b80630e0b098403610526576105266001611245565b604051631e085ca760e11b815260040160405180910390fd5b34801561054b57600080fd5b5061030861055a366004614b07565b6114bd565b34801561056b57600080fd5b5061057f61057a366004614b3e565b61173f565b60405190151581526020015b60405180910390f35b3480156105a057600080fd5b506105a9611776565b60405161058b9190614bbf565b3480156105c257600080fd5b5061057f6105d1366004614bd2565b611808565b3480156105e257600080fd5b506103086105f1366004614c0e565b61181e565b34801561060257600080fd5b506105a9610611366004614c2b565b6118a7565b34801561062257600080fd5b5061030861192b565b34801561063757600080fd5b5061064061198e565b60405190815260200161058b565b34801561065a57600080fd5b5061069d610669366004614c44565b6001600160a01b03918216600090815260ce602090815260408083209390941682529190915220546001600160801b031690565b6040516001600160801b03909116815260200161058b565b3480156106c157600080fd5b5061057f6106d0366004614c7d565b6119ae565b3480156106e157600080fd5b5061057f6106f0366004614cbe565b611a22565b34801561070157600080fd5b5061069d610710366004614cbe565b6001600160a01b0316600090815260cd60205260409020546001600160801b031690565b34801561074057600080fd5b5061030861074f366004614c2b565b611a73565b34801561076057600080fd5b5061077461076f366004614cdb565b611a7d565b60405161058b929190614cfd565b34801561078e57600080fd5b5061057f61079d366004614d16565b611b2b565b3480156107ae57600080fd5b506040516012815260200161058b565b3480156107ca57600080fd5b506103086107d9366004614d74565b611b3f565b3480156107ea57600080fd5b50610308611b93565b3480156107ff57600080fd5b5061064061080e366004614cbe565b611bf3565b34801561081f57600080fd5b5061030861082e366004614db5565b611c2c565b610308610841366004614df5565b611c8b565b34801561085257600080fd5b507386b82972282dd22348374bc63fd21620f7ed847b5b6040516001600160a01b03909116815260200161058b565b34801561088d57600080fd5b5061057f61089c366004614c2b565b611ca7565b3480156108ad57600080fd5b506106406108bc366004614c2b565b60cf6020526000908152604090205481565b3480156108da57600080fd5b506103086108e9366004614eec565b611cb2565b3480156108fa57600080fd5b50610308610909366004614f4b565b611d22565b34801561091a57600080fd5b5061030861092936600461502d565b611d94565b34801561093a57600080fd5b50610308610949366004614cdb565b611df3565b34801561095a57600080fd5b5061064060d95481565b34801561097057600080fd5b5061030861097f366004615092565b611ebf565b34801561099057600080fd5b5061030861099f3660046150d5565b612027565b3480156109b057600080fd5b506106406109bf366004614cbe565b61208d565b3480156109d057600080fd5b506103086120c9565b3480156109e557600080fd5b506109ee6120dd565b60405161058b999897969594939291906150f2565b348015610a0f57600080fd5b50610308610a1e366004614d74565b6121c9565b348015610a2f57600080fd5b5061057f6104b4366004614bd2565b348015610a4a57600080fd5b50610308610a59366004614c0e565b61221d565b348015610a6a57600080fd5b506108696122a7565b610308610a8136600461515f565b6122b6565b348015610a9257600080fd5b506105a961238f565b348015610aa757600080fd5b50610308610ab6366004614c0e565b61239e565b348015610ac757600080fd5b5061069d610ad6366004614cbe565b6001600160a01b0316600090815260ce602090815260408083208380529091529020546001600160801b031690565b348015610b1157600080fd5b50610640610b20366004614c2b565b600090815260cc602052604090205490565b348015610b3e57600080fd5b50610308610b4d36600461523f565b6123fe565b348015610b5e57600080fd5b50610308610b6d366004615274565b6125aa565b348015610b7e57600080fd5b50610640610b8d366004614bd2565b6001600160a01b0391909116600090815260cb60209081526040808320938352929052205490565b348015610bc157600080fd5b50610c5a610bd0366004614c2b565b60c9602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216939181169263ffffffff928204831692600160a01b8304811692600160c01b8104821692600160e01b9182900483169281811692600160201b8304909116916001600160a01b03600160401b8204169160ff9104168b565b604080516001600160801b039c8d1681529a8c1660208c015298909a169789019790975263ffffffff9586166060890152938516608088015291841660a0870152831660c0860152821660e0850152166101008301526001600160a01b03166101208201529015156101408201526101600161058b565b348015610cdd57600080fd5b5061057f610cec366004614bd2565b6125b5565b348015610cfd57600080fd5b50610308610d0c3660046152a2565b6125d2565b348015610d1d57600080fd5b506106406125e5565b348015610d3257600080fd5b50610640610d41366004614c2b565b60ca6020526000908152604090205481565b348015610d5f57600080fd5b5060d254610640565b610308610d7636600461530d565b612609565b348015610d8757600080fd5b506000805260cd6020527fcd565b10a72538d86f6d352f37ebc5dff31587960b12c0afe00fd03947a6932a546001600160801b031661069d565b348015610dcd57600080fd5b50610640610ddc3660046153df565b61277a565b348015610ded57600080fd5b5060d65460d75460d854610e3a9261ffff80821693620100008304821693600160201b8404831693600160301b8104909316926001600160a01b03600160401b9091048116928116911687565b6040805161ffff98891681529688166020880152948716948601949094529190941660608401526001600160a01b039384166080840152831660a08301529190911660c082015260e00161058b565b348015610e9557600080fd5b50610308610ea4366004615418565b61282c565b348015610eb557600080fd5b50610308610ec43660046154eb565b612839565b348015610ed557600080fd5b50610640610ee4366004614c44565b6129b9565b348015610ef557600080fd5b50610308610f04366004614d74565b6129d8565b348015610f1557600080fd5b50610308610f24366004615536565b612a2c565b348015610f3557600080fd5b5061057f610f44366004614c44565b612cd9565b348015610f5557600080fd5b50610308610f64366004614cbe565b612cfa565b348015610f7557600080fd5b50610308610f84366004614cbe565b612d80565b348015610f9557600080fd5b50610308610fa4366004614f4b565b612df6565b348015610fb557600080fd5b50610640610fc4366004614cbe565b612e30565b348015610fd557600080fd5b50610308610fe43660046150d5565b612e3b565b606060405190508135600401803580835280602083018460200137808360200101915050600081528060200160405250919050565b6001600160a01b03831661104557604051633a954ecd60e21b815260040160405180910390fd5b600061104f612eee565b8054909150600160201b900463ffffffff1661107e5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03868116801591871614176110bb5761109e8587612cd9565b6110bb5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b038516600090815260048201602052604090206110df8185612efb565b6110fb5760405162a1148160e81b815260040160405180910390fd5b61110781856000612f0f565b6001600160a01b0385166000908152600483016020526040902061112d90856001612f0f565b6000611137612f33565b90506000836005016000896001600160a01b03166001600160a01b0316815260200190815260200160002090506000846005016000896001600160a01b03166001600160a01b03168152602001908152602001600020905060018360201b1760801b80835403835581548060601c63ffffffff16808a18818b110260601b8218830184555050508860601b60601c98508760601b60601c97508660005260016020528789337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260406000a4826000528789600080516020615f4483398151915260206000a3611226565b505050565b873b156112395761123989898989612f73565b50505050505050505050565b8060005260206000f35b60606040519050813560040180358083528060051b60208301846020013760051b820160200160405250919050565b6001600160a01b0383166112a557604051633a954ecd60e21b815260040160405180910390fd5b60006112af612eee565b8054909150600160201b900463ffffffff166112de5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b038681168015918716141761131b576112fe8587612cd9565b61131b5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b0380861660009081526005830160205260408082209287168252812085519192839290919061134f612f33565b6001600160a01b03808c166000908152600489016020526040808220928d16825290209183029650905b82156113e8576000199092019160006113988b8560051b016020015190565b90506113a48382612efb565b6113c05760405162a1148160e81b815260040160405180910390fd5b6113cc83826000612f0f565b6113d882826001612f0f565b8681189087100290951894611379565b50505086518460201b1760801b80835403835581548060601c63ffffffff168086188187110260601b821883018455505050611425898989613001565b836000528760601b60601c8960601b60601c600080516020615f4483398151915260206000a3611454565b5050565b873b156112395761123989898989613098565b6040805180820181526020810183815260059390931b018101905290565b60006114b4611492612eee565b6001600160a01b03851660009081526004919091016020526040902083612efb565b90505b92915050565b6114c56122a7565b6001600160a01b03166114d661316b565b6001600160a01b0316146114fd576040516330cd747160e01b815260040160405180910390fd5b6101208101516001600160a01b0316156115c25761012081015160405163095ea7b360e01b81526000916001600160a01b03169063095ea7b39061155d9073aafdfa4a935d8511bf285af11a0544ce7e4a11999060001990600401614cfd565b6020604051808303816000875af115801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a091906155f3565b9050806115c0576040516302df483560e21b815260040160405180910390fd5b505b428160a0015163ffffffff1610156115e15763ffffffff421660a08201525b600083815260c9602090815260408083208451928501516001600160801b03938416600160801b918516820217825585830151600183018054606089015160808a015160a08b015160c08c0151959099166001600160a01b03199093169290921763ffffffff91821690950294909417600160a01b600160e01b031916600160a01b9185169190910263ffffffff60c01b191617600160c01b96841696909602959095176001600160e01b0316600160e01b91831682021790945560e0860151600290920180546101008801516101208901516101408a01519585166001600160401b031990931692909217600160201b919094160292909217600160401b600160e81b031916600160401b6001600160a01b039093169290920260ff60e01b1916919091179115159093021790915551839185917fe9a0c17645ed78ccc9996259f00297ffc75e6b9d22cd605ccc9992cc8ca3f4c19190a3505050565b6000611767826301ffc9a760e09190911c90811463d9b67a26821417630e89341c9091141790565b806114b757506114b782613192565b606060d0805461178590615610565b80601f01602080910402602001604051908101604052809291908181526020018280546117b190615610565b80156117fe5780601f106117d3576101008083540402835291602001916117fe565b820191906000526020600020905b8154815290600101906020018083116117e157829003601f168201915b5050505050905090565b60006118153384846131c7565b50600192915050565b6118266122a7565b6001600160a01b031661183761316b565b6001600160a01b03161461185e576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115611885576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160e01b0261ffff60e01b19909216919091179055565b60606118b28261321e565b6118cf57604051630a14c4b560e41b815260040160405180910390fd5b60d380546118dc90615610565b90506000036118fa57604051806020016040528060008152506114b7565b60d36119058361323c565b604051602001611916929190615644565b60405160208183030381529060405292915050565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611962576119626156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81612df6565b50565b6000611998612eee565b54600160401b90046001600160601b0316919050565b6000806119c66119bc612eee565b6003018633613280565b805490915060001981146119fb57808411156119f5576040516313be252b60e01b815260040160405180910390fd5b83810382555b611a168686866040518060200160405280600081525061329a565b50600195945050505050565b600080611a2d612eee565b6001600160a01b03841660009081526005919091016020526040902054600160581b9004600281161515925060ff81169150600116611a6d57823b151591505b50919050565b61198b3382613912565b60008281526098602090815260408083208151808301909252546001600160a01b038116808352600160a01b9091046001600160601b0316928201929092528291611af25750604080518082019091526097546001600160a01b0381168252600160a01b90046001600160601b031660208201525b602081015160009061271090611b11906001600160601b0316876156f7565b611b1b9190615724565b91519350909150505b9250929050565b6000611b3733836139b9565b506001919050565b611b476122a7565b6001600160a01b0316611b5861316b565b6001600160a01b031614611b7f576040516330cd747160e01b815260040160405180910390fd5b611b898282613a49565b6114506002613abc565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611bca57611bca6156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81611d22565b6000611bfd612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160601b900463ffffffff1690565b611c346122a7565b6001600160a01b0316611c4561316b565b6001600160a01b031614611c6c576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff191661ffff8316908117909155611450908390613acc565b611ca08585611c9861316b565b8686866122b6565b5050505050565b60006114b78261321e565b611cba6122a7565b6001600160a01b0316611ccb61316b565b6001600160a01b031614611cf2576040516330cd747160e01b815260040160405180910390fd5b60d95460011615611d165760405163249fab5d60e01b815260040160405180910390fd5b60d36114508282615793565b73d4e182124131fe5f3bde4cdef00975fb97f5b3d8638e1ab86660d660cd611d486122a7565b856040518563ffffffff1660e01b8152600401611d689493929190615890565b60006040518083038186803b158015611d8057600080fd5b505af4158015611ca0573d6000803e3d6000fd5b611d9c6122a7565b6001600160a01b0316611dad61316b565b6001600160a01b031614611dd4576040516330cd747160e01b815260040160405180910390fd5b611ddf858383612839565b611ca0858561055a368790038701876158bd565b611dfb6122a7565b6001600160a01b0316611e0c61316b565b6001600160a01b031614611e33576040516330cd747160e01b815260040160405180910390fd5b600081815260c960205260408082206001908101548584529190922090910154600160801b9182900463ffffffff908116929091041614611e87576040516306b7c75960e31b815260040160405180910390fd5b611e928160016158da565b600083815260cf6020526040902055611eac8260016158da565b600091825260cf60205260409091205550565b611ec76122a7565b6001600160a01b0316611ed861316b565b6001600160a01b031614611eff576040516330cd747160e01b815260040160405180910390fd5b6112218383604051806101600160405280856000016020810190611f2391906158ed565b6001600160801b03168152602090810190611f40908701876158ed565b6001600160801b0316815260200160006001600160801b03168152602001856020016020810190611f7191906150d5565b63ffffffff168152602001611f8c60608701604088016150d5565b63ffffffff168152602001611fa760808701606088016150d5565b63ffffffff168152602001611fc260a08701608088016150d5565b63ffffffff16815260006020820152604001611fe460c0870160a088016150d5565b63ffffffff168152602001611fff60e0870160c08801614cbe565b6001600160a01b0316815260200161201e610100870160e08801614d16565b151590526114bd565b61202f6122a7565b6001600160a01b031661204061316b565b6001600160a01b031614612067576040516330cd747160e01b815260040160405180910390fd5b60d4805463ffffffff909216600160c01b0263ffffffff60c01b19909216919091179055565b6000612097612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160a01b90046001600160601b031690565b6120d1613bc5565b6120db6000613c34565b565b60d3805481906120ec90615610565b80601f016020809104026020016040519081016040528092919081815260200182805461211890615610565b80156121655780601f1061213a57610100808354040283529160200191612165565b820191906000526020600020905b81548152906001019060200180831161214857829003601f168201915b505050600184015460029094015492936001600160a01b0381169363ffffffff600160a01b830481169450600160c01b830416925061ffff600160e01b8304811692600160f01b900481169180821691620100008204811691600160201b90041689565b6121d16122a7565b6001600160a01b03166121e261316b565b6001600160a01b031614612209576040516330cd747160e01b815260040160405180910390fd5b6122138282613a49565b6114506001613abc565b6122256122a7565b6001600160a01b031661223661316b565b6001600160a01b03161461225d576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115612284576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160f01b026001600160f01b03909216919091179055565b6033546001600160a01b031690565b8535600090815260c96020908152604080832060ca9092529091205460028201546001600160201b90910463ffffffff16111561230c57600282015461230990600160201b900463ffffffff16886156f7565b96505b60d554600090600160201b900461ffff1661233061232a828b615724565b84613c86565b61233a91906156f7565b905061237287670de0b6b3a7640000612353848c6158da565b61235d91906156f7565b60405180602001604052806000815250613d20565b612384838a8a8460d2548b8b8b61405b565b505050505050505050565b606060d1805461178590615610565b6123a66122a7565b6001600160a01b03166123b761316b565b6001600160a01b0316146123de576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff909216620100000263ffff000019909216919091179055565b60d55462010000900461ffff1660000361242b5760405163017dfd0760e71b815260040160405180910390fd5b600181101561244d57604051633ccf184f60e11b815260040160405180910390fd5b600061245761316b565b90506000612463612f33565b61246e9060016156f7565b60d5549091506000906127109061248f9062010000900461ffff16846156f7565b6124999190615724565b905060006124a68461208d565b905060006124e08561dead898960008181106124c4576124c46156cb565b90506020020135604051806020016040528060008152506125d2565b826124e9612f33565b6124f39084615908565b101561257657600286101561251b57604051633ccf184f60e11b815260040160405180910390fd5b612552858661dead8a8a6001818110612536576125366156cb565b905060200201356040518060200160405280600081525061101e565b8261255b612f33565b612565919061591c565b61256f90826158da565b9050612593565b6125938561dead856040518060200160405280600081525061329a565b6125a18561235d83876158da565b50505050505050565b611450338383614435565b60006118153384846040518060200160405280600081525061329a565b6125df338585858561101e565b50505050565b60d55460d25460009161260491600160201b90910461ffff1690615724565b905090565b838614612629576040516306b7c75960e31b815260040160405180910390fd5b8735600090815260c96020908152604080832060ca909252822054909180805b8a8110156127595760028501546000906001600160201b90910463ffffffff1611156126ab576002860154600160201b900463ffffffff168b8b84818110612693576126936156cb565b905060200201356126a491906156f7565b90506126c7565b8a8a838181106126bd576126bd6156cb565b9050602002013590505b60d554600090600160201b900461ffff166126eb6126e58285615724565b88613c86565b6126f591906156f7565b90506127348e8e8581811061270c5761270c6156cb565b90506020020160208101906127219190614cbe565b670de0b6b3a764000061235384866158da565b61273e82866158da565b945061274a81856158da565b93508260010192505050612649565b5061276c848d848460d2548c8c8c61405b565b505050505050505050505050565b600083815260c96020908152604080832060cc9092528083205460d4549151631c130e3f60e21b8152909173d4e182124131fe5f3bde4cdef00975fb97f5b3d89163704c38fc916127e1918691600160f01b900461ffff16908a9087908b9060040161592f565b602060405180830381865af41580156127fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128229190615958565b9695505050505050565b6125df338585858561127e565b612841613bc5565b6008811115612863576040516306b7c75960e31b815260040160405180910390fd5b6000805b60ff81168311156129a15760008160ff161180156128eb5750838361288d600184615971565b60ff1681811061289f5761289f6156cb565b6128b59260206040909202019081019150614c0e565b61ffff1684848360ff168181106128ce576128ce6156cb565b6128e49260206040909202019081019150614c0e565b61ffff1610155b15612909576040516306b7c75960e31b815260040160405180910390fd5b600084848360ff16818110612920576129206156cb565b90506040020160200160208101906129389190614c0e565b61ffff16601086868560ff16818110612953576129536156cb565b6129699260206040909202019081019150614c0e565b61ffff16901b17905061297d82602061598a565b60ff168163ffffffff16901b83179250508080612999906159ad565b915050612867565b50600093845260ca6020526040909320929092555050565b60006129d06129c6612eee565b6003018484613280565b549392505050565b6129e06122a7565b6001600160a01b03166129f161316b565b6001600160a01b031614612a18576040516330cd747160e01b815260040160405180910390fd5b612a228282613a49565b6114506000613abc565b600054610100900460ff1615808015612a4c5750600054600160ff909116105b80612a665750303b158015612a66575060005460ff166001145b612ace5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff191660011790558015612af1576000805461ff0019166101001790555b60d0612afd8782615793565b5060d1612b0a8682615793565b508360d3612b188282615b07565b905050612b2660008061448e565b611388612b3960a0860160808701614c0e565b61ffff161180612b5d5750611388612b5760c0860160a08701614c0e565b61ffff16115b80612b8057506000612b756040860160208701614cbe565b6001600160a01b0316145b80612b9e5750612b9660808501606086016150d5565b63ffffffff16155b15612bbc576040516306b7c75960e31b815260040160405180910390fd5b612bc4614613565b6000612bd66080850160608601614c0e565b612be66060860160408701614c0e565b612bf66040870160208801614c0e565b612c036020880188614c0e565b612c0d9190615c88565b612c179190615c88565b612c219190615c88565b61ffff16905060fa612c396040860160208701614c0e565b61ffff161080612c4b57508061271014155b15612c6957604051632429608560e11b815260040160405180910390fd5b8360d6612c768282615ca3565b505060d554612c8a90849061ffff16611c2c565b508015612cd1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b6000612cf0612ce6612eee565b6001018484613280565b5415159392505050565b612d026122a7565b6001600160a01b0316612d1361316b565b6001600160a01b031614612d3a576040516330cd747160e01b815260040160405180910390fd5b60d95460041615612d5e5760405163249fab5d60e01b815260040160405180910390fd5b60d880546001600160a01b0319166001600160a01b0392909216919091179055565b612d88613bc5565b6001600160a01b038116612ded5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401612ac5565b61198b81613c34565b60405163140fbc8560e01b815273d4e182124131fe5f3bde4cdef00975fb97f5b3d89063140fbc8590611d689060ce908590600401615d81565b60006114b782614642565b612e436122a7565b6001600160a01b0316612e5461316b565b6001600160a01b031614612e7b576040516330cd747160e01b815260040160405180910390fd5b60d95460021615612e9f5760405163249fab5d60e01b815260040160405180910390fd5b60d2548163ffffffff161015612ec857604051638a164f6360e01b815260040160405180910390fd5b60d4805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b68b6dffd38a260769cb290565b60006114b483600160201b8410840261467b565b8160081c8360601b018260ff16821515811b6001821b198354161782555050505050565b60d55460009061260490600160201b900461ffff16670de0b6b3a76400006156f7565b808214612f6b57838252602082019150612f56565b505092915050565b60405163f23a6e6181523360208201528460601b60601c60408201528260608201526001608082015260a08082015281518060c08301528015612fc0578060e08301826020860160045afa505b6020828260c401601c85016000895af1612fe3573d15612fe3573d6000833e3d82fd5b50805163f23a6e6160e01b14611ca057639c05499b6000526004601cfd5b805161300c57505050565b6040516040815260408101825160051b6020018082828660045afa50503d60400160208301523d81019050825181523d81016020820191505b80821461305b5760018252816020019150613045565b506001600160a01b03808516908616337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb85850386a45050505050565b8151156125df5760405163bc197c8181523360208201528460601b60601c604082015260a0606082015260c08101835160051b6020018082828760045afa50503d60a00160808301523d60a0013d0160a08301523d81019050835181523d81016020820191505b80821461311557600182528160200191506130ff565b835160200191508181838660045afa50602083601c85013d840103601c860160008a5af161314c573d1561314c573d6000843e3d83fd5b5050805163bc197c8160e01b14611ca057639c05499b6000526004601cfd5b600033736bc558a6dc48defa0e7022713c23d65ab26e4fa71461318d57503390565b503290565b60006001600160e01b0319821663152a902d60e11b14806114b757506301ffc9a760e01b6001600160e01b03198316146114b7565b806131dd6131d3612eee565b6003018585613280565b5560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b60006114b761322b612eee565b600201600160201b8410840261467b565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480613257575050819003601f19909101908152919050565b602890815260149190915260009182526048822091905290565b6001600160a01b0383166132c157604051633a954ecd60e21b815260040160405180910390fd5b60006132cb612eee565b8054909150600160201b900463ffffffff166132fa5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b0385811660009081526005830160209081526040808320938816835280832081516101008101835284815292830184905260808301849052606060a0840181905260c0840181905260e084018190528554600160801b80820463ffffffff90811695870195909552835404909316908401529290600160a01b90046001600160601b0316808811156133a657604051631e9acf1760e31b815260040160405180910390fd5b84546001600160601b0391899003828116600160a01b9081026001600160a01b03938416178855865481810485168c019485169091029216919091178555604084015191925090613411906133f9612f33565b83816134075761340761570e565b0480821191030290565b835261341c89611a22565b61347557886001600160a01b03168a6001600160a01b03160361344757825160408401510360608401525b61346f613452612f33565b83816134605761346061570e565b04846060015180821191030290565b60208401525b505061347f600190565b156136445760006134b182604001516134a684600001518560200151808218908211021890565b808218908211021890565b9050806134be5750613644565b8151819003825260208201805182900390526001600160a01b03808916908a16036134f3576060820180519091019052613644565b6134fc81611467565b60a08301526001600160a01b038981166000908152600487016020526040808220928b16825290819020865491850180518590039081905263ffffffff60801b198316600160801b63ffffffff9283160217885586549192600160601b9081900482169204168181108183180218865460608701805187019081905263ffffffff908116600160801b0263ffffffff60801b1993909116600160601b0292909216600160601b600160a01b0319909116171786555b6135bb8382614694565b9050806135df5787546135dc908490600160201b900463ffffffff16614694565b90505b6135eb83826000612f0f565b6135f782826001612f0f565b6136058560a001518261476c565b60001990930192836135b157865463ffffffff60601b1916600160601b63ffffffff83160217875560a085015161363f908d908d90613001565b505050505b80511561373d57805161365681611467565b60c08301526001600160a01b03891660009081526004860160205260409081902090830151855463ffffffff918490038216600160801b0263ffffffff60801b1990911617808755600160601b9004165b6136b18282614694565b9050806136d55786546136d2908390600160201b900463ffffffff16614694565b90505b6136e182826000612f0f565b6136f087600201826000612f0f565b6136fe8460c001518261476c565b60001990920191826136a757855463ffffffff60601b1916600160601b63ffffffff83160217865560c0840151613739908c90600090613001565b5050505b6020810151156138ba57602081015161375581611467565b60e08301526001600160a01b038816600090815260048601602052604081206060840151855463ffffffff60801b1916600160801b91850163ffffffff1691909102178555906137a3612f33565b8754600160401b90046001600160601b0316816137c2576137c261570e565b8854875492909104925063ffffffff90811683811180159091021791600160601b9004165b6137f4896002018361467b565b156138205761381961380d8a6002018460010186614781565b84811180159091021790565b91506137e7565b61382f89600201836001612f0f565b61383b84836001612f0f565b81811082821802811890506138548660e001518361476c565b60018201838111801590910217600019909501949150846137e757865463ffffffff60601b1916600160601b63ffffffff83811691909102919091178855895463ffffffff191690831617895560e08601516138b4906000908e90613001565b50505050505b856000528660601b60601c8860601b60601c600080516020615f4483398151915260206000a3863b15613908576138f788888360a0015188613098565b6139086000888360e0015188613098565b5050505050505050565b600061391c612eee565b9050613945600183811084821802188254600160201b900463ffffffff16808211908218021890565b6001600160a01b0384166000818152600584016020908152604091829020805463ffffffff60601b1916600160601b63ffffffff871602179055905183815292945090917ff2e9b90ef293d49024cb7f7f9fc4b87a50aca7b77895c750060efc5067161d61910160405180910390a2505050565b60006139c3612eee565b6001600160a01b0384166000818152600592909201602090815260408320805486158015865291955060ff600160581b9091049081166002918216159092180218600117927f0b5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6693939190a2815460ff909116600160581b0260ff60581b199091161790555050565b604051663337b932bb32b960c91b6020820152602701604051602081830303815290604052805190602001208282604051602001613a88929190615d9a565b604051602081830303815290604052805190602001201461145057604051635ee88f9760e01b815260040160405180910390fd5b60d98054600190921b9091179055565b6127106001600160601b0382161115613b3a5760405162461bcd60e51b815260206004820152602a60248201527f455243323938313a20726f79616c7479206665652077696c6c206578636565646044820152692073616c65507269636560b01b6064820152608401612ac5565b6001600160a01b038216613b8c5760405162461bcd60e51b815260206004820152601960248201527822a921991c9c189d1034b73b30b634b2103932b1b2b4bb32b960391b6044820152606401612ac5565b604080518082019091526001600160a01b039092168083526001600160601b039091166020909201829052600160a01b90910217609755565b613bcd61316b565b6001600160a01b0316613bde6122a7565b6001600160a01b0316146120db5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401612ac5565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000805b60088160ff161015613d16576000613ca382602061598a565b60ff1684901c63ffffffff8116915060101c61ffff16816000829003613ccb57505050613d16565b8161ffff168710613d00578061ffff168261ffff1688613ceb9190615724565b613cf591906156f7565b9450505050506114b7565b5050508080613d0e906159ad565b915050613c8a565b5060009392505050565b6001600160a01b038316613d4757604051633a954ecd60e21b815260040160405180910390fd5b6000613d51612eee565b8054909150600160201b900463ffffffff16613d805760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03841660009081526005820160209081526040808320815180830190925292815260609181019190915281546001600160601b03600160a01b80830482168801918216026001600160a01b03909216919091178355613de4612f33565b8181613df257613df261570e565b0482525082546001600160601b03600160401b8083048216888101928316909102600160401b600160a01b0319909316929092178555600091829182613e3a6104b98361487c565b9050898210811715613e5f5760405163e5cfe95760e01b815260040160405180910390fd5b613e67612f33565b8281613e7557613e7561570e565b049350613ea1613e83612f33565b8481613e9157613e9161570e565b0460010185811180159091021790565b8854600160201b80820463ffffffff9081168881108982180218160263ffffffff60201b199091161789559450613edd92508a9150611a229050565b61401e5782518454613f0291600160801b90910463ffffffff16808203911102611467565b60208401819052511561401e576001600160a01b03881660009081526004860160209081526040909120855491850151519091600160601b900463ffffffff16905b613f51886002018661467b565b15613f7d57613f76613f6a896002018760010187614781565b85811180159091021790565b9450613f44565b613f8c88600201866001612f0f565b613f9883866001612f0f565b8482108583180282189150613fb186602001518661476c565b6001850184811180159091021794506000190180613f4457508554855163ffffffff908116600160801b0263ffffffff60801b19918416600160601b0291909116600160601b600160a01b031990921691909117178655602085015161401b906000908c90613001565b50505b505060008581526001600160a01b03871690600080516020615f44833981519152602082a3853b15612cd157612cd1600087836020015187613098565b600061406786886158da565b90506140b46040518060c0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b8835600090815260cf6020526040812054908181036140d45760006140f6565b60cc60006140e360018561591c565b60001b8152602001908152602001600020545b90506040518060c0016040528061410b6122a7565b6001600160a01b03168152602001886001600160a01b0316815260200185815260200189815260200160cc60008e6000013581526020019081526020016000205481526020018281525092505050600073d4e182124131fe5f3bde4cdef00975fb97f5b3d863704c38fc8c60d3600101601e9054906101000a900461ffff168c866080015160006001600160a01b031688602001516001600160a01b031614156040518663ffffffff1660e01b81526004016141cb95949392919061592f565b602060405180830381865af41580156141e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061420c9190615958565b604051631306dbef60e21b815290915073d4e182124131fe5f3bde4cdef00975fb97f5b3d890634c1b6fbc90614256908e9060d3908f9060cb908c908c908b908b90600401615dd3565b60006040518083038186803b15801561426e57600080fd5b505af4158015614282573d6000803e3d6000fd5b50505060018c015463ffffffff600160801b82048116600160a01b9092041610159050614300578260cb60006142b661316b565b6001600160a01b03166001600160a01b0316815260200190815260200160002060008c60000135815260200190815260200160002060008282546142fa91906158da565b90915550505b60018b015463ffffffff600160801b90910481161015614340578935600090815260cc60205260408120805485929061433a9084906158da565b90915550505b8260d2600082825461435291906158da565b909155505060405163032cef8f60e41b8152600481018c905260d3602482015260cd604482015260ce60648201526001600160a01b038716608482015260a481018a90526001600160801b03821660c482015273d4e182124131fe5f3bde4cdef00975fb97f5b3d8906332cef8f09060e40160006040518083038186803b1580156143dc57600080fd5b505af41580156143f0573d6000803e3d6000fd5b50505050806001600160801b03163411156144285761442861441061316b565b6144236001600160801b0384163461591c565b6148a0565b5050505050505050505050565b80151561444d614443612eee565b6001018585613280565b5560008181526001600160a01b0380841691908516907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190602090a3505050565b6000614498612eee565b8054909150600160201b900463ffffffff16156144c857604051633ab534b960e21b815260040160405180910390fd5b6144d0612f33565b83816144de576144de61570e565b825463ffffffff60201b1916600160201b92909104600190811763ffffffff16929092021782556001600160601b0390614516612f33565b03106145355760405163265f13bd60e21b815260040160405180910390fd5b805463ffffffff191660011781558215611221576001600160a01b03821661457057604051633a954ecd60e21b815260040160405180910390fd5b6145798361487c565b156145975760405163e5cfe95760e01b815260040160405180910390fd5b8054600160401b600160a01b031916600160401b6001600160601b0385169081029190911782556001600160a01b038381166000818152600585016020908152604082208054909416600160a01b9095029490941783558681529192909190600080516020615f448339815191529082a36125df8360016139b9565b600054610100900460ff1661463a5760405162461bcd60e51b8152600401612ac590615ef8565b6120db614914565b600061464c612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160801b900463ffffffff1690565b60609190911b600882901c0154600160ff9092161c1690565b60008260601b8260081c81018054841960ff161b841960ff161c82821481176146cf575b5060001901600081905280548282148117156146b8575b8015614763577b01c1818141808141018080c0814100c04181408140c0c100414140c160221b6f8421084210842108cc6318c6db6d54be6001600160801b03831160071b83811c6001600160401b031060061b1783811c63ffffffff1060051b1783811c61ffff1060041b1783811c60ff1060031b1783811c9190911c601f169190911a1783830360081b17858111150293505b50505092915050565b601f1990910180519182526020919091019052565b6000801990508360601b8360081c81018054198560ff161c8560ff161b806147d6578460081c83015b60018301925082541991508083118217156147aa57808311156147d45760ff86191691821b90911c905b505b80156148725782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166101e07a1412563212c14164235266736f7425221143267a4524367526767760fc7b2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff840260f81c161b60f71c1690811c63d76453e004601f169190911a1717858111878210176000031793505b5050509392505050565b600080614887612f33565b606084901c930463fffffffe1092909217151592915050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146148ed576040519150601f19603f3d011682016040523d82523d6000602084013e6148f2565b606091505b5050905080611221576040516312171d8360e31b815260040160405180910390fd5b600054610100900460ff1661493b5760405162461bcd60e51b8152600401612ac590615ef8565b6120db61494661316b565b613c34565b634e487b7160e01b600052604160045260246000fd5b60405161016081016001600160401b03811182821017156149845761498461494b565b60405290565b604051601f8201601f191681016001600160401b03811182821017156149b2576149b261494b565b604052919050565b80356001600160801b03811681146149d157600080fd5b919050565b63ffffffff8116811461198b57600080fd5b80356149d1816149d6565b6001600160a01b038116811461198b57600080fd5b80356149d1816149f3565b801515811461198b57600080fd5b80356149d181614a13565b60006101608284031215614a3f57600080fd5b614a47614961565b9050614a52826149ba565b8152614a60602083016149ba565b6020820152614a71604083016149ba565b6040820152614a82606083016149e8565b6060820152614a93608083016149e8565b6080820152614aa460a083016149e8565b60a0820152614ab560c083016149e8565b60c0820152614ac660e083016149e8565b60e0820152610100614ad98184016149e8565b90820152610120614aeb838201614a08565b90820152610140614afd838201614a21565b9082015292915050565b60008060006101a08486031215614b1d57600080fd5b8335925060208401359150614b358560408601614a2c565b90509250925092565b600060208284031215614b5057600080fd5b81356001600160e01b031981168114614b6857600080fd5b9392505050565b60005b83811015614b8a578181015183820152602001614b72565b50506000910152565b60008151808452614bab816020860160208601614b6f565b601f01601f19169290920160200192915050565b6020815260006114b46020830184614b93565b60008060408385031215614be557600080fd5b8235614bf0816149f3565b946020939093013593505050565b61ffff8116811461198b57600080fd5b600060208284031215614c2057600080fd5b8135614b6881614bfe565b600060208284031215614c3d57600080fd5b5035919050565b60008060408385031215614c5757600080fd5b8235614c62816149f3565b91506020830135614c72816149f3565b809150509250929050565b600080600060608486031215614c9257600080fd5b8335614c9d816149f3565b92506020840135614cad816149f3565b929592945050506040919091013590565b600060208284031215614cd057600080fd5b8135614b68816149f3565b60008060408385031215614cee57600080fd5b50508035926020909101359150565b6001600160a01b03929092168252602082015260400190565b600060208284031215614d2857600080fd5b8135614b6881614a13565b60008083601f840112614d4557600080fd5b5081356001600160401b03811115614d5c57600080fd5b602083019150836020828501011115611b2457600080fd5b60008060208385031215614d8757600080fd5b82356001600160401b03811115614d9d57600080fd5b614da985828601614d33565b90969095509350505050565b60008060408385031215614dc857600080fd5b8235614dd3816149f3565b91506020830135614c7281614bfe565b600060408284031215611a6d57600080fd5b600080600080600060808688031215614e0d57600080fd5b85356001600160401b0380821115614e2457600080fd5b614e3089838a01614de3565b96506020880135955060408801359150614e49826149f3565b90935060608701359080821115614e5f57600080fd5b50614e6c88828901614d33565b969995985093965092949392505050565b600082601f830112614e8e57600080fd5b81356001600160401b03811115614ea757614ea761494b565b614eba601f8201601f191660200161498a565b818152846020838601011115614ecf57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614efe57600080fd5b81356001600160401b03811115614f1457600080fd5b614f2084828501614e7d565b949350505050565b60006001600160401b03821115614f4157614f4161494b565b5060051b60200190565b60006020808385031215614f5e57600080fd5b82356001600160401b03811115614f7457600080fd5b8301601f81018513614f8557600080fd5b8035614f98614f9382614f28565b61498a565b81815260059190911b82018301908381019087831115614fb757600080fd5b928401925b82841015614fde578335614fcf816149f3565b82529284019290840190614fbc565b979650505050505050565b60008083601f840112614ffb57600080fd5b5081356001600160401b0381111561501257600080fd5b6020830191508360208260061b8501011115611b2457600080fd5b60008060008060008587036101c081121561504757600080fd5b8635955060208701359450610160603f198201121561506557600080fd5b506040860192506101a08601356001600160401b0381111561508657600080fd5b614e6c88828901614fe9565b60008060008385036101408112156150a957600080fd5b8435935060208501359250610100603f19820112156150c757600080fd5b506040840190509250925092565b6000602082840312156150e757600080fd5b8135614b68816149d6565b60006101208083526151068184018d614b93565b6001600160a01b039b909b166020840152505063ffffffff978816604082015295909616606086015261ffff938416608086015291831660a0850152821660c0840152811660e083015290911661010090910152919050565b60008060008060008060a0878903121561517857600080fd5b86356001600160401b038082111561518f57600080fd5b61519b8a838b01614de3565b975060208901359650604089013591506151b4826149f3565b9094506060880135906151c6826149f3565b909350608088013590808211156151dc57600080fd5b506151e989828a01614d33565b979a9699509497509295939492505050565b60008083601f84011261520d57600080fd5b5081356001600160401b0381111561522457600080fd5b6020830191508360208260051b8501011115611b2457600080fd5b6000806020838503121561525257600080fd5b82356001600160401b0381111561526857600080fd5b614da9858286016151fb565b6000806040838503121561528757600080fd5b8235615292816149f3565b91506020830135614c7281614a13565b600080600080608085870312156152b857600080fd5b84356152c3816149f3565b935060208501356152d3816149f3565b92506040850135915060608501356001600160401b038111156152f557600080fd5b61530187828801614e7d565b91505092959194509250565b60008060008060008060008060a0898b03121561532957600080fd5b88356001600160401b038082111561534057600080fd5b61534c8c838d01614de3565b995060208b013591508082111561536257600080fd5b61536e8c838d016151fb565b909950975060408b013591508082111561538757600080fd5b6153938c838d016151fb565b909750955060608b013591506153a8826149f3565b90935060808a013590808211156153be57600080fd5b506153cb8b828c01614d33565b999c989b5096995094979396929594505050565b6000806000606084860312156153f457600080fd5b8335925060208401359150604084013561540d81614a13565b809150509250925092565b6000806000806080858703121561542e57600080fd5b8435615439816149f3565b935060208581013561544a816149f3565b935060408601356001600160401b038082111561546657600080fd5b818801915088601f83011261547a57600080fd5b8135615488614f9382614f28565b81815260059190911b8301840190848101908b8311156154a757600080fd5b938501935b828510156154c5578435825293850193908501906154ac565b9650505060608801359250808311156154dd57600080fd5b505061530187828801614e7d565b60008060006040848603121561550057600080fd5b8335925060208401356001600160401b0381111561551d57600080fd5b61552986828701614fe9565b9497909650939450505050565b600080600080600085870361016081121561555057600080fd5b86356001600160401b038082111561556757600080fd5b6155738a838b01614e7d565b9750602089013591508082111561558957600080fd5b6155958a838b01614e7d565b965060408901359150808211156155ab57600080fd5b508701610120818a0312156155bf57600080fd5b935060e0605f19820112156155d357600080fd5b506060860191506155e76101408701614a08565b90509295509295909350565b60006020828403121561560557600080fd5b8151614b6881614a13565b600181811c9082168061562457607f821691505b602082108103611a6d57634e487b7160e01b600052602260045260246000fd5b600080845461565281615610565b6001828116801561566a576001811461567f576156ae565b60ff19841687528215158302870194506156ae565b8860005260208060002060005b858110156156a55781548a82015290840190820161568c565b50505082870194505b5050505083516156c2818360208801614b6f565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176114b7576114b76156e1565b634e487b7160e01b600052601260045260246000fd5b6000826157335761573361570e565b500490565b601f82111561122157600081815260208120601f850160051c8101602086101561575f5750805b601f850160051c820191505b81811015612cd15782815560010161576b565b600019600383901b1c191660019190911b1790565b81516001600160401b038111156157ac576157ac61494b565b6157c0816157ba8454615610565b84615738565b602080601f8311600181146157ef57600084156157dd5750858301515b6157e7858261577e565b865550612cd1565b600085815260208120601f198616915b8281101561581e578886015182559484019460019091019084016157ff565b508582101561583c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600081518084526020808501945080840160005b838110156158855781516001600160a01b031687529582019590820190600101615860565b509495945050505050565b84815283602082015260018060a01b0383166040820152608060608201526000612822608083018461584c565b600061016082840312156158d057600080fd5b6114b48383614a2c565b808201808211156114b7576114b76156e1565b6000602082840312156158ff57600080fd5b6114b4826149ba565b6000826159175761591761570e565b500690565b818103818111156114b7576114b76156e1565b94855261ffff939093166020850152604084019190915260608301521515608082015260a00190565b60006020828403121561596a57600080fd5b5051919050565b60ff82811682821603908111156114b7576114b76156e1565b60ff81811683821602908116908181146159a6576159a66156e1565b5092915050565b600060ff821660ff81036159c3576159c36156e1565b60010192915050565b6001600160401b038311156159e3576159e361494b565b6159f7836159f18354615610565b83615738565b6000601f841160018114615a255760008515615a135750838201355b615a1d868261577e565b845550611ca0565b600083815260209020601f19861690835b82811015615a565786850135825560209485019460019092019101615a36565b5086821015615a735760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b600081356114b7816149f3565b80546001600160a01b0319166001600160a01b0392909216919091179055565b600081356114b7816149d6565b600081356114b781614bfe565b805463ffff00008360101b1663ffff0000198216178255505050565b805461ffff60201b191660209290921b61ffff60201b16919091179055565b8135601e19833603018112615b1b57600080fd5b820180356001600160401b03811115615b3357600080fd5b602082019150803603821315615b4857600080fd5b615b538183856159cc565b505060018101615b6e615b6860208501615a85565b82615a92565b615ba1615b7d60408501615ab2565b82805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b615bd4615bb060608501615ab2565b82805463ffffffff60c01b191660c09290921b63ffffffff60c01b16919091179055565b615c03615be360808501615abf565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b615c36615c1260a08501615abf565b8280546001600160f01b031660f09290921b6001600160f01b031916919091179055565b5060028101615c5d615c4a60c08501615abf565b825461ffff191661ffff91909116178255565b615c72615c6c60e08501615abf565b82615acc565b611221615c826101008501615abf565b82615ae8565b61ffff8181168382160190808211156159a6576159a66156e1565b8135615cae81614bfe565b815461ffff191661ffff8216178255506020820135615ccc81614bfe565b615cd68183615acc565b506040820135615ce581614bfe565b615cef8183615ae8565b506060820135615cfe81614bfe565b815461ffff60301b19811660309290921b61ffff60301b1691821783556080840135615d29816149f3565b600160301b600160e01b03199190911690911760409190911b600160401b600160e01b0316178155615d69615d6060a08401615a85565b60018301615a92565b611450615d7860c08401615a85565b60028301615a92565b828152604060208201526000614f20604083018461584c565b8183823760009101908152919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006101808a8352896020840152806040840152883581840152506020880135601e19893603018112615e0557600080fd5b88016020810190356001600160401b03811115615e2157600080fd5b8060051b803603831315615e3457600080fd5b60406101a08601526101c085018290526101e06001600160fb1b03831115615e5b57600080fd5b81848288013781860193508a606087015280868503016080870152615e838185018a8c615daa565b88516001600160a01b0390811660a08981019190915260208b015190911660c089015260408a015160e089015260608a015161010089015260808a01516101208901528901516101408801529450615edb9350505050565b6001600160801b0383166101608301529998505050505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122021f93e85066bebf74ccd8699a0d7668cf79e75e8b6518bcf716582c3f584590664736f6c63430008140033
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.