Feature Tip: Add private address tag to any address under My Name Tag !
Overview
TokenID
3385306
Total Transfers
-
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract
Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
XXYYZZ
Compiler Version
v0.8.20+commit.a1b79de6
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {XXYYZZMetadata} from "./XXYYZZMetadata.sol"; import {XXYYZZBurn} from "./XXYYZZBurn.sol"; import {XXYYZZSeaDrop} from "./XXYYZZSeaDrop.sol"; import {XXYYZZCore} from "./XXYYZZCore.sol"; import {XXYYZZRerollFinalize} from "./XXYYZZRerollFinalize.sol"; import {XXYYZZCore} from "./XXYYZZCore.sol"; import {LibString} from "solady/utils/LibString.sol"; import {Base64} from "solady/utils/Base64.sol"; /** * @title XXYYZZ * @author emo.eth * @notice XXYYZZ is a collection of fully onchain, collectible colors. Each token has a unique hex value. * Tokens may be "rerolled" to new hex values, unless they are "finalized," in which case, they are immutable. * * Finalizing tokens also adds the finalizer's wallet address to the token's metadata. * Tokens may be burned, which removes it from the token supply, but unless the token was finalized, its * particular hex value may be minted or rerolled again. * * Mints and rerolls are pseudorandom by default, unless one of the "Specific" methods is called. * To prevent front-running "specific" mint transactions, the XXYYZZ contract uses a commit-reveal scheme. * Users must commit a hash of their desired hex value with a secret salt, wait at least one minute, and then * submit their mint or reroll transaction with the original hex value(s) and salt. * Multiple IDs may be minted or rerolled in a single transaction by committixng the result of hash of all IDs in order * with a single secret salt. * In batch methods, unavailable IDs are skipped, and excess payment is refunded to the caller. */ contract XXYYZZ is XXYYZZMetadata, XXYYZZBurn, XXYYZZSeaDrop, XXYYZZRerollFinalize { using LibString for uint256; using LibString for address; using Base64 for bytes; constructor( address initialOwner, address creatorPayout, uint256 maxBatchSize, uint24[] memory preMintIds, address seaDrop ) XXYYZZSeaDrop(seaDrop, creatorPayout, initialOwner, maxBatchSize) { for (uint256 i; i < preMintIds.length;) { _mint(initialOwner, preMintIds[i]); _finalizeToken(preMintIds[i], initialOwner); unchecked { ++i; } } _numMinted = uint32(preMintIds.length); } function supportsInterface(bytes4 interfaceId) public pure virtual override(XXYYZZSeaDrop, XXYYZZCore) returns (bool) { return XXYYZZSeaDrop.supportsInterface(interfaceId); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {XXYYZZCore} from "./XXYYZZCore.sol"; import {LibString} from "solady/utils/LibString.sol"; import {Base64} from "solady/utils/Base64.sol"; /** * @title XXYYZZMetadata * @author emo.eth * @notice XXYYZZMetadata implements the onchain metadata for XXYYZZ tokens. */ abstract contract XXYYZZMetadata is XXYYZZCore { using LibString for uint256; using LibString for address; using Base64 for bytes; /** * @notice Return the base64-encoded token metadata. Won't revert if the token doesn't exist. * Will revert if the id is not a valid six-hex-digit ID. */ function tokenURI(uint256 id) public view virtual override returns (string memory) { _validateId(id); return string.concat("data:application/json;base64,", bytes(_stringURI(id)).encode()); } ///@notice Return the base64-encoded contract-level metadata function contractURI() public pure returns (string memory) { return string.concat("data:application/json;base64,", bytes(_stringContractURI()).encode()); } ///@dev Return a token-level JSON string function _stringURI(uint256 id) internal view virtual returns (string memory) { return string.concat( "{", _kv("name", _name(id)), ",", _kv("external_link", "https://xxyyzz.art"), ",", _kv( "description", "Proof of color. XXYYZZ is a collection of fully onchain, unique, composable, and collectable colors." ), ",", _kv("image", _imageURI(id)), ",", _kRawV("attributes", _traits(id)), "}" ); } ///@dev Return a contract-level JSON string function _stringContractURI() internal pure returns (string memory) { return '{"name":"XXYYZZ","description":"Collectible, composable, and unique onchain colors.","external_link":"https://xxyyzz.art"}'; } ///@dev Return a name like "#aabbcc" function _name(uint256 id) internal pure returns (string memory) { return string.concat("#", id.toHexStringNoPrefix({length: 3})); } ///@dev Return an svg as a base64-encoded data uri string function _imageURI(uint256 id) internal pure returns (string memory) { return string.concat("data:image/svg+xml;base64,", bytes(_svg(id)).encode()); } ///@dev Return a 690x690 SVG with a single rect of the token's color function _svg(uint256 id) internal pure returns (string memory) { return string.concat( '<svg xmlns="http://www.w3.org/2000/svg" width="690" height="690"><rect width="690" height="690" fill="#', id.toHexStringNoPrefix({length: 3}), '" /></svg>' ); } ///@dev Return a JSON array of {"trait_type":"key","value":"value"} pairs function _traits(uint256 id) internal view returns (string memory) { string memory color = _trait("Color", _name(id)); if (isFinalized(id)) { string memory finalizedProp = _trait("Finalized", "Yes"); return string.concat( "[", color, ",", finalizedProp, ",", _trait("Finalizer", finalizers[id].toHexString()), "]" ); } else { return string.concat("[", color, ",", _trait("Finalized", "No"), "]"); } } ///@dev return a {"trait_type":"key","value":"value"} pair function _trait(string memory key, string memory value) internal pure returns (string memory) { return string.concat('{"trait_type":"', key, '","value":"', value, '"}'); } ///@dev return a "key":"value" pair function _kv(string memory key, string memory value) internal pure returns (string memory) { return string.concat('"', key, '":"', value, '"'); } ///@dev Return a "key":value pair without quoting value function _kRawV(string memory key, string memory value) internal pure returns (string memory) { return string.concat('"', key, '":', value); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {XXYYZZCore} from "./XXYYZZCore.sol"; abstract contract XXYYZZBurn is XXYYZZCore { ////////// // BURN // ////////// /** * @notice Permanently burn a token that the caller owns or is approved for. * @param xxyyzz The token to burn. * @param onlyFinalized If true, only tokens that have been finalized can be burned. Useful if an approved operator * is burning tokens on behalf of a user. */ function burn(uint256 xxyyzz, bool onlyFinalized) public { // cannot overflow as there are at most 2^24 tokens, and _numBurned is a uint32 unchecked { _numBurned += 1; } if (onlyFinalized) { if (!_isFinalized(xxyyzz)) { revert OnlyFinalized(); } } _burn(msg.sender, xxyyzz); } /** * @notice Permanently burn multiple tokens. All must be owned by the same address. * @param ids The tokens to burn. * @param onlyFinalized If true, only tokens that have been finalized can be burned. Useful if an approved operator * is burning tokens on behalf of a user. */ function batchBurn(uint256[] calldata ids, bool onlyFinalized) public { if (ids.length == 0) { revert NoIdsProvided(); } uint256 packedOwnerFinalizedSlot = _packedOwnershipSlot(ids[0]); address initialTokenOwner = address(uint160(packedOwnerFinalizedSlot)); if (onlyFinalized) { if (packedOwnerFinalizedSlot < type(uint160).max) { revert OnlyFinalized(); } } // validate that msg.sender has approval to burn all tokens if (initialTokenOwner != msg.sender) { if (!isApprovedForAll(initialTokenOwner, msg.sender)) { revert BatchBurnerNotApprovedForAll(); } } // safe because there are at most 2^24 tokens, and ownerships are checked unchecked { _numBurned += uint32(ids.length); } _burn(ids[0]); for (uint256 i = 1; i < ids.length;) { uint256 id = ids[i]; packedOwnerFinalizedSlot = _packedOwnershipSlot(id); address owner = address(uint160(packedOwnerFinalizedSlot)); // ensure that all tokens are owned by the same address if (owner != initialTokenOwner) { revert OwnerMismatch(); } if (onlyFinalized) { if (packedOwnerFinalizedSlot < type(uint160).max) { revert OnlyFinalized(); } } // no need to specify msg.sender since caller is approved for all tokens // this also checks token exists _burn(id); unchecked { ++i; } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {XXYYZZMint} from "./XXYYZZMint.sol"; import {ISeaDrop, PublicDrop} from "./lib/SeaDropSpecific.sol"; abstract contract XXYYZZSeaDrop is XXYYZZMint { error OnlySeadrop(); address immutable SEADROP; address immutable CREATOR_PAYOUT; uint256 immutable DEPLOYED_TIME; uint256 private constant SEADROP_TOKEN_CREATED_EVENT_TOPIC = 0xd7aca75208b9be5ffc04c6a01922020ffd62b55e68e502e317f5344960279af8; address private constant SEADROP_FEE_RECIPIENT = 0x0000a26b00c1F0DF003000390027140000fAa719; address private constant SEADROP_ALLOWED_PAYER_1 = 0xf408Bee3443D0397e2c1cdE588Fb060AC657006F; address private constant SEADROP_ALLOWED_PAYER_2 = 0xE3d3D0eD702504e19825f44BC6542Ff2ec45cB9A; uint256 private constant INONFUNGIBLESEADROP_INTERFACE_ID = 0x1890fe8e; constructor(address seadrop, address creatorPayout, address initialOwner, uint256 maxBatchSize) XXYYZZMint(initialOwner, maxBatchSize) { SEADROP = seadrop; DEPLOYED_TIME = block.timestamp; CREATOR_PAYOUT = creatorPayout; // log without adding event to abi assembly { log1(0, 0, SEADROP_TOKEN_CREATED_EVENT_TOPIC) } } /** * @notice Configure the SeaDrop contract. onlyOwner. * @dev SeaDrop calls supportsInterface, so this unfortunately can't live in the constructor. */ function configureSeaDrop() external onlyOwner { ISeaDrop seadrop = ISeaDrop(SEADROP); seadrop.updatePublicDrop( PublicDrop({ mintPrice: uint80(0.005 ether), startTime: uint48(DEPLOYED_TIME), endTime: uint48(MAX_MINT_CLOSE_TIMESTAMP), maxTotalMintableByWallet: type(uint16).max, feeBps: uint16(1000), restrictFeeRecipients: true }) ); seadrop.updateCreatorPayoutAddress(CREATOR_PAYOUT); seadrop.updateAllowedFeeRecipient(SEADROP_FEE_RECIPIENT, true); seadrop.updatePayer(SEADROP_ALLOWED_PAYER_1, true); seadrop.updatePayer(SEADROP_ALLOWED_PAYER_2, true); } function mintSeaDrop(address recipient, uint256 quantity) external { if (msg.sender != SEADROP) { revert OnlySeadrop(); } // increment supply before minting uint128 newAmount; // this can be unchecked because an ID can only be minted once, and all IDs are later validated to be uint24s unchecked { newAmount = _numMinted + uint128(quantity); } _numMinted = newAmount; _mintTo(recipient, quantity, newAmount); } /** * @dev See {IERC165-supportsInterface}. Overridden to support SeaDrop. */ function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool result) { assembly { let s := shr(224, interfaceId) // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. ERC4906: 0x49064906 result := or( or(or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)), eq(s, 0x49064906)), eq(s, INONFUNGIBLESEADROP_INTERFACE_ID) ) } } /** * @dev Hard-coded for SeaDrop support */ function getMintStats(address) external view returns (uint256, uint256, uint256) { return (0, _numMinted, 16777216); } /** * @dev Hard-coded for SeaDrop support */ function maxSupply() external pure returns (uint256) { return 16777216; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {ERC721} from "solady/tokens/ERC721.sol"; import {CommitReveal} from "./lib/CommitReveal.sol"; import {Ownable} from "solady/auth/Ownable.sol"; import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; import {IERC4906, IERC165} from "./interfaces/IERC4906.sol"; /** * @title XXYYZZCore * @author emo.eth * @notice Core contract for XXYYZZ NFTs. Contains errors, constants, core token information, and helper functions. */ abstract contract XXYYZZCore is ERC721, IERC4906, CommitReveal, Ownable { error InvalidPayment(); error InvalidHex(); error MaximumSupplyExceeded(); error AlreadyFinalized(); error OnlyTokenOwner(); error NoIdsProvided(); error OwnerMismatch(); error BatchBurnerNotApprovedForAll(); error ArrayLengthMismatch(); error MintClosed(); error InvalidTimestamp(); error OnlyFinalized(); error Unavailable(); error NoneAvailable(); error MaxBatchSizeExceeded(); uint256 public constant MINT_PRICE = 0.005 ether; uint256 public constant REROLL_PRICE = 0.00025 ether; uint256 public constant FINALIZE_PRICE = 0.005 ether; uint256 public constant REROLL_AND_FINALIZE_PRICE = 0.00525 ether; uint256 public immutable MAX_SPECIFIC_BATCH_SIZE; uint256 constant BYTES3_UINT_SHIFT = 232; uint256 constant MAX_UINT24 = 0xFFFFFF; uint96 constant FINALIZED = 1; uint96 constant NOT_FINALIZED = 0; // re-declared from solady ERC721 for custom gas optimizations uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192; mapping(uint256 tokenId => address finalizer) public finalizers; uint128 _numBurned; uint128 _numMinted; constructor(address initialOwner, uint256 maxBatchSize) // lifespan CommitReveal( 1 days, // delay – MM/RPC will report a tx will revert until first eligible block is validated, // so 48 seconds will result in 60 seconds of delay before the frontend will report // that a tx will succeed 48 seconds ) { _initializeOwner(initialOwner); MAX_SPECIFIC_BATCH_SIZE = maxBatchSize; } receive() external payable { // send ether – see what happens! :) } /////////////////// // OWNER METHODS // /////////////////// /** * @notice Withdraws all funds from the contract to the current owner. onlyOwner. */ function withdraw() public onlyOwner { assembly ("memory-safe") { let succ := call(gas(), caller(), selfbalance(), 0, 0, 0, 0) // revert with returndata if call failed if iszero(succ) { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } } /////////////////// // INFORMATIONAL // /////////////////// /** * @notice Get the total number of tokens in circulation */ function totalSupply() public view returns (uint256) { return _numMinted - _numBurned; } /** * @notice Get the total number of tokens minted */ function numMinted() external view returns (uint256) { return _numMinted; } /** * @notice Get the total number of tokens burned */ function numBurned() external view returns (uint256) { return _numBurned; } /** * @notice Get the name of the token */ function name() public pure override returns (string memory) { // note that this is unsafe to call internally, as it abi-encodes the name and // performs a low-level return assembly { mstore(0x20, 0x20) mstore(0x46, 0x06585859595a5a) return(0x20, 0x80) } } /** * @notice Get the symbol of the token */ function symbol() public pure override returns (string memory) { // note that this is unsafe to call internally, as it abi-encodes the symbol and // performs a low-level return assembly { mstore(0x20, 0x20) mstore(0x46, 0x06585859595a5a) return(0x20, 0x80) } } /** * @notice Check if a specific token ID has been finalized. Will return true for tokens that were finalized and * then burned. Will not revert if the tokenID does not currently exist. Will revert on invalid tokenIds. * @param id The token ID to check * @return True if the token ID has been finalized, false otherwise */ function isFinalized(uint256 id) public view returns (bool) { _validateId(id); return _isFinalized(id); } ///@inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) public pure virtual override(ERC721, IERC165) returns (bool result) { assembly { let s := shr(224, interfaceId) // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. ERC4906: 0x49064906 result := or(or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)), eq(s, 0x49064906)) } } ///////////////// // COMMITMENTS // ///////////////// /** * @notice Get a commitment hash for a given sender, tokenId, and salt. Note that this could expose your desired * ID to the RPC provider. Won't revert if the ID is invalid, but will return an invalid hash. * @param sender The address of the account that will mint or reroll the token ID * @param id The 6-hex-digit token ID to mint or reroll * @param salt The salt to use for the commitment */ function computeCommitment(address sender, uint256 id, bytes32 salt) public pure returns (bytes32 committmentHash) { assembly ("memory-safe") { // shift sender left by 24 bits; id stays in bottom 24 mstore(0, or(shl(24, sender), and(id, MAX_UINT24))) mstore(0x20, salt) // start hashing at 0x09 to skip 9 empty bytes (32 - (20 + 3)) committmentHash := keccak256(0x09, 0x40) } } /** * @notice Get a commitment hash for a given sender, array of tokenIds, and salt. This allows for a single * commitment for a batch of IDs, but note that order and length of IDs matters. * If 5 IDs are passed, all 5 must be passed to either batchMintSpecific or batchRerollSpecific, in the * same order. Note that this could expose your desired IDs to the RPC provider. * Won't revert if any IDs are invalid or duplicated. * @param sender The address of the account that will mint or reroll the token IDs * @param ids The 6-hex-digit token IDs to mint or reroll * @param salt The salt to use for the batch commitment */ function computeBatchCommitment(address sender, uint256[] calldata ids, bytes32 salt) public pure returns (bytes32 commitmentHash) { assembly ("memory-safe") { // cache free mem pointer let freeMemPtr := mload(0x40) // multiply length of elements by 32 bytes for each element let numBytes := shl(5, ids.length) // copy contents of array to unallocated free memory calldatacopy(freeMemPtr, ids.offset, numBytes) // hash contents of array, without length let arrayHash := keccak256( // start of array contents freeMemPtr, //length of array contents numBytes ) // store sender in first memory slot mstore(0, sender) // store array hash in second memory slot mstore(0x20, arrayHash) // clobber free memory pointer with salt mstore(0x40, salt) // compute commitment hash // start hashing at 12 bytes since addresses are 20 bytes commitmentHash := keccak256(0x0c, 0x60) // restore free memory pointer mstore(0x40, freeMemPtr) } } ///////////// // HELPERS // ///////////// /** * @dev Mint a token with a specific hex value and validate it was committed to * @param id The 6-hex-digit token ID to mint * @param salt The salt to use for the commitment */ function _mintSpecific(uint256 id, bytes32 salt) internal { bytes32 computedCommitment = computeCommitment(msg.sender, id, salt); // validate ID is valid 6-hex-digit number _validateId(id); // validate commitment to prevent front-running _assertCommittedReveal(computedCommitment); // don't allow minting of tokens that were finalized and then burned if (_isFinalized(id)) { revert AlreadyFinalized(); } _mint(msg.sender, id); } /** * @dev Mint a token with a specific hex value and validate it was committed to * @param id The 6-hex-digit token ID to mint * @param computedCommitment The commitment hash to validate */ function _mintSpecificWithCommitment(uint256 id, bytes32 computedCommitment) internal { // validate ID is valid 6-hex-digit number _validateId(id); // validate commitment to prevent front-running _assertCommittedReveal(computedCommitment); // don't allow minting of tokens that were finalized and then burned if (_packedOwnershipSlot(id) != 0) { revert AlreadyFinalized(); } _mint(msg.sender, id); } /** * @dev Mint a token with a specific hex value without validating it was committed to * @param id The 6-hex-digit token ID to mint * @return True if the token was minted, false otherwise */ function _mintSpecificUnprotected(uint256 id) internal returns (bool) { // validate ID is valid 6-hex-digit number _validateId(id); // don't allow minting of tokens that exist or were finalized and then burned if (_packedOwnershipSlot(id) != 0) { // return false indicating a no-op return false; } // otherwise mint the token _mint(msg.sender, id); return true; } /** * @dev Find the first unminted token ID based on the current number minted and PREVRANDAO * @param seed The seed to use for the random number generation – when minting, should be _numMinted, when * re-rolling, should be a function of the caller. In the case of re-rolling, this means that if a single caller makes * multiple re-rolls in the same block, there will be collisions. This is fine, as the extra gas cost * discourages batch re-rolling with bots or scripts (at least from the same address). */ function _findAvailableHex(uint256 seed) internal view returns (uint256) { uint256 tokenId; assembly { mstore(0, seed) mstore(0x20, prevrandao()) // hash the two values together and then mask to a uint24 // seed is max an address, so start hashing at 0x0c tokenId := and(keccak256(0x0c, 0x40), MAX_UINT24) } // check for the small chance that the token ID is already minted or finalized – if so, increment until we // find one that isn't while (_packedOwnershipSlot(tokenId) != 0) { // safe to do unchecked math here as it is modulo 2^24 unchecked { tokenId = (tokenId + 1) & MAX_UINT24; } } return tokenId; } ///@dev Check if an ID is a valid six-hex-digit number function _validateId(uint256 xxyyzz) internal pure { if (xxyyzz > MAX_UINT24) { revert InvalidHex(); } } ///@dev Validate msg value is equal to total price function _validatePayment(uint256 unitPrice, uint256 quantity) internal view { // can't overflow because there are at most uint24 tokens, and existence is checked for each token down the line unchecked { if (msg.value != (unitPrice * quantity)) { revert InvalidPayment(); } } } /** * @dev Refund any overpayment * @param unitPrice The price per action (mint, reroll, reroll+finalize) * @param availableQuantity The number of tokens (mints, rerolls) that were actually available for purchase */ function _refundOverpayment(uint256 unitPrice, uint256 availableQuantity) internal { unchecked { // can't underflow because payment was already validated; even if it did, value would be larger than ether // supply uint256 overpayment = msg.value - (unitPrice * availableQuantity); if (overpayment != 0) { SafeTransferLib.safeTransferETH(msg.sender, overpayment); } } } /** * @dev Check if a specific token has been finalized. Does not check if token exists. * @param id The 6-hex-digit token ID to check */ function _isFinalized(uint256 id) internal view returns (bool) { return _getExtraData(id) == FINALIZED; } /** * @dev Load the raw ownership slot for a given token ID, which contains both the owner and the extra data * (finalization status). This allows for succint checking of whether or not a token is mintable, * i.e., whether it does not currently exist and has not been finalized. It also allows for avoiding * an extra SLOAD in cases when checking both owner/existence and finalization status. * @param id The 6-hex-digit token ID to check */ function _packedOwnershipSlot(uint256 id) internal view returns (uint256 result) { assembly { // since all ids are < uint24, this basically just clears the 0-slot before writing 4 bytes of slot seed mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := sload(add(id, add(id, keccak256(0x00, 0x20)))) } } function _checkCallerIsOwnerAndNotFinalized(uint256 id) internal view { uint256 packedSlot = _packedOwnershipSlot(id); // clean and cast to address address owner = address(uint160(packedSlot)); if ((packedSlot) > type(uint160).max) { revert AlreadyFinalized(); } // check that caller is owner if (owner != msg.sender) { revert OnlyTokenOwner(); } } /** * @dev Check that array lengths match, the batch size is not too large, and that the payment is correct * @param a The first array to check * @param b The second array to check * @param unitPrice The price per action (mint, reroll, reroll+finalize) */ function _validateRerollBatchAndPayment(uint256[] calldata a, uint256[] calldata b, uint256 unitPrice) internal view { if (a.length != b.length) { revert ArrayLengthMismatch(); } if (a.length > MAX_SPECIFIC_BATCH_SIZE) { revert MaxBatchSizeExceeded(); } _validatePayment(a.length, unitPrice); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {XXYYZZCore} from "./XXYYZZCore.sol"; /** * @title XXYYZZRerollFinalize * @author emo.eth * @notice This contract handles "rerolling" and "finalizing" tokens. * Rerolling allows users to burn a token they own in exchange for a new one. The new token may be either * pseudorandom, or a specific color when using one of the "Specific" methods. * Finalizing allows users to prevent a token from being rerolled again, in addition to adding their * wallet address to the token's metadata as the "Finalizer" trait. */ abstract contract XXYYZZRerollFinalize is XXYYZZCore { //////////// // REROLL // //////////// /** * @notice Burn a token you own and mint a new one with a pseudorandom hex value. * @param oldId The 6-hex-digit token ID to burn * @return The new token ID */ function reroll(uint256 oldId) public payable returns (uint256) { _validatePayment(REROLL_PRICE, 1); // use the caller as the seed to derive the new token ID // this means multiple calls in the same block will be gas-inefficient // which may somewhat discourage botting return _rerollWithSeed(oldId, uint160(msg.sender)); } /** * @notice Burn a number of tokens you own and mint new ones with pseudorandom hex values. * @param ids The 6-hex-digit token IDs to burn in exchange for new tokens * @return The new token IDs */ function batchReroll(uint256[] calldata ids) public payable returns (uint256[] memory) { _validatePayment(REROLL_PRICE, ids.length); // use the caller as the seed to derive the new token IDs // this means multiple calls in the same block will be gas-inefficient // which may somewhat discourage botting uint256 seed = uint256(uint160(msg.sender)); uint256[] memory newIds = new uint256[](ids.length); for (uint256 i; i < ids.length;) { newIds[i] = _rerollWithSeed(ids[i], seed); unchecked { ++i; ++seed; } } return newIds; } /** * @notice Burn and re-mint a token with a specific hex ID. Uses a commit-reveal scheme to prevent front-running. * Only callable by the owner of the token. Users must call `commit(bytes32)` with the result of * `computeCommitment(address,uint256,bytes32)` and wait at least COMMITMENT_LIFESPAN seconds before * calling `rerollSpecific`. * @param oldId The 6-hex-digit token ID to burn * @param newId The 6-hex-digit token ID to mint * @param salt The salt used in the commitment for the new ID commitment */ function rerollSpecific(uint256 oldId, uint256 newId, bytes32 salt) public payable { _validatePayment(REROLL_PRICE, 1); _rerollSpecificWithSalt(oldId, newId, salt); } /** * @notice Burn and re-mint a number of tokens with specific hex values. Uses a commit-reveal scheme to prevent * front-running. Only callable by the owner of the tokens. Users must call `commit(bytes32)` with the * result of `computeBatchCommitment(address,uint256[],bytes32)` and wait at least COMMITMENT_LIFESPAN * seconds before calling `batchRerollSpecific`. * @param oldIds The 6-hex-digit token IDs to burn * @param newIds The 6-hex-digit token IDs to mint * @param salt The salt used in the commitment for the new IDs commitment * @return An array of booleans indicating whether each token was successfully rerolled */ function batchRerollSpecific(uint256[] calldata oldIds, uint256[] calldata newIds, bytes32 salt) public payable returns (bool[] memory) { _validateRerollBatchAndPayment(oldIds, newIds, REROLL_PRICE); bytes32 computedCommitment = computeBatchCommitment(msg.sender, newIds, salt); _assertCommittedReveal(computedCommitment); return _batchRerollAndRefund(oldIds, newIds); } /** * @notice Burn and re-mint a token with a specific hex ID, then finalize it. Uses a commit-reveal scheme to * prevent front-running. Only callable by the owner of the token. Users must call `commit(bytes32)` * with the result of `computeCommitment(address,uint256,bytes32)` and wait at least COMMITMENT_LIFESPAN * seconds before calling `rerollSpecificAndFinalize`. * @param oldId The 6-hex-digit token ID to burn * @param newId The 6-hex-digit token ID to mint * @param salt The salt used in the commitment for the new ID commitment */ function rerollSpecificAndFinalize(uint256 oldId, uint256 newId, bytes32 salt) public payable { _validatePayment(REROLL_AND_FINALIZE_PRICE, 1); _rerollSpecificWithSalt(oldId, newId, salt); // won't re-validate price, but above function already did _finalizeToken(newId, msg.sender); } /** * @notice Burn and re-mint a number of tokens with specific hex values, then finalize them. * @param oldIds The 6-hex-digit token IDs to burn * @param newIds The 6-hex-digit token IDs to mint * @param salt The salt used in the batch commitment for the new ID commitment * @return An array of booleans indicating whether each token was successfully rerolled */ function batchRerollSpecificAndFinalize(uint256[] calldata oldIds, uint256[] calldata newIds, bytes32 salt) public payable returns (bool[] memory) { _validateRerollBatchAndPayment(oldIds, newIds, REROLL_AND_FINALIZE_PRICE); bytes32 computedCommitment = computeBatchCommitment(msg.sender, newIds, salt); _assertCommittedReveal(computedCommitment); return _batchRerollAndFinalizeAndRefund(oldIds, newIds); } ////////////// // FINALIZE // ////////////// /** * @notice Finalize a token, which updates its metadata with a "Finalizer" trait and prevents it from being * rerolled in the future. * @param id The 6-hex-digit token ID to finalize. Must be owned by the caller. */ function finalize(uint256 id) public payable { _validatePayment(FINALIZE_PRICE, 1); _finalize(id); } /** * @notice Finalize a number of tokens, which updates their metadata with a "Finalizer" trait and prevents them * from being rerolled in the future. The caller must pay the finalization price for each token, and must * own all tokens. * @param ids The 6-hex-digit token IDs to finalize */ function batchFinalize(uint256[] calldata ids) public payable { _validatePayment(FINALIZE_PRICE, ids.length); for (uint256 i; i < ids.length;) { _finalize(ids[i]); unchecked { ++i; } } } ////////////// // INTERNAL // ////////////// /** * @dev Internal function to burn and re-mint tokens with a specific hex ID. Does not check initial payment. * Does refund any overpayment. * @param oldIds The 6-hex-digit token IDs to burn * @param newIds The 6-hex-digit token IDs to mint * @return An array of booleans indicating whether each token was successfully rerolled */ function _batchRerollAndRefund(uint256[] calldata oldIds, uint256[] calldata newIds) internal returns (bool[] memory) { bool[] memory rerolled = new bool[](oldIds.length); uint256 quantityRerolled; for (uint256 i; i < oldIds.length;) { if (_rerollSpecificUnprotected(oldIds[i], newIds[i])) { rerolled[i] = true; unchecked { ++quantityRerolled; } } unchecked { ++i; } } // if none were rerolled, revert to avoid wasting further gas if (quantityRerolled == 0) { revert NoneAvailable(); } // refund any overpayment _refundOverpayment(REROLL_PRICE, quantityRerolled); return rerolled; } /** * @dev Internal function to burn and re-mint tokens with a specific hex ID, then finalize them. Does not check * initial payment. Does refund any overpayment. * @param oldIds The 6-hex-digit token IDs to burn * @param newIds The 6-hex-digit token IDs to mint * @return An array of booleans indicating whether each token was successfully rerolled */ function _batchRerollAndFinalizeAndRefund(uint256[] calldata oldIds, uint256[] calldata newIds) internal returns (bool[] memory) { bool[] memory rerolled = new bool[](oldIds.length); uint256 quantityRerolled; for (uint256 i; i < oldIds.length;) { if (_rerollSpecificUnprotected(oldIds[i], newIds[i])) { _finalizeToken(newIds[i], msg.sender); rerolled[i] = true; unchecked { ++quantityRerolled; } } unchecked { ++i; } } // if none were rerolled, revert to avoid wasting gas if (quantityRerolled == 0) { revert NoneAvailable(); } // refund any overpayment _refundOverpayment(REROLL_AND_FINALIZE_PRICE, quantityRerolled); return rerolled; } /** * @dev Validate an old tokenId is rerollable, burn it, then mint a token with a pseudorandom * hex ID. * @param oldId The old ID to reroll * @param seed The seed to use for the reroll * */ function _rerollWithSeed(uint256 oldId, uint256 seed) internal returns (uint256) { _checkCallerIsOwnerAndNotFinalized(oldId); // burn old token _burn(oldId); uint256 tokenId = _findAvailableHex(seed); _mint(msg.sender, tokenId); return tokenId; } /** * @dev Validate an old tokenId is rerollable, burn it, then mint a token with a specific * hex ID, validating that the commit-reveal scheme was followed. * @param oldId The old ID to reroll * @param newId The new ID to mint * @param salt The salt used in the commit-reveal scheme */ function _rerollSpecificWithSalt(uint256 oldId, uint256 newId, bytes32 salt) internal { _checkCallerIsOwnerAndNotFinalized(oldId); // burn old token _burn(oldId); _mintSpecific(newId, salt); } /** * @dev Validate an old tokenId is rerollable, mint a token with a specific new hex ID (if available) * and burn the old token. * @param oldId The old ID to reroll * @param newId The new ID to mint * @return Whether the mint succeeded, ie, the new ID was available */ function _rerollSpecificUnprotected(uint256 oldId, uint256 newId) internal returns (bool) { _checkCallerIsOwnerAndNotFinalized(oldId); // only burn old token if mint succeeded if (_mintSpecificUnprotected(newId)) { _burn(oldId); return true; } return false; } /** * @dev Internal function to finalize a token, first checking that the caller is the owner and that the token * has not already been finalized. * @param id The 6-hex-digit token ID to finalize */ function _finalize(uint256 id) internal { _checkCallerIsOwnerAndNotFinalized(id); // set finalized flag _finalizeToken(id, msg.sender); // emit onchain metadata update event emit MetadataUpdate(id); } /** * @dev Finalize a tokenId, updating its metadata with a "Finalizer" trait, and preventing it from being rerolled in the future. * @param id The 6-hex-digit token ID to finalize * @param finalizer The address of the account finalizing the token */ function _finalizeToken(uint256 id, address finalizer) internal { finalizers[id] = finalizer; _setExtraData(id, 1); } }
// 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) library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `length` of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { /// @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. str := add(mload(0x40), 0x80) // Update the free memory pointer to allocate. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str 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 {} { str := add(str, w) // `sub(str, 1)`. // Write the character to the pointer. // The ASCII index of the '0' character is 48. mstore8(str, add(48, mod(temp, 10))) // Keep dividing `temp` until zero. temp := div(temp, 10) if iszero(temp) { break } } let length := sub(end, str) // Move the pointer 32 bytes leftwards to make room for the length. str := sub(str, 0x20) // Store the length. mstore(str, length) } } /// @dev Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory str) { if (value >= 0) { return toString(uint256(value)); } unchecked { str = toString(uint256(-value)); } /// @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 length := mload(str) // Load the string length. mstore(str, 0x2d) // Store the '-' character. str := sub(str, 1) // Move back the string pointer by a byte. mstore(str, add(length, 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 str) { str = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @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` 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 str) { /// @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. str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let start := sub(str, 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 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(str, start)) { break } } if temp { // Store the function selector of `HexLengthInsufficient()`. mstore(0x00, 0x2194895a) // Revert with (offset, size). revert(0x1c, 0x04) } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @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 str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @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 str) { /// @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. str := add(mload(0x40), 0x80) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) 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 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @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 toHexStringChecksumed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 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 str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write 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 str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the 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(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) 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 str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write 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 str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) 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 the 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 { let mask := shl(7, div(not(0), 255)) result := 1 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) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, all indices of the following operations // are byte (ASCII) offsets, not UTF character offsets. /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`. function replace(string memory subject, string memory search, string memory replacement) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) let replacementLength := mload(replacement) subject := add(subject, 0x20) search := add(search, 0x20) replacement := add(replacement, 0x20) result := add(mload(0x40), 0x20) let subjectEnd := add(subject, subjectLength) if iszero(gt(searchLength, subjectLength)) { let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } } let resultRemainder := result result := add(mload(0x40), 0x20) let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) // Copy the rest of the string one word at a time. for {} lt(subject, subjectEnd) {} { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) let last := add(add(result, 0x20), k) // Zeroize the slot after the string. mstore(last, 0) mstore(0x40, add(last, 0x20)) // Allocate the memory. mstore(result, k) // Store the length. } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { if iszero(gt(from, subjectLength)) { result := from break } result := subjectLength break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(add(search, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLength))) { break } if iszero(lt(searchLength, 0x20)) { for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, searchLength), 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 `search` in `subject`, /// searching from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = indexOf(subject, search, 0); } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let searchLength := mload(search) if gt(searchLength, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), searchLength) 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(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), 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 `search` in `subject`, /// searching from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = lastIndexOf(subject, search, uint256(int256(-1))); } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( iszero(gt(searchLength, mload(subject))), eq( keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns whether `subject` ends with `search`. function endsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) let subjectLength := mload(subject) // Whether `search` is not longer than `subject`. let withinRange := iszero(gt(searchLength, subjectLength)) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( withinRange, eq( keccak256( // `subject + 0x20 + max(subjectLength - searchLength, 0)`. add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), searchLength ), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) if iszero(times) { break } } mstore(output, 0) // Zeroize the slot after the string. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Store the length. // Allocate the memory. mstore(0x40, add(result, add(resultLength, 0x20))) } } } /// @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 subjectLength := mload(subject) if iszero(gt(subjectLength, end)) { end := subjectLength } if iszero(gt(subjectLength, start)) { start := subjectLength } if lt(start, end) { result := mload(0x40) let resultLength := sub(end, start) mstore(result, resultLength) subject := add(subject, start) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let o := and(add(resultLength, 0x1f), w) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(result, 0x20), resultLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 0x3f), w))) } } } /// @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, uint256(int256(-1))); } /// @dev Returns all the indices of `search` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) if iszero(gt(searchLength, subjectLength)) { subject := add(subject, 0x20) search := add(search, 0x20) result := add(mload(0x40), 0x20) let subjectStart := subject let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Append to `result`. mstore(result, sub(subject, subjectStart)) result := add(result, 0x20) // Advance `subject` by `searchLength`. subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } let resultEnd := result // Assign `result` to the free memory pointer. result := mload(0x40) // Store the length of `result`. mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(resultEnd, 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)) let prevIndex := 0 for {} 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let elementLength := sub(index, prevIndex) mstore(element, elementLength) // Copy the `subject` one word at a time, backwards. for { let o := and(add(elementLength, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(element, 0x20), elementLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(element, and(add(elementLength, 0x3f), w))) // Store the `element` into the array. mstore(indexPtr, element) } 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 { let w := not(0x1f) result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(mload(a), 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLength := mload(b) let output := add(result, mload(a)) // Copy `b` one word at a time, backwards. for { let o := and(add(bLength, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLength := add(aLength, bLength) let last := add(add(result, 0x20), totalLength) // Zeroize the slot after the string. mstore(last, 0) // Stores the length. mstore(result, totalLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 0x1f), w)) } } /// @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 length := mload(subject) if length { result := add(mload(0x40), 0x20) subject := add(subject, 1) let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) let w := not(0) for { let o := length } 1 {} { o := add(o, w) let b := and(0xff, mload(add(subject, o))) mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20))) if iszero(o) { break } } result := mload(0x40) mstore(result, length) // Store the length. let last := add(add(result, 0x20), length) mstore(last, 0) // Zeroize the slot after the string. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } } /// @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 { for { let end := add(s, mload(s)) result := add(mload(0x40), 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)) } iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(result, c) result := add(result, 1) continue } let t := shr(248, mload(c)) mstore(result, mload(and(t, 0x1f))) result := add(result, shr(5, t)) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the 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) { /// @solidity memory-safe-assembly assembly { for { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) // 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)) } 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(result, c) result := add(result, 1) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), c) result := add(result, 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(result, mload(0x19)) // "\\u00XX". result := add(result, 6) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), mload(add(c, 8))) result := add(result, 2) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @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 behaviour is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. result := mload(0x40) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(0x40, add(result, 0x40)) // Zeroize the length slot. mstore(result, 0) // Store the length and bytes. mstore(add(result, 0x1f), packed) // Right pad with zeroes. mstore(add(add(result, 0x20), mload(result)), 0) } } /// @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 aLength := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes of `a` and `b`. or( shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength)) ), // `totalLength != 0 && totalLength < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLength, 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 behaviour is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. resultA := mload(0x40) 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 retSize := 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, retSize), 0) // Store the return offset. mstore(retStart, 0x20) // End the transaction, returning the string. return(retStart, retSize) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library to encode strings in Base64. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol) /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>. library Base64 { /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// See: https://datatracker.ietf.org/doc/html/rfc4648 /// @param fileSafe Whether to replace '+' with '-' and '/' with '_'. /// @param noPadding Whether to strip away the padding. function encode(bytes memory data, bool fileSafe, bool noPadding) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let dataLength := mload(data) if dataLength { // Multiply by 4/3 rounded up. // The `shl(2, ...)` is equivalent to multiplying by 4. let encodedLength := shl(2, div(add(dataLength, 2), 3)) // Set `result` to point to the start of the free memory. result := mload(0x40) // Store the table into the scratch space. // Offsetted by -1 byte so that the `mload` will load the character. // We will rewrite the free memory pointer at `0x40` later with // the allocated size. // The magic constant 0x0230 will translate "-_" + "+/". mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230))) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, encodedLength) // Run over the input, 3 bytes at a time. for {} 1 {} { data := add(data, 3) // Advance 3 bytes. let input := mload(data) // Write 4 bytes. Optimized for fewer stack operations. mstore8(0, mload(and(shr(18, input), 0x3F))) mstore8(1, mload(and(shr(12, input), 0x3F))) mstore8(2, mload(and(shr(6, input), 0x3F))) mstore8(3, mload(and(input, 0x3F))) mstore(ptr, mload(0x00)) ptr := add(ptr, 4) // Advance 4 bytes. if iszero(lt(ptr, end)) { break } } mstore(0x40, add(end, 0x20)) // Allocate the memory. // Equivalent to `o = [0, 2, 1][dataLength % 3]`. let o := div(2, mod(dataLength, 3)) // Offset `ptr` and pad with '='. We can simply write over the end. mstore(sub(ptr, o), shl(240, 0x3d3d)) // Set `o` to zero if there is padding. o := mul(iszero(iszero(noPadding)), o) mstore(sub(ptr, o), 0) // Zeroize the slot after the string. mstore(result, sub(encodedLength, o)) // Store the length. } } } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, false, false)`. function encode(bytes memory data) internal pure returns (string memory result) { result = encode(data, false, false); } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, fileSafe, false)`. function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) { result = encode(data, fileSafe, false); } /// @dev Decodes base64 encoded `data`. /// /// Supports: /// - RFC 4648 (both standard and file-safe mode). /// - RFC 3501 (63: ','). /// /// Does not support: /// - Line breaks. /// /// Note: For performance reasons, /// this function will NOT revert on invalid `data` inputs. /// Outputs for invalid inputs will simply be undefined behaviour. /// It is the user's responsibility to ensure that the `data` /// is a valid base64 encoded string. function decode(string memory data) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { let dataLength := mload(data) if dataLength { let decodedLength := mul(shr(2, dataLength), 3) for {} 1 {} { // If padded. if iszero(and(dataLength, 3)) { let t := xor(mload(add(data, dataLength)), 0x3d3d) // forgefmt: disable-next-item decodedLength := sub( decodedLength, add(iszero(byte(30, t)), iszero(byte(31, t))) ) break } // If non-padded. decodedLength := add(decodedLength, sub(and(dataLength, 3), 1)) break } result := mload(0x40) // Write the length of the bytes. mstore(result, decodedLength) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, decodedLength) // Load the table into the scratch space. // Constants are optimized for smaller bytecode with zero gas overhead. // `m` also doubles as the mask of the upper 6 bits. let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc mstore(0x5b, m) mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064) mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4) for {} 1 {} { // Read 4 bytes. data := add(data, 4) let input := mload(data) // Write 3 bytes. // forgefmt: disable-next-item mstore(ptr, or( and(m, mload(byte(28, input))), shr(6, or( and(m, mload(byte(29, input))), shr(6, or( and(m, mload(byte(30, input))), shr(6, mload(byte(31, input))) )) )) )) ptr := add(ptr, 3) if iszero(lt(ptr, end)) { break } } mstore(0x40, add(end, 0x20)) // Allocate the memory. mstore(end, 0) // Zeroize the slot after the bytes. mstore(0x60, 0) // Restore the zero slot. } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {XXYYZZCore} from "./XXYYZZCore.sol"; /** * @title XXYYZZMint * @author emo.eth * @notice This contract handles minting of XXYYZZ tokens. * Tokens may be minted with a pseudorandom hex value, or with a specific hex value. * The "Specific" methods allow for minting tokens with specific hex values with a commit-reveal scheme. * Users may protect themselves against front-running by */ abstract contract XXYYZZMint is XXYYZZCore { uint256 public immutable MAX_MINT_CLOSE_TIMESTAMP; constructor(address initialOwner, uint256 maxBatchSize) XXYYZZCore(initialOwner, maxBatchSize) { MAX_MINT_CLOSE_TIMESTAMP = block.timestamp + 14 days; } ////////// // MINT // ////////// /** * @notice Mint a token with a pseudorandom hex value. * @return The token ID */ function mint() public payable returns (uint256) { uint256 newAmount = _checkMintAndIncrementNumMinted(1); // get pseudorandom hex id – doesn't need to be derived from caller uint256 tokenId = _findAvailableHex(newAmount); _mint(msg.sender, tokenId); return tokenId; } /** * @notice Mint a number of tokens with pseudorandom hex values. * @param quantity The number of tokens to mint * @return The token IDs */ function mint(uint256 quantity) public payable returns (uint256[] memory) { return _checkMintTo(msg.sender, quantity); } function mintTo(address to, uint256 quantity) public payable returns (uint256[] memory) { return _checkMintTo(to, quantity); } /** * @notice Mint a token with a specific hex value. * A user must first call commit(bytes32) or batchCommit(bytes32[]) with the result(s) of * computeCommittment(address,uint256,bytes32), and wait at least one minute. * When calling mintSpecific, the "salt" should be the bytes32 salt provided to `computeCommitment` when * creating the commitment hash. * * Example: To register 0x123456 with salt bytes32(0xDEADBEEF) * 1. Call `computeCommitment(<minting addr>, 0x123456, bytes32(0xDEADBEEF))` for `bytes32 result` * 2. Call `commit(result)` * 3. Wait at least 1 minute, but less than 1 day * 4. Call `mintSpecific(0x123456, bytes32(0xDEADBEEF))` * @param id The 6-hex-digit token ID to mint * @param salt The salt used in the commitment for the commitment */ function mintSpecific(uint256 id, bytes32 salt) public payable { _checkMintAndIncrementNumMinted(1); _mintSpecific(id, salt); } /** * @notice Mint a number of tokens with specific hex values. * A user must first call commit(bytes32) with the result of * `computeBatchCommitment(address,uint256[],bytes32)`, and wait at least COMMITMENT_LIFESPAN seconds. * @param ids The 6-hex-digit token IDs to mint * @param salt The salt used in the batch commitment * @return An array of booleans indicating whether each token was minted */ function batchMintSpecific(uint256[] calldata ids, bytes32 salt) public payable returns (bool[] memory) { _validateBatchMintAndTimestamp(ids); bytes32 computedCommitment = computeBatchCommitment(msg.sender, ids, salt); _assertCommittedReveal(computedCommitment); return _batchMintAndIncrementAndRefund(ids); } ///////////// // HELPERS // ///////////// /** * @dev Mint tokens, validate that tokens were minted, and increment the number of minted tokens * @param to Recipient of the tokens * @param quantity Number of tokens to mint */ function _checkMintTo(address to, uint256 quantity) internal returns (uint256[] memory) { // check payment and quantity once uint256 newAmount = _checkMintAndIncrementNumMinted(quantity); return _mintTo(to, quantity, newAmount); } /** * @dev Mint tokens, validate that tokens were minted, and increment the number of minted tokens * @param to Recipient of the tokens * @param quantity Number of tokens to mint */ function _mintTo(address to, uint256 quantity, uint256 newAmount) internal returns (uint256[] memory) { uint256[] memory tokenIds = new uint256[](quantity); for (uint256 i; i < quantity;) { // get pseudorandom hex id uint256 tokenId = _findAvailableHex(newAmount); _mint(to, tokenId); tokenIds[i] = tokenId; unchecked { ++i; ++newAmount; } } return tokenIds; } /** * @dev Mint tokens, validate that tokens were minted, increment the number of minted tokens, and refund any * overpayment * @param ids The 6-hex-digit token IDs to mint */ function _batchMintAndIncrementAndRefund(uint256[] calldata ids) internal returns (bool[] memory) { bool[] memory minted = new bool[](ids.length); uint256 quantityMinted; for (uint256 i; i < ids.length;) { if (_mintSpecificUnprotected(ids[i])) { minted[i] = true; unchecked { ++quantityMinted; } } unchecked { ++i; } } if (quantityMinted == 0) { revert NoneAvailable(); } _incrementNumMintedAndRefundOverpayment(quantityMinted); return minted; } /** * @dev Check payment and quantity validation – quantityRequested for payment, quantityAvailable for updating * the number of minted tokens, which may be different * @param quantityRequested The number of tokens requested by the user, which must be paid for * @return The new number of minted tokens */ function _checkMintAndIncrementNumMinted(uint256 quantityRequested) internal returns (uint256) { if (block.timestamp > MAX_MINT_CLOSE_TIMESTAMP) { revert MintClosed(); } _validatePayment(MINT_PRICE, quantityRequested); // increment supply before minting uint128 newAmount; // this can be unchecked because an ID can only be minted once, and all IDs are later validated to be uint24s unchecked { newAmount = _numMinted + uint128(quantityRequested); } _numMinted = newAmount; return newAmount; } /** * @dev Increment the number of minted tokens and refund any overpayment * @param quantity The number of tokens actually minted */ function _incrementNumMintedAndRefundOverpayment(uint256 quantity) internal returns (uint256) { uint256 newAmount; // this can be unchecked because an ID can only be minted once, and all IDs are validated to be uint24s // overflow here implies invalid IDs down the line, which will cause a revert when minting unchecked { newAmount = _numMinted + quantity; } _numMinted = uint32(newAmount); _refundOverpayment(MINT_PRICE, quantity); return newAmount; } /** * @dev Validate the timestamp and payment for a batch mint * @param ids The 6-hex-digit token IDs to mint */ function _validateBatchMintAndTimestamp(uint256[] calldata ids) internal view { if (block.timestamp > MAX_MINT_CLOSE_TIMESTAMP) { revert MintClosed(); } if (ids.length > MAX_SPECIFIC_BATCH_SIZE) { revert MaxBatchSizeExceeded(); } _validatePayment(ids.length, MINT_PRICE); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; struct PublicDrop { uint80 mintPrice; // 80/256 bits uint48 startTime; // 128/256 bits uint48 endTime; // 176/256 bits uint16 maxTotalMintableByWallet; // 224/256 bits uint16 feeBps; // 240/256 bits bool restrictFeeRecipients; // 248/256 bits } interface ISeaDrop { /** * @notice Updates the public drop data for the nft contract * and emits an event. * * This method assume msg.sender is an nft contract and its * ERC165 interface id matches INonFungibleSeaDropToken. * * Note: Be sure only authorized users can call this from * token contracts that implement INonFungibleSeaDropToken. * * @param publicDrop The public drop data. */ function updatePublicDrop(PublicDrop calldata publicDrop) external; /** * @notice Updates the creator payout address and emits an event. * * This method assume msg.sender is an nft contract and its * ERC165 interface id matches INonFungibleSeaDropToken. * * Note: Be sure only authorized users can call this from * token contracts that implement INonFungibleSeaDropToken. * * @param payoutAddress The creator payout address. */ function updateCreatorPayoutAddress(address payoutAddress) external; /** * @notice Updates the allowed fee recipient and emits an event. * * This method assume msg.sender is an nft contract and its * ERC165 interface id matches INonFungibleSeaDropToken. * * Note: Be sure only authorized users can call this from * token contracts that implement INonFungibleSeaDropToken. * * @param feeRecipient The fee recipient. * @param allowed If the fee recipient is allowed. */ function updateAllowedFeeRecipient(address feeRecipient, bool allowed) external; /** * @notice Updates the allowed payer and emits an event. * * This method assume msg.sender is an nft contract and its * ERC165 interface id matches INonFungibleSeaDropToken. * * Note: Be sure only authorized users can call this from * token contracts that implement INonFungibleSeaDropToken. * * @param payer The payer to add or remove. * @param allowed Whether to add or remove the payer. */ function updatePayer(address payer, bool allowed) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple ERC721 implementation with storage hitchhiking. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol) /// Note: /// The ERC721 standard allows for self-approvals. /// For performance, this implementation WILL NOT revert for such actions. /// Please add any checks with overrides if desired. abstract contract ERC721 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev An account can hold up to 4294967295 tokens. uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Only the token owner or an approved account can manage the token. error NotOwnerNorApproved(); /// @dev The token does not exist. error TokenDoesNotExist(); /// @dev The token already exists. error TokenAlreadyExists(); /// @dev Cannot query the balance for the zero address. error BalanceQueryForZeroAddress(); /// @dev Cannot mint or transfer to the zero address. error TransferToZeroAddress(); /// @dev The token must be owned by `from`. error TransferFromIncorrectOwner(); /// @dev The recipient's balance has overflowed. error AccountBalanceOverflow(); /// @dev Cannot safely transfer to a contract that does not implement /// the ERC721Receiver interface. error TransferToNonERC721ReceiverImplementer(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Emitted when token `id` is transferred from `from` to `to`. event Transfer(address indexed from, address indexed to, uint256 indexed id); /// @dev Emitted when `owner` enables `account` to manage the `id` token. event Approval(address indexed owner, address indexed account, uint256 indexed id); /// @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 `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("ApprovalForAll(address,address,bool)"))`. uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE = 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership data slot of `id` is given by: /// ``` /// mstore(0x00, id) /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) /// let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) /// ``` /// Bits Layout: // - [0..159] `addr` // - [160..223] `extraData` /// /// The approved address slot is given by: `add(1, ownershipSlot)`. /// /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip /// /// The balance slot of `owner` is given by: /// ``` /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) /// mstore(0x00, owner) /// let balanceSlot := keccak256(0x0c, 0x1c) /// ``` /// Bits Layout: /// - [0..31] `balance` /// - [32..225] `aux` /// /// The `operator` approval slot of `owner` is given by: /// ``` /// mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) /// mstore(0x00, owner) /// let operatorApprovalSlot := keccak256(0x0c, 0x30) /// ``` uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192; /// @dev Pre-shifted and pre-masked constant. uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC721 METADATA */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the token collection name. function name() public view virtual returns (string memory); /// @dev Returns the token collection symbol. function symbol() public view virtual returns (string memory); /// @dev Returns the Uniform Resource Identifier (URI) for token `id`. function tokenURI(uint256 id) public view virtual returns (string memory); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC721 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of token `id`. /// /// Requirements: /// - Token `id` must exist. function ownerOf(uint256 id) public view virtual returns (address result) { result = _ownerOf(id); /// @solidity memory-safe-assembly assembly { if iszero(result) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } } } /// @dev Returns the number of tokens owned by `owner`. /// /// Requirements: /// - `owner` must not be the zero address. function balanceOf(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Revert if the `owner` is the zero address. if iszero(owner) { mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`. revert(0x1c, 0x04) } mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE) } } /// @dev Returns the account approved to managed token `id`. /// /// Requirements: /// - Token `id` must exist. function getApproved(uint256 id) public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) if iszero(shr(96, shl(96, sload(ownershipSlot)))) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } result := sload(add(1, ownershipSlot)) } } /// @dev Sets `account` as the approved account to manage token `id`. /// /// Requirements: /// - Token `id` must exist. /// - The caller must be the owner of the token, /// or an approved operator for the token owner. /// /// Emits a {Approval} event. function approve(address account, uint256 id) public payable virtual { _approve(msg.sender, account, id); } /// @dev Returns whether `operator` is approved to manage the tokens of `owner`. function isApprovedForAll(address owner, address operator) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { mstore(0x1c, operator) mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x30)) } } /// @dev Sets whether `operator` is approved to manage the tokens of the caller. /// /// Emits a {ApprovalForAll} event. function setApprovalForAll(address operator, bool isApproved) public virtual { /// @solidity memory-safe-assembly assembly { // Convert to 0 or 1. isApproved := iszero(iszero(isApproved)) // Update the `isApproved` for (`msg.sender`, `operator`). mstore(0x1c, operator) mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x30), isApproved) // Emit the {ApprovalForAll} event. mstore(0x00, isApproved) log3( 0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)) ) } } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 id) public payable virtual { _beforeTokenTransfer(from, to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) from := and(bitmaskAddress, from) to := and(bitmaskAddress, to) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller())) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) let owner := and(bitmaskAddress, ownershipPacked) // Revert if `from` is not the owner, or does not exist. if iszero(mul(owner, eq(owner, from))) { if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`. revert(0x1c, 0x04) } // Revert if `to` is the zero address. if iszero(to) { mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. revert(0x1c, 0x04) } // Load, check, and update the token approval. { mstore(0x00, from) let approvedAddress := sload(add(1, ownershipSlot)) // Revert if the caller is not the owner, nor approved. if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Update with the new owner. sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) // Decrement the balance of `from`. { let fromBalanceSlot := keccak256(0x0c, 0x1c) sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) } // Increment the balance of `to`. { mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x1c) let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) { mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. revert(0x1c, 0x04) } sstore(toBalanceSlot, toBalanceSlotPacked) } // Emit the {Transfer} event. log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) } _afterTokenTransfer(from, to, id); } /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`. function safeTransferFrom(address from, address to, uint256 id) public payable virtual { transferFrom(from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function safeTransferFrom(address from, address to, uint256 id, bytes calldata data) public payable virtual { transferFrom(from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, 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, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL QUERY FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns if token `id` exists. function _exists(uint256 id) internal view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))) } } /// @dev Returns the owner of token `id`. /// Returns the zero address instead of reverting if the token does not exist. function _ownerOf(uint256 id) internal view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL 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 (uint224 result) { /// @solidity memory-safe-assembly assembly { mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) result := shr(32, sload(keccak256(0x0c, 0x1c))) } } /// @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, uint224 value) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) let balanceSlot := keccak256(0x0c, 0x1c) let packed := sload(balanceSlot) sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed))))) } } /// @dev Returns the extra data for token `id`. /// Minting, transferring, burning a token will not change the extra data. /// The extra data can be set on a non existent token. function _getExtraData(uint256 id) internal view virtual returns (uint96 result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20))))) } } /// @dev Sets the extra data for token `id` to `value`. /// Minting, transferring, burning a token will not change the extra data. /// The extra data can be set on a non existent token. function _setExtraData(uint256 id, uint96 value) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let packed := sload(ownershipSlot) sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed))))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL MINT FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Mints token `id` to `to`. /// /// Requirements: /// /// - Token `id` must not exist. /// - `to` cannot be the zero address. /// /// Emits a {Transfer} event. function _mint(address to, uint256 id) internal virtual { _beforeTokenTransfer(address(0), to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. to := shr(96, shl(96, to)) // Revert if `to` is the zero address. if iszero(to) { mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. revert(0x1c, 0x04) } // Load the ownership data. mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) // Revert if the token already exists. if shl(96, ownershipPacked) { mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`. revert(0x1c, 0x04) } // Update with the owner. sstore(ownershipSlot, or(ownershipPacked, to)) // Increment the balance of the owner. { mstore(0x00, to) let balanceSlot := keccak256(0x0c, 0x1c) let balanceSlotPacked := add(sload(balanceSlot), 1) if iszero(and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE)) { mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. revert(0x1c, 0x04) } sstore(balanceSlot, balanceSlotPacked) } // Emit the {Transfer} event. log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id) } _afterTokenTransfer(address(0), to, id); } /// @dev Equivalent to `_safeMint(to, id, "")`. function _safeMint(address to, uint256 id) internal virtual { _safeMint(to, id, ""); } /// @dev Mints token `id` to `to`. /// /// Requirements: /// /// - Token `id` must not exist. /// - `to` cannot be the zero address. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeMint(address to, uint256 id, bytes memory data) internal virtual { _mint(to, id); if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL BURN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `_burn(address(0), id)`. function _burn(uint256 id) internal virtual { _burn(address(0), id); } /// @dev Destroys token `id`, using `by`. /// /// Requirements: /// /// - Token `id` must exist. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function _burn(address by, uint256 id) internal virtual { address owner = ownerOf(id); _beforeTokenTransfer(owner, address(0), id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. by := shr(96, shl(96, by)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) // Reload the owner in case it is changed in `_beforeTokenTransfer`. owner := shr(96, shl(96, ownershipPacked)) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // Load and check the token approval. { mstore(0x00, owner) let approvedAddress := sload(add(1, ownershipSlot)) // If `by` is not the zero address, do the authorization check. // Revert if the `by` is not the owner, nor approved. if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Clear the owner. sstore(ownershipSlot, xor(ownershipPacked, owner)) // Decrement the balance of `owner`. { let balanceSlot := keccak256(0x0c, 0x1c) sstore(balanceSlot, sub(sload(balanceSlot), 1)) } // Emit the {Transfer} event. log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id) } _afterTokenTransfer(owner, address(0), id); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL APPROVAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `account` is the owner of token `id`, or is approved to managed it. /// /// Requirements: /// - Token `id` must exist. function _isApprovedOrOwner(address account, uint256 id) internal view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { result := 1 // Clear the upper 96 bits. account := shr(96, shl(96, account)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let owner := shr(96, shl(96, sload(ownershipSlot))) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // Check if `account` is the `owner`. if iszero(eq(account, owner)) { mstore(0x00, owner) // Check if `account` is approved to if iszero(sload(keccak256(0x0c, 0x30))) { result := eq(account, sload(add(1, ownershipSlot))) } } } } /// @dev Returns the account approved to manage token `id`. /// Returns the zero address instead of reverting if the token does not exist. function _getApproved(uint256 id) internal view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20))))) } } /// @dev Equivalent to `_approve(address(0), account, id)`. function _approve(address account, uint256 id) internal virtual { _approve(address(0), account, id); } /// @dev Sets `account` as the approved account to manage token `id`, using `by`. /// /// Requirements: /// - Token `id` must exist. /// - If `by` is not the zero address, `by` must be the owner /// or an approved operator for the token owner. /// /// Emits a {Transfer} event. function _approve(address by, address account, uint256 id) internal virtual { assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) account := and(bitmaskAddress, account) by := and(bitmaskAddress, by) // Load the owner of the token. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let owner := and(bitmaskAddress, sload(ownershipSlot)) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // If `by` is not the zero address, do the authorization check. // Revert if `by` is not the owner, nor approved. if iszero(or(iszero(by), eq(by, owner))) { mstore(0x00, owner) if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Sets `account` as the approved account to manage `id`. sstore(add(1, ownershipSlot), account) // Emit the {Approval} event. log4(0x00, 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id) } } /// @dev Approve or remove the `operator` as an operator for `by`, /// without authorization checks. /// /// Emits a {ApprovalForAll} event. function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual { /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. by := shr(96, shl(96, by)) operator := shr(96, shl(96, operator)) // Convert to 0 or 1. isApproved := iszero(iszero(isApproved)) // Update the `isApproved` for (`by`, `operator`). mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) mstore(0x00, by) sstore(keccak256(0x0c, 0x30), isApproved) // Emit the {ApprovalForAll} event. mstore(0x00, isApproved) log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL TRANSFER FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `_transfer(address(0), from, to, id)`. function _transfer(address from, address to, uint256 id) internal virtual { _transfer(address(0), from, to, id); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function _transfer(address by, address from, address to, uint256 id) internal virtual { _beforeTokenTransfer(from, to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) from := and(bitmaskAddress, from) to := and(bitmaskAddress, to) by := and(bitmaskAddress, by) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) let owner := and(bitmaskAddress, ownershipPacked) // Revert if `from` is not the owner, or does not exist. if iszero(mul(owner, eq(owner, from))) { if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`. revert(0x1c, 0x04) } // Revert if `to` is the zero address. if iszero(to) { mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. revert(0x1c, 0x04) } // Load, check, and update the token approval. { mstore(0x00, from) let approvedAddress := sload(add(1, ownershipSlot)) // If `by` is not the zero address, do the authorization check. // Revert if the `by` is not the owner, nor approved. if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Update with the new owner. sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) // Decrement the balance of `from`. { let fromBalanceSlot := keccak256(0x0c, 0x1c) sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) } // Increment the balance of `to`. { mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x1c) let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) { mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. revert(0x1c, 0x04) } sstore(toBalanceSlot, toBalanceSlotPacked) } // Emit the {Transfer} event. log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) } _afterTokenTransfer(from, to, id); } /// @dev Equivalent to `_safeTransfer(from, to, id, "")`. function _safeTransfer(address from, address to, uint256 id) internal virtual { _safeTransfer(from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeTransfer(address from, address to, uint256 id, bytes memory data) internal virtual { _transfer(address(0), from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`. function _safeTransfer(address by, address from, address to, uint256 id) internal virtual { _safeTransfer(by, from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data) internal virtual { _transfer(by, from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HOOKS FOR OVERRIDING */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Hook that is called before any token transfers, including minting and burning. function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {} /// @dev Hook that is called after any token transfers, including minting and burning. function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @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 Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`. /// Reverts if the target does not support the function correctly. function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data) private { /// @solidity memory-safe-assembly assembly { // Prepare the calldata. let m := mload(0x40) let onERC721ReceivedSelector := 0x150b7a02 mstore(m, onERC721ReceivedSelector) mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`. mstore(add(m, 0x40), shr(96, shl(96, from))) mstore(add(m, 0x60), id) mstore(add(m, 0x80), 0x80) let n := mload(data) mstore(add(m, 0xa0), n) if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) } // Revert if the call reverts. if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) { if returndatasize() { // Bubble up the revert if the call reverts. returndatacopy(0x00, 0x00, returndatasize()) revert(0x00, returndatasize()) } mstore(m, 0) } // Load the returndata and compare it. if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) { mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`. revert(0x1c, 0x04) } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; contract CommitReveal { error InvalidCommitment(uint256 committedTimestamp); uint256 private constant INVALID_COMMITMENT_SELECTOR = 0x31e63ea0; uint256 public immutable COMMITMENT_LIFESPAN; uint256 public immutable COMMITMENT_DELAY; constructor(uint256 commitmentLifespan, uint256 commitmentDelay) { COMMITMENT_LIFESPAN = commitmentLifespan; COMMITMENT_DELAY = commitmentDelay; } ///@dev mapping of user to key to commitment hash to timestamp. mapping(address user => mapping(bytes32 commitment => uint256 timestamp)) public commitments; /** * @notice Commit a hash to the contract, to be retrieved and verified after a delay. A commitment is valid only * after COMMITMENT_DELAY seconds have passed, and is only valid for COMMITMENT_LIFESPAN seconds. * @param commitment The hash to commit. */ function commit(bytes32 commitment) public { commitments[msg.sender][commitment] = block.timestamp; } /** * @dev Assert that a commitment has been made and is within a valid time * window. * @param computedCommitmentHash The derived commitment hash to verify. */ function _assertCommittedReveal(bytes32 computedCommitmentHash) internal view { // retrieve the timestamp of the commitment (if it exists) uint256 retrievedTimestamp = commitments[msg.sender][computedCommitmentHash]; // compute the time difference uint256 timeDiff; // unchecked; assume blockchain time is monotonically increasing unchecked { timeDiff = block.timestamp - retrievedTimestamp; } uint256 commitmentLifespan = COMMITMENT_LIFESPAN; uint256 commitmentDelay = COMMITMENT_DELAY; assembly { // if the time difference is greater than the commitment lifespan, // the commitment has expired // if the time difference is less than the commitment delay, the // commitment is pending let invalidCommitment := or(gt(timeDiff, commitmentLifespan), lt(timeDiff, commitmentDelay)) if invalidCommitment { mstore(0, INVALID_COMMITMENT_SELECTOR) mstore(0x20, retrievedTimestamp) revert(0x1c, 0x24) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173) /// for compatibility, the nomenclature for the 2-step ownership handover /// may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`. /// It is intentionally choosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(not(_OWNER_SLOT_NOT), newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { /// @solidity memory-safe-assembly assembly { let ownerSlot := not(_OWNER_SLOT_NOT) // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will be automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(not(_OWNER_SLOT_NOT)) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. function ownershipHandoverValidFor() public view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH /// that disallows any storage writes. uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. /// Multiply by a small constant (e.g. 2), if needed. uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` (in wei) ETH to `to`. /// Reverts upon failure. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. if iszero(call(gas(), to, amount, 0, 0, 0, 0)) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 if iszero(create(amount, 0x0b, 0x16)) { // For better gas estimation. if iszero(gt(gas(), 1000000)) { revert(0, 0) } } } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default /// for 99% of cases and can be overriden with the three-argument version of this /// function if necessary. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount) internal { // Manually inlined because the compiler doesn't inline functions with branches. /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 if iszero(create(amount, 0x0b, 0x16)) { // For better gas estimation. if iszero(gt(gas(), 1000000)) { revert(0, 0) } } } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend. /// /// Note: Does NOT revert upon failure. /// Returns whether the transfer of ETH is successful instead. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. success := call(gasStipend, to, amount, 0, 0, 0, 0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x0c, 0x23b872dd000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. // Store the function selector of `balanceOf(address)`. mstore(0x0c, 0x70a08231000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x00, 0x23b872dd) // The `amount` argument is already written to the memory word at 0x6c. amount := mload(0x60) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. // Store the function selector of `transfer(address,uint256)`. mstore(0x00, 0xa9059cbb000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x14, to) // Store the `to` argument. // The `amount` argument is already written to the memory word at 0x34. amount := mload(0x34) // Store the function selector of `transfer(address,uint256)`. mstore(0x00, 0xa9059cbb000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. // Store the function selector of `approve(address,uint256)`. mstore(0x00, 0x095ea7b3000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `ApproveFailed()`. mstore(0x00, 0x3e3f8f73) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. // Store the function selector of `balanceOf(address)`. mstore(0x00, 0x70a08231000000000000000000000000) amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {IERC165} from "forge-std/interfaces/IERC165.sol"; interface IERC4906 is IERC165 { /// @dev This event emits when the metadata of a token is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFT. event MetadataUpdate(uint256 _tokenId); /// @dev This event emits when the metadata of a range of tokens is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFTs. event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2; interface IERC165 { /// @notice Query if a contract implements an interface /// @param interfaceID The interface identifier, as specified in ERC-165 /// @dev Interface identification is specified in ERC-165. This function /// uses less than 30,000 gas. /// @return `true` if the contract implements `interfaceID` and /// `interfaceID` is not 0xffffffff, `false` otherwise function supportsInterface(bytes4 interfaceID) external view returns (bool); }
{ "remappings": [ "create2-helpers/=lib/create2-helpers/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc721a/=lib/erc721a/contracts/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/create2-helpers/lib/openzeppelin-contracts/", "solady-test/=lib/solady/test/", "solady/=lib/solady/src/", "solarray/=lib/solarray/src/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "bytecodeHash": "none", "appendCBOR": false }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "evmVersion": "shanghai", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"address","name":"creatorPayout","type":"address"},{"internalType":"uint256","name":"maxBatchSize","type":"uint256"},{"internalType":"uint24[]","name":"preMintIds","type":"uint24[]"},{"internalType":"address","name":"seaDrop","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyFinalized","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"BatchBurnerNotApprovedForAll","type":"error"},{"inputs":[{"internalType":"uint256","name":"committedTimestamp","type":"uint256"}],"name":"InvalidCommitment","type":"error"},{"inputs":[],"name":"InvalidHex","type":"error"},{"inputs":[],"name":"InvalidPayment","type":"error"},{"inputs":[],"name":"InvalidTimestamp","type":"error"},{"inputs":[],"name":"MaxBatchSizeExceeded","type":"error"},{"inputs":[],"name":"MaximumSupplyExceeded","type":"error"},{"inputs":[],"name":"MintClosed","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NoIdsProvided","type":"error"},{"inputs":[],"name":"NoneAvailable","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"OnlyFinalized","type":"error"},{"inputs":[],"name":"OnlySeadrop","type":"error"},{"inputs":[],"name":"OnlyTokenOwner","type":"error"},{"inputs":[],"name":"OwnerMismatch","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Unavailable","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","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":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"COMMITMENT_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COMMITMENT_LIFESPAN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FINALIZE_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MINT_CLOSE_TIMESTAMP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SPECIFIC_BATCH_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REROLL_AND_FINALIZE_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REROLL_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bool","name":"onlyFinalized","type":"bool"}],"name":"batchBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"batchFinalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"batchMintSpecific","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"batchReroll","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"oldIds","type":"uint256[]"},{"internalType":"uint256[]","name":"newIds","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"batchRerollSpecific","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"oldIds","type":"uint256[]"},{"internalType":"uint256[]","name":"newIds","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"batchRerollSpecificAndFinalize","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"xxyyzz","type":"uint256"},{"internalType":"bool","name":"onlyFinalized","type":"bool"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"commit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"commitments","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"computeBatchCommitment","outputs":[{"internalType":"bytes32","name":"commitmentHash","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"computeCommitment","outputs":[{"internalType":"bytes32","name":"committmentHash","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"configureSeaDrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"finalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"finalizers","outputs":[{"internalType":"address","name":"finalizer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getMintStats","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"isFinalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintSeaDrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"mintSpecific","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintTo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"numBurned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"oldId","type":"uint256"}],"name":"reroll","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"oldId","type":"uint256"},{"internalType":"uint256","name":"newId","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"rerollSpecific","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"oldId","type":"uint256"},{"internalType":"uint256","name":"newId","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"rerollSpecificAndFinalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","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":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","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":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
61016060405234801562000011575f80fd5b50604051620042cd380380620042cd8339810160408190526200003491620002d4565b62015180608052603060a0528084868581818181620000538262000156565b60c05250620000664262127500620003e4565b60e05250506001600160a01b038085166101005242610140528316610120527fd7aca75208b9be5ffc04c6a01922020ffd62b55e68e502e317f5344960279af85f80a1505050505f5b82518110156200012857620000ec86848381518110620000d357620000d36200040a565b602002602001015162ffffff166200019160201b60201c565b6200011f8382815181106200010557620001056200040a565b602002602001015162ffffff16876200023b60201b60201c565b600101620000af565b505051600280546001600160801b031663ffffffff909216600160801b02919091179055506200041e915050565b6001600160a01b0316638b78c6d819819055805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6001600160a01b039091169081620001b05763ea553b345f526004601cfd5b805f52673ec412a9852d173d60c11b601c5260205f208101810180548060601b15620001e35763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff81166200020e576301336cea5f526004601cfd5b905580825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a45050565b5f828152600160208181526040832080546001600160a01b0386166001600160a01b0319909116179055673ec412a9852d173d60c11b601c5290912083018301805460a081811c90931890921b90911890555050565b80516001600160a01b0381168114620002a8575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b805162ffffff81168114620002a8575f80fd5b5f805f805f60a08688031215620002e9575f80fd5b620002f48662000291565b945060206200030581880162000291565b6040880151606089015191965094506001600160401b038082111562000329575f80fd5b818901915089601f8301126200033d575f80fd5b815181811115620003525762000352620002ad565b8060051b604051601f19603f830116810181811085821117156200037a576200037a620002ad565b60405291825284820192508381018501918c83111562000398575f80fd5b938501935b82851015620003c157620003b185620002c1565b845293850193928501926200039d565b809750505050505050620003d86080870162000291565b90509295509295909350565b808201808211156200040457634e487b7160e01b5f52601160045260245ffd5b92915050565b634e487b7160e01b5f52603260045260245ffd5b60805160a05160c05160e051610100516101205161014051613e25620004a85f395f61133901525f6114b501525f818161126e015261140c01525f81816106f40152818161136301528181611f7d015261228f01525f818161047c01528181611cd401526122e901525f81816105160152611d7501525f81816104bd0152611d540152613e255ff3fe60806040526004361061037f575f3560e01c8063715018a6116101d3578063c002d23d116100fd578063dd686e261161009d578063f04e283e1161006d578063f04e283e14610a2c578063f14fcbc814610a3f578063f2fde38b14610a74578063fee81cf414610a87575f80fd5b8063dd686e2614610905578063e8a3d485146109d1578063e985e9c5146109e5578063ea08b46514610a19575f80fd5b8063d52079b4116100d8578063d52079b414610951578063d5abeb011461098b578063d7533f02146109a1578063d8176369146109be575f80fd5b8063c002d23d14610905578063c87b56dd1461091f578063ccedf5721461093e575f80fd5b80639fac68cb11610173578063ad7556d711610143578063ad7556d7146108ad578063afb34abe146108cc578063b88d4fde146108df578063b8a49eba146108f2575f80fd5b80639fac68cb14610849578063a0712d6814610868578063a22cb4651461087b578063ad3554661461089a575f80fd5b80638b1592b5116101ae5780638b1592b5146107ca5780638da5cb5b146107fd57806395d89b41146103f35780639f51758e14610830575f80fd5b8063715018a6146107165780637e92a4231461071e578063840e15d41461075f575f80fd5b806323b872dd116102b45780634e9edf1c1161025457806364869dad1161022457806364869dad146106915780636cd473cb146106b057806370a08231146106c45780637112ca4f146106e3575f80fd5b80634e9edf1c1461063d57806354d1f13d1461065757806360abc14b1461065f5780636352211e14610672575f80fd5b806333727c4d1161028f57806333727c4d146105d75780633ccfd60b146105f657806342842e0e1461060a578063449a52f81461061d575f80fd5b806323b872dd14610586578063256929621461059957806327e99a5c146105a1575f80fd5b80630affb8341161031f5780631249c58b116102fa5780631249c58b1461053857806318160ddd146105405780631c19c215146105545780631ced131814610567575f80fd5b80630affb834146104ac57806311a040ac146104df57806311a733f614610505575f80fd5b806306fdde031161035a57806306fdde03146103f3578063081812fc14610414578063095ea7b3146104585780630a157d9e1461046b575f80fd5b806301ffc9a71461038a57806305261aea146103be57806306740ff4146103d3575f80fd5b3661038657005b5f80fd5b348015610395575f80fd5b506103a96103a436600461316a565b610ab8565b60405190151581526020015b60405180910390f35b6103d16103cc3660046131a9565b610af5565b005b6103e66103e1366004613208565b610b13565b6040516103b59190613276565b3480156103fe575f80fd5b50610407610b57565b6040516103b591906132dd565b34801561041f575f80fd5b5061043361042e3660046131a9565b610b6e565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103b5565b6103d1610466366004613350565b610bd1565b348015610476575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016103b5565b3480156104b7575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104ea575f80fd5b506002546fffffffffffffffffffffffffffffffff1661049e565b348015610510575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b61049e610be0565b34801561054b575f80fd5b5061049e610c04565b61049e6105623660046131a9565b610c53565b348015610572575f80fd5b506103d1610581366004613387565b610c6f565b6103d16105943660046133d7565b610f4b565b6103d1611098565b3480156105ac575f80fd5b5061049e6105bb366004613410565b60189290921b62ffffff91909116175f52602052604060092090565b3480156105e2575f80fd5b506103a96105f13660046131a9565b6110e5565b348015610601575f80fd5b506103d161112c565b6103d16106183660046133d7565b611149565b61063061062b366004613350565b611175565b6040516103b59190613440565b348015610648575f80fd5b5061049e6612a6d8e112200081565b6103d1611188565b6103e661066d366004613477565b6111c1565b34801561067d575f80fd5b5061043361068c3660046131a9565b6111f8565b34801561069c575f80fd5b506103d16106ab366004613350565b611256565b3480156106bb575f80fd5b506103d1611314565b3480156106cf575f80fd5b5061049e6106de3660046134bf565b6116d6565b3480156106ee575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b6103d1611723565b348015610729575f80fd5b506104336107383660046131a9565b60016020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b34801561076a575f80fd5b506107af6107793660046134bf565b506002545f917001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690630100000090565b604080519384526020840192909252908201526060016103b5565b3480156107d5575f80fd5b5061049e6107e4366004613350565b5f60208181529281526040808220909352908152205481565b348015610808575f80fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754610433565b34801561083b575f80fd5b5061049e65e35fa931a00081565b348015610854575f80fd5b506103d16108633660046134d8565b611736565b6106306108763660046131a9565b6117fc565b348015610886575f80fd5b506103d1610895366004613502565b611808565b6103d16108a836600461352a565b61185b565b3480156108b8575f80fd5b5061049e6108c7366004613569565b61189f565b6103d16108da3660046135bf565b6118cd565b6103d16108ed3660046135df565b6118e2565b61063061090036600461352a565b611935565b348015610910575f80fd5b5061049e6611c37937e0800081565b34801561092a575f80fd5b506104076109393660046131a9565b6119ec565b6103d161094c366004613672565b611a2e565b34801561095c575f80fd5b5060025470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661049e565b348015610996575f80fd5b50630100000061049e565b3480156109ac575f80fd5b506040516202a30081526020016103b5565b6103d16109cc366004613672565b611a4a565b3480156109dc575f80fd5b50610407611ae0565b3480156109f0575f80fd5b506103a96109ff36600461369b565b601c52670a5a2e7a000000006008525f526030600c205490565b6103e6610a27366004613208565b611b11565b6103d1610a3a3660046134bf565b611b4b565b348015610a4a575f80fd5b506103d1610a593660046131a9565b335f9081526020818152604080832093835292905220429055565b6103d1610a823660046134bf565b611b85565b348015610a92575f80fd5b5061049e610aa13660046134bf565b63389a75e1600c9081525f91909152602090205490565b5f610aef826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f8214176349064906821417631890fe8e9091141790565b92915050565b610b076611c37937e080006001611bab565b610b1081611be6565b50565b6060610b288686868665e35fa931a000611c99565b5f610b353386868661189f565b9050610b4081611d36565b610b4c87878787611db2565b979650505050505050565b6060602080526606585859595a5a60465260806020f35b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020812082018201805473ffffffffffffffffffffffffffffffffffffffff16610bc75763ceea21b65f526004601cfd5b6001015492915050565b610bdc338383611ecb565b5050565b5f80610bec6001611f7a565b90505f610bf882612023565b9050610aef3382612079565b6002545f90610c3c906fffffffffffffffffffffffffffffffff808216917001000000000000000000000000000000009004166136c3565b6fffffffffffffffffffffffffffffffff16905090565b5f610c6565e35fa931a0006001611bab565b610aef8233612142565b5f829003610ca9576040517fe663430d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610cf984845f818110610cbf57610cbf613718565b905060200201355f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b9050808215610d505773ffffffffffffffffffffffffffffffffffffffff821015610d50576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314610dc257610d8c8133601c52670a5a2e7a000000006008525f526030600c205490565b610dc2576040517fcbd6109100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821663ffffffff881601167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116179055610e2985855f81610e1d57610e1d613718565b9050602002013561216b565b60015b84811015610f43575f868683818110610e4757610e47613718565b905060200201359050610e87815f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b93508373ffffffffffffffffffffffffffffffffffffffff80821690851614610edc576040517fa8c8162300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8515610f305773ffffffffffffffffffffffffffffffffffffffff851015610f30576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f398261216b565b5050600101610e2c565b505050505050565b5f8181527f7d8825530a5a2e7a0000000000000000000000000000000000000000000000003317601c526020902081018101805473ffffffffffffffffffffffffffffffffffffffff9485169493841693811691908286148302610fc85782610fbb5763ceea21b65f526004601cfd5b63a11481005f526004601cfd5b84610fda5763ea553b345f526004601cfd5b855f528160010154925082331486331417611006576030600c205461100657634b6e7f185f526004601cfd5b8215611013575f82600101555b85851818905550601c600c81812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190555f84905220805460010163ffffffff8116611069576301336cea5f526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a45b505050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f6110ef82612175565b610aef825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6111346121b2565b5f8081828347335af1610b10573d81823e3d81fd5b611154838383610f4b565b813b156110935761109383838360405180602001604052805f8152506121e7565b6060611181838361226c565b9392505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b60606111cd848461228d565b5f6111da3386868661189f565b90506111e581611d36565b6111ef8585612352565b95945050505050565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015473ffffffffffffffffffffffffffffffffffffffff16806112515763ceea21b65f526004601cfd5b919050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146112c5576040517fa8eb05d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821670010000000000000000000000000000000092839004821685019182169283021790925561130e908490849061244b565b50505050565b61131c6121b2565b6040805160c0810182526611c37937e08000815265ffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116602083019081527f0000000000000000000000000000000000000000000000000000000000000000821683850190815261ffff606085018181526103e860808701908152600160a0880190815297517f01308e65000000000000000000000000000000000000000000000000000000008152965169ffffffffffffffffffff166004880152935185166024870152915190931660448501525182166064840152511660848201529051151560a48201527f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff8216906301308e659060c4015f604051808303815f87803b158015611464575f80fd5b505af1158015611476573d5f803e3d5ffd5b50506040517f12738db800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166004830152841692506312738db891506024015f604051808303815f87803b158015611500575f80fd5b505af1158015611512573d5f803e3d5ffd5b50506040517f8e7d1e4300000000000000000000000000000000000000000000000000000000815271a26b00c1f0df003000390027140000faa71960048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250638e7d1e4391506044015f604051808303815f87803b158015611593575f80fd5b505af11580156115a5573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273f408bee3443d0397e2c1cde588fb060ac657006f60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b158015611628575f80fd5b505af115801561163a573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273e3d3d0ed702504e19825f44bc6542ff2ec45cb9a60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b1580156116bd575f80fd5b505af11580156116cf573d5f803e3d5ffd5b5050505050565b5f816116e957638f4eb6045f526004601cfd5b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52815f5263ffffffff601c600c2054169050919050565b61172b6121b2565b6117345f6124de565b565b600280546fffffffffffffffffffffffffffffffff808216600101167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617905580156117f2576117bc825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6117f2576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc3383612543565b6060610aef338361226c565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b61186c6611c37937e0800082611bab565b5f5b818110156110935761189783838381811061188b5761188b613718565b90506020020135611be6565b60010161186e565b5f6040518360051b8086833781205f969096526020959095525060409081526060600c209390525090919050565b6118d76001611f7a565b50610bdc828261264a565b6118ed858585610f4b565b833b156116cf576116cf85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506121e792505050565b606061194765e35fa931a00083611bab565b335f8367ffffffffffffffff81111561196257611962613745565b60405190808252806020026020018201604052801561198b578160200160208202803683370190505b5090505f5b848110156119e3576119ba8686838181106119ad576119ad613718565b9050602002013584612142565b8282815181106119cc576119cc613718565b602090810291909101015260019283019201611990565b50949350505050565b60606119f782612175565b611a08611a03836126f1565b6128a2565b604051602001611a189190613772565b6040516020818303038152906040529050919050565b611a3f65e35fa931a0006001611bab565b6110938383836128af565b611a5c6612a6d8e11220006001611bab565b611a678383836128af565b5f828152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912083018301805460a081811c90931890921b9091189055505050565b6060611aed611a036128cb565b604051602001611afd9190613772565b604051602081830303815290604052905090565b6060611b27868686866612a6d8e1122000611c99565b5f611b343386868661189f565b9050611b3f81611d36565b610b4c878787876128eb565b611b536121b2565b63389a75e1600c52805f526020600c208054421115611b7957636f5e88185f526004601cfd5b5f9055610b10816124de565b611b8d6121b2565b8060601b611ba257637448fbae5f526004601cfd5b610b10816124de565b8082023414610bdc576040517f3c6b4b2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bef81612a6a565b5f818152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912082018201805460a081811c90931890921b90911890556040518181527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a150565b838214611cd2576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000841115611d2c576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116cf8482611bab565b335f90815260208181526040808320848452909152902054428190037f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000818311818410178015610f43576331e63ea05f52846020526024601cfd5b60605f8467ffffffffffffffff811115611dce57611dce613745565b604051908082528060200260200182016040528015611df7578160200160208202803683370190505b5090505f805b86811015611e7757611e3f888883818110611e1a57611e1a613718565b90506020020135878784818110611e3357611e33613718565b90506020020135612b39565b15611e6f576001838281518110611e5857611e58613718565b911515602092830291909101909101526001909101905b600101611dfd565b50805f03611eb1576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec165e35fa931a00082612b6a565b5095945050505050565b5f1960601c82811692508381169350815f52837f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f208201820180548216915081611f235763ceea21b65f526004601cfd5b818514851517611f4757815f526030600c2054611f4757634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a450505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000421115611fd5576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fe66611c37937e0800083611bab565b50600280546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482169094018116938402911617905590565b5f818152446020526040600c2062ffffff165b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015415610aef5760010162ffffff16612036565b73ffffffffffffffffffffffffffffffffffffffff90911690816120a45763ea553b345f526004601cfd5b805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208101810180548060601b156120eb5763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff8116612115576301336cea5f526004601cfd5b905580825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a45050565b5f61214c83612a6a565b6121558361216b565b5f61215f83612023565b90506111813382612079565b610b105f82612543565b62ffffff811115610b10576040517fcbbc48a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314611734576382b429005f526004601cfd5b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a0840152801561222e578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1612253573d1561224f573d5f803e3d5ffd5b5f83525b508060e01b825114610f435763d1a57ed65f526004601cfd5b60605f61227883611f7a565b905061228584848361244b565b949350505050565b7f00000000000000000000000000000000000000000000000000000000000000004211156122e7576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000811115612341576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc816611c37937e08000611bab565b60605f8267ffffffffffffffff81111561236e5761236e613745565b604051908082528060200260200182016040528015612397578160200160208202803683370190505b5090505f805b848110156123fe576123c68686838181106123ba576123ba613718565b90506020020135612b81565b156123f65760018382815181106123df576123df613718565b911515602092830291909101909101526001909101905b60010161239d565b50805f03612438576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61244181612bda565b5090949350505050565b60605f8367ffffffffffffffff81111561246757612467613745565b604051908082528060200260200182016040528015612490578160200160208202803683370190505b5090505f5b848110156119e3575f6124a785612023565b90506124b38782612079565b808383815181106124c6576124c6613718565b60209081029190910101525060019384019301612495565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927805473ffffffffffffffffffffffffffffffffffffffff9092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f61254d826111f8565b9050505f81815273ffffffffffffffffffffffffffffffffffffffff9283167f7d8825530a5a2e7a0000000000000000000000000000000000000000000000008117601c5260209091208201820180549193821691826125b45763ceea21b65f526004601cfd5b825f528160010154808614848714178615176125e1576030600c20546125e157634b6e7f185f526004601cfd5b80156125ee575f83600101555b5082189055601c600c2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055815f827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4505050565b3360181b62ffffff8316175f526020819052604060092061266a83612175565b61267381611d36565b6126b0835f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b156126e7576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110933384612079565b606061273a6040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525061273584612c2d565b612c4a565b6127ae6040518060400160405280600d81526020017f65787465726e616c5f6c696e6b000000000000000000000000000000000000008152506040518060400160405280601281526020017f68747470733a2f2f787879797a7a2e6172740000000000000000000000000000815250612c4a565b6128056040518060400160405280600b81526020017f6465736372697074696f6e0000000000000000000000000000000000000000008152506040518060a0016040528060648152602001613d4760649139612c4a565b6128476040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525061273587612c76565b61288e6040518060400160405280600a81526020017f617474726962757465730000000000000000000000000000000000000000000081525061288988612c94565b612e99565b604051602001611a189594939291906137b6565b6060610aef825f80612eae565b6128b883612a6a565b6128c18361216b565b611093828261264a565b60606040518060a00160405280607a8152602001613dab607a9139905090565b60605f8467ffffffffffffffff81111561290757612907613745565b604051908082528060200260200182016040528015612930578160200160208202803683370190505b5090505f805b86811015612a1f57612953888883818110611e1a57611e1a613718565b15612a17576129ec86868381811061296d5761296d613718565b602090810292909201355f81815260018085526040822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52939020810101805460a081811c90941890931b90921890915550565b6001838281518110612a0057612a00613718565b911515602092830291909101909101526001909101905b600101612936565b50805f03612a59576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec16612a6d8e112200082612b6a565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020902081018101548073ffffffffffffffffffffffffffffffffffffffff811115612aea576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314611093576040517fcdf1f8f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612b4383612a6a565b612b4c82612b81565b15612b6257612b5a8361216b565b506001610aef565b505f92915050565b818102348181039114611093576110933382612fae565b5f612b8b82612175565b5f8281527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020820182015415612bc857505f919050565b612bd23383612079565b506001919050565b6002805463ffffffff6fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080840482168601928316029216919091179091555f90610aef6611c37937e0800084612b6a565b6060612c3a826003612fc7565b604051602001611a1891906138bc565b60608282604051602001612c5f929190613900565b604051602081830303815290604052905092915050565b6060612c84611a038361306b565b604051602001611a189190613989565b60605f612cde6040518060400160405280600581526020017f436f6c6f72000000000000000000000000000000000000000000000000000000815250612cd985612c2d565b613088565b9050612ce9836110e5565b15612dfc575f612d636040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f5965730000000000000000000000000000000000000000000000000000000000815250613088565b604080518082018252600981527f46696e616c697a657200000000000000000000000000000000000000000000006020808301919091525f8881526001909152919091205491925083918391612dd291612cd99073ffffffffffffffffffffffffffffffffffffffff1661309d565b604051602001612de4939291906139cd565b60405160208183030381529060405292505050919050565b80612e716040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600281526020017f4e6f000000000000000000000000000000000000000000000000000000000000815250613088565b604051602001612e82929190613a95565b604051602081830303815290604052915050919050565b60608282604051602001612c5f929190613b3f565b606083518015612fa6576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526102308515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f03603f52602083018181015b6003880197508751603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f518252600482019150808210612f1e576020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b5f805f8084865af1610bdc5763b12d13eb5f526004601cfd5b6060601f1960428360011b0116604051019050602081016040525f8152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c84830361300357801561303857632194895a5f526004601cfd5b5050508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090910190815292915050565b6060613078826003612fc7565b604051602001611a189190613bbf565b60608282604051602001612c5f929190613c9c565b60606130a8826130df565b805161307882526002017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90910190815292915050565b60606040519050608081016040526f30313233343536373839616263646566600f5260028101905060288152602081015f60288201528260601b92505f5b808101820184821a600f81165160018301538060041c518253505060018101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed0161311d575050919050565b5f6020828403121561317a575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611181575f80fd5b5f602082840312156131b9575f80fd5b5035919050565b5f8083601f8401126131d0575f80fd5b50813567ffffffffffffffff8111156131e7575f80fd5b6020830191508360208260051b8501011115613201575f80fd5b9250929050565b5f805f805f6060868803121561321c575f80fd5b853567ffffffffffffffff80821115613233575f80fd5b61323f89838a016131c0565b90975095506020880135915080821115613257575f80fd5b50613264888289016131c0565b96999598509660400135949350505050565b602080825282518282018190525f9190848201906040850190845b818110156132af578351151583529284019291840191600101613291565b50909695505050505050565b5f5b838110156132d55781810151838201526020016132bd565b50505f910152565b602081525f82518060208401526132fb8160408501602087016132bb565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611251575f80fd5b5f8060408385031215613361575f80fd5b61336a8361332d565b946020939093013593505050565b80358015158114611251575f80fd5b5f805f60408486031215613399575f80fd5b833567ffffffffffffffff8111156133af575f80fd5b6133bb868287016131c0565b90945092506133ce905060208501613378565b90509250925092565b5f805f606084860312156133e9575f80fd5b6133f28461332d565b92506134006020850161332d565b9150604084013590509250925092565b5f805f60608486031215613422575f80fd5b61342b8461332d565b95602085013595506040909401359392505050565b602080825282518282018190525f9190848201906040850190845b818110156132af5783518352928401929184019160010161345b565b5f805f60408486031215613489575f80fd5b833567ffffffffffffffff81111561349f575f80fd5b6134ab868287016131c0565b909790965060209590950135949350505050565b5f602082840312156134cf575f80fd5b6111818261332d565b5f80604083850312156134e9575f80fd5b823591506134f960208401613378565b90509250929050565b5f8060408385031215613513575f80fd5b61351c8361332d565b91506134f960208401613378565b5f806020838503121561353b575f80fd5b823567ffffffffffffffff811115613551575f80fd5b61355d858286016131c0565b90969095509350505050565b5f805f806060858703121561357c575f80fd5b6135858561332d565b9350602085013567ffffffffffffffff8111156135a0575f80fd5b6135ac878288016131c0565b9598909750949560400135949350505050565b5f80604083850312156135d0575f80fd5b50508035926020909101359150565b5f805f805f608086880312156135f3575f80fd5b6135fc8661332d565b945061360a6020870161332d565b935060408601359250606086013567ffffffffffffffff8082111561362d575f80fd5b818801915088601f830112613640575f80fd5b81358181111561364e575f80fd5b89602082850101111561365f575f80fd5b9699959850939650602001949392505050565b5f805f60608486031215613684575f80fd5b505081359360208301359350604090920135919050565b5f80604083850312156136ac575f80fd5b6136b58361332d565b91506134f96020840161332d565b6fffffffffffffffffffffffffffffffff828116828216039080821115613711577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f82516137a981601d8501602087016132bb565b91909101601d0192915050565b7f7b0000000000000000000000000000000000000000000000000000000000000081525f86516137ed816001850160208b016132bb565b80830190507f2c00000000000000000000000000000000000000000000000000000000000000806001830152875161382c816002850160208c016132bb565b600292019182018190528651613849816003850160208b016132bb565b600392019182018190528551613866816004850160208a016132bb565b600492019182015283516138818160058401602088016132bb565b016138ae600582017f7d000000000000000000000000000000000000000000000000000000000000009052565b600601979650505050505050565b7f230000000000000000000000000000000000000000000000000000000000000081525f82516138f38160018501602087016132bb565b9190910160010192915050565b5f7f220000000000000000000000000000000000000000000000000000000000000080835284516139388160018601602089016132bb565b7f223a22000000000000000000000000000000000000000000000000000000000060019185019182015284516139758160048401602089016132bb565b016004810191909152600501949350505050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c00000000000081525f82516139c081601a8501602087016132bb565b91909101601a0192915050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8451613a048160018501602089016132bb565b80830190507f2c000000000000000000000000000000000000000000000000000000000000008060018301528551613a43816002850160208a016132bb565b60029201918201528351613a5e8160038401602088016132bb565b7f5d000000000000000000000000000000000000000000000000000000000000006003929091019182015260040195945050505050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8351613acc8160018501602088016132bb565b7f2c000000000000000000000000000000000000000000000000000000000000006001918401918201528351613b098160028401602088016132bb565b7f5d0000000000000000000000000000000000000000000000000000000000000060029290910191820152600301949350505050565b7f220000000000000000000000000000000000000000000000000000000000000081525f8351613b768160018501602088016132bb565b7f223a0000000000000000000000000000000000000000000000000000000000006001918401918201528351613bb38160038401602088016132bb565b01600301949350505050565b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d2236393022206865696768743d223639302260208201527f3e3c726563742077696474683d2236393022206865696768743d22363930222060408201527f66696c6c3d22230000000000000000000000000000000000000000000000000060608201525f8251613c688160678501602087016132bb565b7f22202f3e3c2f7376673e000000000000000000000000000000000000000000006067939091019283015250607101919050565b7f7b2274726169745f74797065223a22000000000000000000000000000000000081525f8351613cd381600f8501602088016132bb565b7f222c2276616c7565223a22000000000000000000000000000000000000000000600f918401918201528351613d1081601a8401602088016132bb565b7f227d000000000000000000000000000000000000000000000000000000000000601a9290910191820152601c0194935050505056fe50726f6f66206f6620636f6c6f722e20585859595a5a206973206120636f6c6c656374696f6e206f662066756c6c79206f6e636861696e2c20756e697175652c20636f6d706f7361626c652c20616e6420636f6c6c65637461626c6520636f6c6f72732e7b226e616d65223a22585859595a5a222c226465736372697074696f6e223a22436f6c6c65637469626c652c20636f6d706f7361626c652c20616e6420756e69717565206f6e636861696e20636f6c6f72732e222c2265787465726e616c5f6c696e6b223a2268747470733a2f2f787879797a7a2e617274227d0000000000000000000000007039813983ba47a5a46b19f3a269aef5c6c75abf0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff92000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf5000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000006969690000000000000000000000000000000000000000000000000000000000ff6000000000000000000000000000000000000000000000000000000000000000ff00
Deployed Bytecode
0x60806040526004361061037f575f3560e01c8063715018a6116101d3578063c002d23d116100fd578063dd686e261161009d578063f04e283e1161006d578063f04e283e14610a2c578063f14fcbc814610a3f578063f2fde38b14610a74578063fee81cf414610a87575f80fd5b8063dd686e2614610905578063e8a3d485146109d1578063e985e9c5146109e5578063ea08b46514610a19575f80fd5b8063d52079b4116100d8578063d52079b414610951578063d5abeb011461098b578063d7533f02146109a1578063d8176369146109be575f80fd5b8063c002d23d14610905578063c87b56dd1461091f578063ccedf5721461093e575f80fd5b80639fac68cb11610173578063ad7556d711610143578063ad7556d7146108ad578063afb34abe146108cc578063b88d4fde146108df578063b8a49eba146108f2575f80fd5b80639fac68cb14610849578063a0712d6814610868578063a22cb4651461087b578063ad3554661461089a575f80fd5b80638b1592b5116101ae5780638b1592b5146107ca5780638da5cb5b146107fd57806395d89b41146103f35780639f51758e14610830575f80fd5b8063715018a6146107165780637e92a4231461071e578063840e15d41461075f575f80fd5b806323b872dd116102b45780634e9edf1c1161025457806364869dad1161022457806364869dad146106915780636cd473cb146106b057806370a08231146106c45780637112ca4f146106e3575f80fd5b80634e9edf1c1461063d57806354d1f13d1461065757806360abc14b1461065f5780636352211e14610672575f80fd5b806333727c4d1161028f57806333727c4d146105d75780633ccfd60b146105f657806342842e0e1461060a578063449a52f81461061d575f80fd5b806323b872dd14610586578063256929621461059957806327e99a5c146105a1575f80fd5b80630affb8341161031f5780631249c58b116102fa5780631249c58b1461053857806318160ddd146105405780631c19c215146105545780631ced131814610567575f80fd5b80630affb834146104ac57806311a040ac146104df57806311a733f614610505575f80fd5b806306fdde031161035a57806306fdde03146103f3578063081812fc14610414578063095ea7b3146104585780630a157d9e1461046b575f80fd5b806301ffc9a71461038a57806305261aea146103be57806306740ff4146103d3575f80fd5b3661038657005b5f80fd5b348015610395575f80fd5b506103a96103a436600461316a565b610ab8565b60405190151581526020015b60405180910390f35b6103d16103cc3660046131a9565b610af5565b005b6103e66103e1366004613208565b610b13565b6040516103b59190613276565b3480156103fe575f80fd5b50610407610b57565b6040516103b591906132dd565b34801561041f575f80fd5b5061043361042e3660046131a9565b610b6e565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103b5565b6103d1610466366004613350565b610bd1565b348015610476575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000581565b6040519081526020016103b5565b3480156104b7575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000001518081565b3480156104ea575f80fd5b506002546fffffffffffffffffffffffffffffffff1661049e565b348015610510575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000003081565b61049e610be0565b34801561054b575f80fd5b5061049e610c04565b61049e6105623660046131a9565b610c53565b348015610572575f80fd5b506103d1610581366004613387565b610c6f565b6103d16105943660046133d7565b610f4b565b6103d1611098565b3480156105ac575f80fd5b5061049e6105bb366004613410565b60189290921b62ffffff91909116175f52602052604060092090565b3480156105e2575f80fd5b506103a96105f13660046131a9565b6110e5565b348015610601575f80fd5b506103d161112c565b6103d16106183660046133d7565b611149565b61063061062b366004613350565b611175565b6040516103b59190613440565b348015610648575f80fd5b5061049e6612a6d8e112200081565b6103d1611188565b6103e661066d366004613477565b6111c1565b34801561067d575f80fd5b5061043361068c3660046131a9565b6111f8565b34801561069c575f80fd5b506103d16106ab366004613350565b611256565b3480156106bb575f80fd5b506103d1611314565b3480156106cf575f80fd5b5061049e6106de3660046134bf565b6116d6565b3480156106ee575f80fd5b5061049e7f00000000000000000000000000000000000000000000000000000000649dd9ef81565b6103d1611723565b348015610729575f80fd5b506104336107383660046131a9565b60016020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b34801561076a575f80fd5b506107af6107793660046134bf565b506002545f917001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690630100000090565b604080519384526020840192909252908201526060016103b5565b3480156107d5575f80fd5b5061049e6107e4366004613350565b5f60208181529281526040808220909352908152205481565b348015610808575f80fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754610433565b34801561083b575f80fd5b5061049e65e35fa931a00081565b348015610854575f80fd5b506103d16108633660046134d8565b611736565b6106306108763660046131a9565b6117fc565b348015610886575f80fd5b506103d1610895366004613502565b611808565b6103d16108a836600461352a565b61185b565b3480156108b8575f80fd5b5061049e6108c7366004613569565b61189f565b6103d16108da3660046135bf565b6118cd565b6103d16108ed3660046135df565b6118e2565b61063061090036600461352a565b611935565b348015610910575f80fd5b5061049e6611c37937e0800081565b34801561092a575f80fd5b506104076109393660046131a9565b6119ec565b6103d161094c366004613672565b611a2e565b34801561095c575f80fd5b5060025470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661049e565b348015610996575f80fd5b50630100000061049e565b3480156109ac575f80fd5b506040516202a30081526020016103b5565b6103d16109cc366004613672565b611a4a565b3480156109dc575f80fd5b50610407611ae0565b3480156109f0575f80fd5b506103a96109ff36600461369b565b601c52670a5a2e7a000000006008525f526030600c205490565b6103e6610a27366004613208565b611b11565b6103d1610a3a3660046134bf565b611b4b565b348015610a4a575f80fd5b506103d1610a593660046131a9565b335f9081526020818152604080832093835292905220429055565b6103d1610a823660046134bf565b611b85565b348015610a92575f80fd5b5061049e610aa13660046134bf565b63389a75e1600c9081525f91909152602090205490565b5f610aef826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f8214176349064906821417631890fe8e9091141790565b92915050565b610b076611c37937e080006001611bab565b610b1081611be6565b50565b6060610b288686868665e35fa931a000611c99565b5f610b353386868661189f565b9050610b4081611d36565b610b4c87878787611db2565b979650505050505050565b6060602080526606585859595a5a60465260806020f35b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020812082018201805473ffffffffffffffffffffffffffffffffffffffff16610bc75763ceea21b65f526004601cfd5b6001015492915050565b610bdc338383611ecb565b5050565b5f80610bec6001611f7a565b90505f610bf882612023565b9050610aef3382612079565b6002545f90610c3c906fffffffffffffffffffffffffffffffff808216917001000000000000000000000000000000009004166136c3565b6fffffffffffffffffffffffffffffffff16905090565b5f610c6565e35fa931a0006001611bab565b610aef8233612142565b5f829003610ca9576040517fe663430d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610cf984845f818110610cbf57610cbf613718565b905060200201355f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b9050808215610d505773ffffffffffffffffffffffffffffffffffffffff821015610d50576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314610dc257610d8c8133601c52670a5a2e7a000000006008525f526030600c205490565b610dc2576040517fcbd6109100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821663ffffffff881601167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116179055610e2985855f81610e1d57610e1d613718565b9050602002013561216b565b60015b84811015610f43575f868683818110610e4757610e47613718565b905060200201359050610e87815f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b93508373ffffffffffffffffffffffffffffffffffffffff80821690851614610edc576040517fa8c8162300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8515610f305773ffffffffffffffffffffffffffffffffffffffff851015610f30576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f398261216b565b5050600101610e2c565b505050505050565b5f8181527f7d8825530a5a2e7a0000000000000000000000000000000000000000000000003317601c526020902081018101805473ffffffffffffffffffffffffffffffffffffffff9485169493841693811691908286148302610fc85782610fbb5763ceea21b65f526004601cfd5b63a11481005f526004601cfd5b84610fda5763ea553b345f526004601cfd5b855f528160010154925082331486331417611006576030600c205461100657634b6e7f185f526004601cfd5b8215611013575f82600101555b85851818905550601c600c81812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190555f84905220805460010163ffffffff8116611069576301336cea5f526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a45b505050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f6110ef82612175565b610aef825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6111346121b2565b5f8081828347335af1610b10573d81823e3d81fd5b611154838383610f4b565b813b156110935761109383838360405180602001604052805f8152506121e7565b6060611181838361226c565b9392505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b60606111cd848461228d565b5f6111da3386868661189f565b90506111e581611d36565b6111ef8585612352565b95945050505050565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015473ffffffffffffffffffffffffffffffffffffffff16806112515763ceea21b65f526004601cfd5b919050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf516146112c5576040517fa8eb05d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821670010000000000000000000000000000000092839004821685019182169283021790925561130e908490849061244b565b50505050565b61131c6121b2565b6040805160c0810182526611c37937e08000815265ffffffffffff7f00000000000000000000000000000000000000000000000000000000648b64ef8116602083019081527f00000000000000000000000000000000000000000000000000000000649dd9ef821683850190815261ffff606085018181526103e860808701908152600160a0880190815297517f01308e65000000000000000000000000000000000000000000000000000000008152965169ffffffffffffffffffff166004880152935185166024870152915190931660448501525182166064840152511660848201529051151560a48201527f00000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf59073ffffffffffffffffffffffffffffffffffffffff8216906301308e659060c4015f604051808303815f87803b158015611464575f80fd5b505af1158015611476573d5f803e3d5ffd5b50506040517f12738db800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff9281166004830152841692506312738db891506024015f604051808303815f87803b158015611500575f80fd5b505af1158015611512573d5f803e3d5ffd5b50506040517f8e7d1e4300000000000000000000000000000000000000000000000000000000815271a26b00c1f0df003000390027140000faa71960048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250638e7d1e4391506044015f604051808303815f87803b158015611593575f80fd5b505af11580156115a5573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273f408bee3443d0397e2c1cde588fb060ac657006f60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b158015611628575f80fd5b505af115801561163a573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273e3d3d0ed702504e19825f44bc6542ff2ec45cb9a60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b1580156116bd575f80fd5b505af11580156116cf573d5f803e3d5ffd5b5050505050565b5f816116e957638f4eb6045f526004601cfd5b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52815f5263ffffffff601c600c2054169050919050565b61172b6121b2565b6117345f6124de565b565b600280546fffffffffffffffffffffffffffffffff808216600101167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617905580156117f2576117bc825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6117f2576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc3383612543565b6060610aef338361226c565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b61186c6611c37937e0800082611bab565b5f5b818110156110935761189783838381811061188b5761188b613718565b90506020020135611be6565b60010161186e565b5f6040518360051b8086833781205f969096526020959095525060409081526060600c209390525090919050565b6118d76001611f7a565b50610bdc828261264a565b6118ed858585610f4b565b833b156116cf576116cf85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506121e792505050565b606061194765e35fa931a00083611bab565b335f8367ffffffffffffffff81111561196257611962613745565b60405190808252806020026020018201604052801561198b578160200160208202803683370190505b5090505f5b848110156119e3576119ba8686838181106119ad576119ad613718565b9050602002013584612142565b8282815181106119cc576119cc613718565b602090810291909101015260019283019201611990565b50949350505050565b60606119f782612175565b611a08611a03836126f1565b6128a2565b604051602001611a189190613772565b6040516020818303038152906040529050919050565b611a3f65e35fa931a0006001611bab565b6110938383836128af565b611a5c6612a6d8e11220006001611bab565b611a678383836128af565b5f828152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912083018301805460a081811c90931890921b9091189055505050565b6060611aed611a036128cb565b604051602001611afd9190613772565b604051602081830303815290604052905090565b6060611b27868686866612a6d8e1122000611c99565b5f611b343386868661189f565b9050611b3f81611d36565b610b4c878787876128eb565b611b536121b2565b63389a75e1600c52805f526020600c208054421115611b7957636f5e88185f526004601cfd5b5f9055610b10816124de565b611b8d6121b2565b8060601b611ba257637448fbae5f526004601cfd5b610b10816124de565b8082023414610bdc576040517f3c6b4b2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bef81612a6a565b5f818152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912082018201805460a081811c90931890921b90911890556040518181527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a150565b838214611cd2576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000005841115611d2c576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116cf8482611bab565b335f90815260208181526040808320848452909152902054428190037f00000000000000000000000000000000000000000000000000000000000151807f0000000000000000000000000000000000000000000000000000000000000030818311818410178015610f43576331e63ea05f52846020526024601cfd5b60605f8467ffffffffffffffff811115611dce57611dce613745565b604051908082528060200260200182016040528015611df7578160200160208202803683370190505b5090505f805b86811015611e7757611e3f888883818110611e1a57611e1a613718565b90506020020135878784818110611e3357611e33613718565b90506020020135612b39565b15611e6f576001838281518110611e5857611e58613718565b911515602092830291909101909101526001909101905b600101611dfd565b50805f03611eb1576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec165e35fa931a00082612b6a565b5095945050505050565b5f1960601c82811692508381169350815f52837f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f208201820180548216915081611f235763ceea21b65f526004601cfd5b818514851517611f4757815f526030600c2054611f4757634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a450505050565b5f7f00000000000000000000000000000000000000000000000000000000649dd9ef421115611fd5576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fe66611c37937e0800083611bab565b50600280546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482169094018116938402911617905590565b5f818152446020526040600c2062ffffff165b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015415610aef5760010162ffffff16612036565b73ffffffffffffffffffffffffffffffffffffffff90911690816120a45763ea553b345f526004601cfd5b805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208101810180548060601b156120eb5763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff8116612115576301336cea5f526004601cfd5b905580825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a45050565b5f61214c83612a6a565b6121558361216b565b5f61215f83612023565b90506111813382612079565b610b105f82612543565b62ffffff811115610b10576040517fcbbc48a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314611734576382b429005f526004601cfd5b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a0840152801561222e578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1612253573d1561224f573d5f803e3d5ffd5b5f83525b508060e01b825114610f435763d1a57ed65f526004601cfd5b60605f61227883611f7a565b905061228584848361244b565b949350505050565b7f00000000000000000000000000000000000000000000000000000000649dd9ef4211156122e7576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000005811115612341576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc816611c37937e08000611bab565b60605f8267ffffffffffffffff81111561236e5761236e613745565b604051908082528060200260200182016040528015612397578160200160208202803683370190505b5090505f805b848110156123fe576123c68686838181106123ba576123ba613718565b90506020020135612b81565b156123f65760018382815181106123df576123df613718565b911515602092830291909101909101526001909101905b60010161239d565b50805f03612438576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61244181612bda565b5090949350505050565b60605f8367ffffffffffffffff81111561246757612467613745565b604051908082528060200260200182016040528015612490578160200160208202803683370190505b5090505f5b848110156119e3575f6124a785612023565b90506124b38782612079565b808383815181106124c6576124c6613718565b60209081029190910101525060019384019301612495565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927805473ffffffffffffffffffffffffffffffffffffffff9092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f61254d826111f8565b9050505f81815273ffffffffffffffffffffffffffffffffffffffff9283167f7d8825530a5a2e7a0000000000000000000000000000000000000000000000008117601c5260209091208201820180549193821691826125b45763ceea21b65f526004601cfd5b825f528160010154808614848714178615176125e1576030600c20546125e157634b6e7f185f526004601cfd5b80156125ee575f83600101555b5082189055601c600c2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055815f827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4505050565b3360181b62ffffff8316175f526020819052604060092061266a83612175565b61267381611d36565b6126b0835f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b156126e7576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110933384612079565b606061273a6040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525061273584612c2d565b612c4a565b6127ae6040518060400160405280600d81526020017f65787465726e616c5f6c696e6b000000000000000000000000000000000000008152506040518060400160405280601281526020017f68747470733a2f2f787879797a7a2e6172740000000000000000000000000000815250612c4a565b6128056040518060400160405280600b81526020017f6465736372697074696f6e0000000000000000000000000000000000000000008152506040518060a0016040528060648152602001613d4760649139612c4a565b6128476040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525061273587612c76565b61288e6040518060400160405280600a81526020017f617474726962757465730000000000000000000000000000000000000000000081525061288988612c94565b612e99565b604051602001611a189594939291906137b6565b6060610aef825f80612eae565b6128b883612a6a565b6128c18361216b565b611093828261264a565b60606040518060a00160405280607a8152602001613dab607a9139905090565b60605f8467ffffffffffffffff81111561290757612907613745565b604051908082528060200260200182016040528015612930578160200160208202803683370190505b5090505f805b86811015612a1f57612953888883818110611e1a57611e1a613718565b15612a17576129ec86868381811061296d5761296d613718565b602090810292909201355f81815260018085526040822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52939020810101805460a081811c90941890931b90921890915550565b6001838281518110612a0057612a00613718565b911515602092830291909101909101526001909101905b600101612936565b50805f03612a59576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec16612a6d8e112200082612b6a565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020902081018101548073ffffffffffffffffffffffffffffffffffffffff811115612aea576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314611093576040517fcdf1f8f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612b4383612a6a565b612b4c82612b81565b15612b6257612b5a8361216b565b506001610aef565b505f92915050565b818102348181039114611093576110933382612fae565b5f612b8b82612175565b5f8281527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020820182015415612bc857505f919050565b612bd23383612079565b506001919050565b6002805463ffffffff6fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080840482168601928316029216919091179091555f90610aef6611c37937e0800084612b6a565b6060612c3a826003612fc7565b604051602001611a1891906138bc565b60608282604051602001612c5f929190613900565b604051602081830303815290604052905092915050565b6060612c84611a038361306b565b604051602001611a189190613989565b60605f612cde6040518060400160405280600581526020017f436f6c6f72000000000000000000000000000000000000000000000000000000815250612cd985612c2d565b613088565b9050612ce9836110e5565b15612dfc575f612d636040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f5965730000000000000000000000000000000000000000000000000000000000815250613088565b604080518082018252600981527f46696e616c697a657200000000000000000000000000000000000000000000006020808301919091525f8881526001909152919091205491925083918391612dd291612cd99073ffffffffffffffffffffffffffffffffffffffff1661309d565b604051602001612de4939291906139cd565b60405160208183030381529060405292505050919050565b80612e716040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600281526020017f4e6f000000000000000000000000000000000000000000000000000000000000815250613088565b604051602001612e82929190613a95565b604051602081830303815290604052915050919050565b60608282604051602001612c5f929190613b3f565b606083518015612fa6576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526102308515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f03603f52602083018181015b6003880197508751603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f518252600482019150808210612f1e576020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b5f805f8084865af1610bdc5763b12d13eb5f526004601cfd5b6060601f1960428360011b0116604051019050602081016040525f8152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c84830361300357801561303857632194895a5f526004601cfd5b5050508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090910190815292915050565b6060613078826003612fc7565b604051602001611a189190613bbf565b60608282604051602001612c5f929190613c9c565b60606130a8826130df565b805161307882526002017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90910190815292915050565b60606040519050608081016040526f30313233343536373839616263646566600f5260028101905060288152602081015f60288201528260601b92505f5b808101820184821a600f81165160018301538060041c518253505060018101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed0161311d575050919050565b5f6020828403121561317a575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611181575f80fd5b5f602082840312156131b9575f80fd5b5035919050565b5f8083601f8401126131d0575f80fd5b50813567ffffffffffffffff8111156131e7575f80fd5b6020830191508360208260051b8501011115613201575f80fd5b9250929050565b5f805f805f6060868803121561321c575f80fd5b853567ffffffffffffffff80821115613233575f80fd5b61323f89838a016131c0565b90975095506020880135915080821115613257575f80fd5b50613264888289016131c0565b96999598509660400135949350505050565b602080825282518282018190525f9190848201906040850190845b818110156132af578351151583529284019291840191600101613291565b50909695505050505050565b5f5b838110156132d55781810151838201526020016132bd565b50505f910152565b602081525f82518060208401526132fb8160408501602087016132bb565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611251575f80fd5b5f8060408385031215613361575f80fd5b61336a8361332d565b946020939093013593505050565b80358015158114611251575f80fd5b5f805f60408486031215613399575f80fd5b833567ffffffffffffffff8111156133af575f80fd5b6133bb868287016131c0565b90945092506133ce905060208501613378565b90509250925092565b5f805f606084860312156133e9575f80fd5b6133f28461332d565b92506134006020850161332d565b9150604084013590509250925092565b5f805f60608486031215613422575f80fd5b61342b8461332d565b95602085013595506040909401359392505050565b602080825282518282018190525f9190848201906040850190845b818110156132af5783518352928401929184019160010161345b565b5f805f60408486031215613489575f80fd5b833567ffffffffffffffff81111561349f575f80fd5b6134ab868287016131c0565b909790965060209590950135949350505050565b5f602082840312156134cf575f80fd5b6111818261332d565b5f80604083850312156134e9575f80fd5b823591506134f960208401613378565b90509250929050565b5f8060408385031215613513575f80fd5b61351c8361332d565b91506134f960208401613378565b5f806020838503121561353b575f80fd5b823567ffffffffffffffff811115613551575f80fd5b61355d858286016131c0565b90969095509350505050565b5f805f806060858703121561357c575f80fd5b6135858561332d565b9350602085013567ffffffffffffffff8111156135a0575f80fd5b6135ac878288016131c0565b9598909750949560400135949350505050565b5f80604083850312156135d0575f80fd5b50508035926020909101359150565b5f805f805f608086880312156135f3575f80fd5b6135fc8661332d565b945061360a6020870161332d565b935060408601359250606086013567ffffffffffffffff8082111561362d575f80fd5b818801915088601f830112613640575f80fd5b81358181111561364e575f80fd5b89602082850101111561365f575f80fd5b9699959850939650602001949392505050565b5f805f60608486031215613684575f80fd5b505081359360208301359350604090920135919050565b5f80604083850312156136ac575f80fd5b6136b58361332d565b91506134f96020840161332d565b6fffffffffffffffffffffffffffffffff828116828216039080821115613711577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f82516137a981601d8501602087016132bb565b91909101601d0192915050565b7f7b0000000000000000000000000000000000000000000000000000000000000081525f86516137ed816001850160208b016132bb565b80830190507f2c00000000000000000000000000000000000000000000000000000000000000806001830152875161382c816002850160208c016132bb565b600292019182018190528651613849816003850160208b016132bb565b600392019182018190528551613866816004850160208a016132bb565b600492019182015283516138818160058401602088016132bb565b016138ae600582017f7d000000000000000000000000000000000000000000000000000000000000009052565b600601979650505050505050565b7f230000000000000000000000000000000000000000000000000000000000000081525f82516138f38160018501602087016132bb565b9190910160010192915050565b5f7f220000000000000000000000000000000000000000000000000000000000000080835284516139388160018601602089016132bb565b7f223a22000000000000000000000000000000000000000000000000000000000060019185019182015284516139758160048401602089016132bb565b016004810191909152600501949350505050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c00000000000081525f82516139c081601a8501602087016132bb565b91909101601a0192915050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8451613a048160018501602089016132bb565b80830190507f2c000000000000000000000000000000000000000000000000000000000000008060018301528551613a43816002850160208a016132bb565b60029201918201528351613a5e8160038401602088016132bb565b7f5d000000000000000000000000000000000000000000000000000000000000006003929091019182015260040195945050505050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8351613acc8160018501602088016132bb565b7f2c000000000000000000000000000000000000000000000000000000000000006001918401918201528351613b098160028401602088016132bb565b7f5d0000000000000000000000000000000000000000000000000000000000000060029290910191820152600301949350505050565b7f220000000000000000000000000000000000000000000000000000000000000081525f8351613b768160018501602088016132bb565b7f223a0000000000000000000000000000000000000000000000000000000000006001918401918201528351613bb38160038401602088016132bb565b01600301949350505050565b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d2236393022206865696768743d223639302260208201527f3e3c726563742077696474683d2236393022206865696768743d22363930222060408201527f66696c6c3d22230000000000000000000000000000000000000000000000000060608201525f8251613c688160678501602087016132bb565b7f22202f3e3c2f7376673e000000000000000000000000000000000000000000006067939091019283015250607101919050565b7f7b2274726169745f74797065223a22000000000000000000000000000000000081525f8351613cd381600f8501602088016132bb565b7f222c2276616c7565223a22000000000000000000000000000000000000000000600f918401918201528351613d1081601a8401602088016132bb565b7f227d000000000000000000000000000000000000000000000000000000000000601a9290910191820152601c0194935050505056fe50726f6f66206f6620636f6c6f722e20585859595a5a206973206120636f6c6c656374696f6e206f662066756c6c79206f6e636861696e2c20756e697175652c20636f6d706f7361626c652c20616e6420636f6c6c65637461626c6520636f6c6f72732e7b226e616d65223a22585859595a5a222c226465736372697074696f6e223a22436f6c6c65637469626c652c20636f6d706f7361626c652c20616e6420756e69717565206f6e636861696e20636f6c6f72732e222c2265787465726e616c5f6c696e6b223a2268747470733a2f2f787879797a7a2e617274227d
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000007039813983ba47a5a46b19f3a269aef5c6c75abf0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff92000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf5000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000006969690000000000000000000000000000000000000000000000000000000000ff6000000000000000000000000000000000000000000000000000000000000000ff00
-----Decoded View---------------
Arg [0] : initialOwner (address): 0x7039813983ba47a5A46B19f3a269aeF5C6c75ABF
Arg [1] : creatorPayout (address): 0x7ade04fa0cbE2167E8bff758F48879BD0C6fFf92
Arg [2] : maxBatchSize (uint256): 5
Arg [3] : preMintIds (uint24[]): 0,16777215,6908265,16736256,65280
Arg [4] : seaDrop (address): 0x00005EA00Ac477B1030CE78506496e8C2dE24bf5
-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 0000000000000000000000007039813983ba47a5a46b19f3a269aef5c6c75abf
Arg [1] : 0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff92
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 00000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf5
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000ffffff
Arg [8] : 0000000000000000000000000000000000000000000000000000000000696969
Arg [9] : 0000000000000000000000000000000000000000000000000000000000ff6000
Arg [10] : 000000000000000000000000000000000000000000000000000000000000ff00
Deployed Bytecode Sourcemap
1714:937:6:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2419:230;;;;;;;;;;-1:-1:-1;2419:230:6;;;;;:::i;:::-;;:::i;:::-;;;516:14:16;;509:22;491:41;;479:2;464:18;2419:230:6;;;;;;;;6218:120:11;;;;;;:::i;:::-;;:::i;:::-;;3659:435;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;3505:328:8:-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;7313:541:2:-;;;;;;;;;;-1:-1:-1;7313:541:2;;;;;:::i;:::-;;:::i;:::-;;;3484:42:16;3472:55;;;3454:74;;3442:2;3427:18;7313:541:2;3308:226:16;8147:119:2;;;;;;:::i;:::-;;:::i;1261:48:8:-;;;;;;;;;;;;;;;;;;4145:25:16;;;4133:2;4118:18;1261:48:8;3999:177:16;215:44:14;;;;;;;;;;;;;;;3355:87:8;;;;;;;;;;-1:-1:-1;3425:10:8;;;;3355:87;;265:41:14;;;;;;;;;;;;;;;905:314:10;;;:::i;3018:100:8:-;;;;;;;;;;;;;:::i;914:367:11:-;;;;;;:::i;:::-;;:::i;1237:1691:7:-;;;;;;;;;;-1:-1:-1;1237:1691:7;;;;;:::i;:::-;;:::i;9956:2995:2:-;;;;;;:::i;:::-;;:::i;6825:616:1:-;;;:::i;5718:485:8:-;;;;;;;;;;-1:-1:-1;5718:485:8;;;;;:::i;:::-;5994:2;5990:15;;;;6015:10;6007:19;;;;5987:40;5832:23;5977:51;6048:4;6041:18;6182:4;6176;6166:21;;5718:485;4597:125;;;;;;;;;;-1:-1:-1;4597:125:8;;;;;:::i;:::-;;:::i;2504:358::-;;;;;;;;;;;;;:::i;13022:198:2:-;;;;;;:::i;:::-;;:::i;1529:138:10:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;1190:65:8:-;;;;;;;;;;;;1242:13;1190:65;;7523:456:1;;;:::i;3206:345:10:-;;;;;;:::i;:::-;;:::i;6170:332:2:-;;;;;;;;;;-1:-1:-1;6170:332:2;;;;;:::i;:::-;;:::i;2178:510:12:-;;;;;;;;;;-1:-1:-1;2178:510:12;;;;;:::i;:::-;;:::i;1436:736::-;;;;;;;;;;;;;:::i;6646:533:2:-;;;;;;;;;;-1:-1:-1;6646:533:2;;;;;:::i;:::-;;:::i;530:49:10:-;;;;;;;;;;;;;;;6563:100:1;;;:::i;1632:63:8:-;;;;;;;;;;-1:-1:-1;1632:63:8;;;;;:::i;:::-;;;;;;;;;;;;;;;;3359:130:12;;;;;;;;;;-1:-1:-1;3359:130:12;;;;;:::i;:::-;-1:-1:-1;3461:10:12;;3413:7;;3461:10;;;;;;;3473:8;;3359:130;;;;;7238:25:16;;;7294:2;7279:18;;7272:34;;;;7322:18;;;7315:34;7226:2;7211:18;3359:130:12;7036:319:16;553:92:14;;;;;;;;;;-1:-1:-1;553:92:14;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;9211:191:1;;;;;;;;;;-1:-1:-1;9365:20:1;9359:27;9211:191;;1074:52:8;;;;;;;;;;;;1113:13;1074:52;;522:380:7;;;;;;;;;;-1:-1:-1;522:380:7;;;;;:::i;:::-;;:::i;1391:132:10:-;;;;;;:::i;:::-;;:::i;8888:713:2:-;;;;;;;;;;-1:-1:-1;8888:713:2;;;;;:::i;:::-;;:::i;6681:266:11:-;;;;;;:::i;:::-;;:::i;6913:1312:8:-;;;;;;;;;;-1:-1:-1;6913:1312:8;;;;;:::i;:::-;;:::i;2598:147:10:-;;;;;;:::i;:::-;;:::i;13723:249:2:-;;;;;;:::i;:::-;;:::i;1510:668:11:-;;;;;;:::i;:::-;;:::i;1020:48:8:-;;;;;;;;;;;;1057:11;1020:48;;666:210:9;;;;;;;;;;-1:-1:-1;666:210:9;;;;;:::i;:::-;;:::i;2764:186:11:-;;;;;;:::i;:::-;;:::i;3193:87:8:-;;;;;;;;;;-1:-1:-1;3263:10:8;;;;;;;3193:87;;3554:85:12;;;;;;;;;;-1:-1:-1;3624:8:12;3554:85;;10031:107:1;;;;;;;;;;-1:-1:-1;10031:107:1;;10122:9;10688:50:16;;10676:2;10661:18;10031:107:1;10544:200:16;4709:321:11;;;;;;:::i;:::-;;:::i;947:167:9:-;;;;;;;;;;;;;:::i;8357:392:2:-;;;;;;;;;;-1:-1:-1;8357:392:2;;;;;:::i;:::-;8577:4;8570:22;8618:31;8612:4;8605:45;8477:11;8663:19;8727:4;8721;8711:21;8705:28;;8357:392;5434:469:11;;;;;;:::i;:::-;;:::i;8166:708:1:-;;;;;;:::i;:::-;;:::i;940:113:14:-;;;;;;;;;;-1:-1:-1;940:113:14;;;;;:::i;:::-;1005:10;993:11;:23;;;;;;;;;;;:35;;;;;;;1031:15;993:53;;940:113;6149:349:1;;;;;;:::i;:::-;;:::i;9505:435::-;;;;;;;;;;-1:-1:-1;9505:435:1;;;;;:::i;:::-;9775:19;9769:4;9762:33;;;9624:14;9808:26;;;;9918:4;9902:21;;9896:28;;9505:435;2419:230:6;2571:4;2598:44;2630:11;3128:10:12;2930:3;2926:21;;;;3122:17;;;3147:10;3141:17;;3119:40;3167:10;3161:17;;3116:63;3187:10;3181:17;;3113:86;3227:32;3221:39;;;3089:189;;2786:508;2598:44:6;2591:51;2419:230;-1:-1:-1;;2419:230:6:o;6218:120:11:-;6273:35;1173:11:8;6306:1:11;6273:16;:35::i;:::-;6318:13;6328:2;6318:9;:13::i;:::-;6218:120;:::o;3659:435::-;3804:13;3833:60;3864:6;;3872;;1113:13:8;3833:30:11;:60::i;:::-;3903:26;3932:48;3955:10;3967:6;;3975:4;3932:22;:48::i;:::-;3903:77;;3990:42;4013:18;3990:22;:42::i;:::-;4050:37;4072:6;;4080;;4050:21;:37::i;:::-;4043:44;3659:435;-1:-1:-1;;;;;;;3659:435:11:o;3505:328:8:-;3551:13;3738:4;;3725:18;3769:16;3763:4;3756:30;3812:4;3738;3799:18;7313:541:2;7375:14;7467:16;;;7509:24;7503:4;7496:38;7600:4;7584:21;;7576:30;;7568:39;;7646:20;;7630:38;;7620:167;;7701:10;7695:4;7688:24;7768:4;7762;7755:18;7620:167;7820:1;7816:21;7810:28;;7313:541;-1:-1:-1;;7313:541:2:o;8147:119::-;8226:33;8235:10;8247:7;8256:2;8226:8;:33::i;:::-;8147:119;;:::o;905:314:10:-;945:7;964:17;984:34;1016:1;984:31;:34::i;:::-;964:54;;1106:15;1124:28;1142:9;1124:17;:28::i;:::-;1106:46;;1162:26;1168:10;1180:7;1162:5;:26::i;3018:100:8:-;3101:10;;3062:7;;3088:23;;3101:10;;;;;3088;;;;:23;:::i;:::-;3081:30;;;;3018:100;:::o;914:367:11:-;969:7;988:33;1113:13:8;1019:1:11;988:16;:33::i;:::-;1231:43;1247:5;1262:10;1231:15;:43::i;1237:1691:7:-;1335:1;1321:15;;;1317:68;;1359:15;;;;;;;;;;;;;;1317:68;1394:32;1429:28;1450:3;;1454:1;1450:6;;;;;;;:::i;:::-;;;;;;;13924:14:8;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;14180:46;;13859:383;1429:28:7;1394:63;-1:-1:-1;1394:63:7;1547:148;;;;1611:17;1584:44;;1580:105;;;1655:15;;;;;;;;;;;;;;1580:105;1776:31;;;1797:10;1776:31;1772:185;;1828:47;1845:17;1864:10;8577:4:2;8570:22;8618:31;8612:4;8605:45;8477:11;8663:19;8727:4;8721;8711:21;8705:28;;8357:392;1828:47:7;1823:124;;1902:30;;;;;;;;;;;;;;1823:124;2072:10;:32;;;;;;;;;;;;;;;;;;2124:13;2093:3;;2072:10;2130:6;;;;;:::i;:::-;;;;;;;2124:5;:13::i;:::-;2164:1;2147:775;2167:14;;;2147:775;;;2198:10;2211:3;;2215:1;2211:6;;;;;;;:::i;:::-;;;;;;;2198:19;;2258:24;2279:2;13924:14:8;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;14180:46;;13859:383;2258:24:7;2231:51;-1:-1:-1;2231:51:7;2440:26;;;;;;;;2436:87;;2493:15;;;;;;;;;;;;;;2436:87;2540:13;2536:164;;;2604:17;2577:44;;2573:113;;;2652:15;;;;;;;;;;;;;;2573:113;2843:9;2849:2;2843:5;:9::i;:::-;-1:-1:-1;;2894:3:7;;2147:775;;;;1307:1621;;1237:1691;;;:::o;9956:2995:2:-;10233:1;10377:16;;;10422:24;10448:8;10419:38;10413:4;10406:52;10524:4;10508:21;;10500:30;;10492:39;;10567:20;;10221:15;10257:25;;;;10301:23;;;;10613:36;;;10492:39;10752:15;;;10741:27;;10731:328;;10798:5;10788:146;;10840:10;10834:4;10827:24;10911:4;10905;10898:18;10788:146;10964:10;10958:4;10951:24;11040:4;11034;11027:18;10731:328;11133:2;11123:135;;11168:10;11162:4;11155:24;11239:4;11233;11226:18;11123:135;11361:4;11355;11348:18;11419:13;11416:1;11412:21;11406:28;11383:51;;11569:15;11559:8;11556:29;11549:4;11539:8;11536:18;11533:53;11523:288;;11642:4;11636;11626:21;11620:28;11610:183;;11689:10;11683:4;11676:24;11766:4;11760;11753:18;11610:183;11886:15;11883:55;;;11934:1;11918:13;11915:1;11911:21;11904:32;11883:55;12050:13;;;12029:35;12007:58;;-1:-1:-1;12183:4:2;12177;12167:21;;;12233:22;;12229:30;;12205:55;;-1:-1:-1;12351:16:2;;;12405:21;12474:20;;12257:1;12470:28;12550:20;12525:46;;12515:192;;12608:10;12602:4;12595:24;12684:4;12678;12671:18;12515:192;12724:42;;12889:2;12885;12879:4;12852:25;12846:4;;12835:57;12911:33;9956:2995;;;:::o;6825:616:1:-;6918:15;10122:9;6936:45;;:15;:45;6918:63;;7149:19;7143:4;7136:33;7199:8;7193:4;7186:22;7255:7;7248:4;7242;7232:21;7225:38;7402:8;7355:45;7352:1;7349;7344:67;7051:374;6825:616::o;4597:125:8:-;4651:4;4667:15;4679:2;4667:11;:15::i;:::-;4699:16;4712:2;13302:4;17500:16:2;;;17542:24;17536:4;17529:38;17637:4;17621:21;;17613:30;;17605:39;17599:46;17594:3;17590:56;1434:1:8;13325:30;;13245:117;2504:358;10527:13:1;:11;:13::i;:::-;2648:1:8::1;;;;;2624:13;2614:8;2607:5;2602:48;2716:130;;2771:16;2648:1;;2750:38;2815:16;2648:1;2805:27;13022:198:2::0;13119:26;13132:4;13138:2;13142;13119:12;:26::i;:::-;35982:14;;13155:58;;;13173:40;13196:4;13202:2;13206;13173:40;;;;;;;;;;;;:22;:40::i;1529:138:10:-;1599:16;1634:26;1647:2;1651:8;1634:12;:26::i;:::-;1627:33;1529:138;-1:-1:-1;;;1529:138:10:o;7523:456:1:-;7725:19;7719:4;7712:33;7771:8;7765:4;7758:22;7823:1;7816:4;7810;7800:21;7793:32;7954:8;7908:44;7905:1;7902;7897:66;7523:456::o;3206:345:10:-;3295:13;3320:35;3351:3;;3320:30;:35::i;:::-;3365:26;3394:45;3417:10;3429:3;;3434:4;3394:22;:45::i;:::-;3365:74;;3449:42;3472:18;3449:22;:42::i;:::-;3508:36;3540:3;;3508:31;:36::i;:::-;3501:43;3206:345;-1:-1:-1;;;;;3206:345:10:o;6170:332:2:-;6228:14;15491:16;;;15533:24;15527:4;15520:38;15635:4;15619:21;;15611:30;;15603:39;;15597:46;15581:64;;;6351:135;;6400:10;6394:4;6387:24;6467:4;6461;6454:18;6351:135;6170:332;;;:::o;2178:510:12:-;2259:10;:21;2273:7;2259:21;;2255:72;;2303:13;;;;;;;;;;;;;;2255:72;2560:10;;;;2610:22;;;2560:10;;;;;;;:30;;2610:22;;;;;;;;;;2642:39;;2650:9;;2560:30;;2642:7;:39::i;:::-;;2245:443;2178:510;;:::o;1436:736::-;10527:13:1;:11;:13::i;:::-;1577:326:12::1;::::0;;::::1;::::0;::::1;::::0;;1624:11:::1;1577:326:::0;;::::1;1672:13;1577:326:::0;::::1;;::::0;::::1;::::0;;;1720:24:::1;1577:326:::0;::::1;::::0;;;;;;1789:16:::1;1577:326:::0;;;;;;1838:4:::1;1577:326:::0;;;;;;1884:4:::1;1577:326:::0;;;;;;1539:374;;;;;11972:13:16;;11987:22;11968:42;1539:374:12::1;::::0;::::1;11950:61:16::0;12040:24;;12135:21;;12113:20;;;12106:51;12199:24;;12195:33;;;12173:20;;;12166:63;12260:24;12347:23;;12325:20;;;12318:53;12413:24;12409:33;12387:20;;;12380:63;12495:24;;12488:32;12481:40;12459:20;;;12452:70;1521:7:12::1;::::0;1539:24:::1;::::0;::::1;::::0;::::1;::::0;11922:19:16;;1539:374:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;1923:50:12::1;::::0;;;;:34:::1;1958:14;3472:55:16::0;;1923:50:12::1;::::0;::::1;3454:74:16::0;1923:34:12;::::1;::::0;-1:-1:-1;1923:34:12::1;::::0;-1:-1:-1;3427:18:16;;1923:50:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;1983:62:12::1;::::0;;;;542:42:::1;1983:62;::::0;::::1;12701:74:16::0;2040:4:12::1;12791:18:16::0;;;12784:50;1983:33:12::1;::::0;::::1;::::0;-1:-1:-1;1983:33:12::1;::::0;-1:-1:-1;12674:18:16;;1983:62:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;2055:50:12::1;::::0;;;;641:42:::1;2055:50;::::0;::::1;12701:74:16::0;2100:4:12::1;12791:18:16::0;;;12784:50;2055:19:12::1;::::0;::::1;::::0;-1:-1:-1;2055:19:12::1;::::0;-1:-1:-1;12674:18:16;;2055:50:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;2115:50:12::1;::::0;;;;740:42:::1;2115:50;::::0;::::1;12701:74:16::0;2160:4:12::1;12791:18:16::0;;;12784:50;2115:19:12::1;::::0;::::1;::::0;-1:-1:-1;2115:19:12::1;::::0;-1:-1:-1;12674:18:16;;2115:50:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;1483:689;1436:736::o:0;6646:533:2:-;6709:14;6869:5;6859:143;;6907:10;6901:4;6894:24;6983:4;6977;6970:18;6859:143;7028:24;7022:4;7015:38;7079:5;7073:4;7066:19;7142:20;7134:4;7128;7118:21;7112:28;7108:55;7098:65;;6646:533;;;:::o;6563:100:1:-;10527:13;:11;:13::i;:::-;6635:21:::1;6653:1;6635:9;:21::i;:::-;6563:100::o:0;522:380:7:-;701:10;:15;;;;;;715:1;701:15;;;;;;;;;736:125;;;;774:20;787:6;13302:4:8;17500:16:2;;;17542:24;17536:4;17529:38;17637:4;17621:21;;17613:30;;17605:39;17599:46;17594:3;17590:56;1434:1:8;13325:30;;13245:117;774:20:7;769:82;;821:15;;;;;;;;;;;;;;769:82;870:25;876:10;888:6;870:5;:25::i;1391:132:10:-;1447:16;1482:34;1495:10;1507:8;1482:12;:34::i;8888:713:2:-;9103:10;9096:18;9089:26;9075:40;;9212:8;9206:4;9199:22;9247:31;9241:4;9234:45;9305:8;9299:4;9292:22;9357:10;9350:4;9344;9334:21;9327:41;9442:10;9436:4;9429:24;9561:8;9557:2;9553:17;9549:2;9545:26;9535:8;9500:33;9494:4;9488;9466:119;8888:713;;:::o;6681:266:11:-;6753:44;1173:11:8;6786:3:11;6753:16;:44::i;:::-;6812:9;6807:134;6823:14;;;6807:134;;;6854:17;6864:3;;6868:1;6864:6;;;;;;;:::i;:::-;;;;;;;6854:9;:17::i;:::-;6913:3;;6807:134;;6913:1312:8;7044:22;7183:4;7177:11;7296:10;7293:1;7289:18;7422:8;7410:10;7398;7385:46;7531:183;;7784:1;7777:17;;;;7868:4;7861:23;;;;-1:-1:-1;7957:4:8;7950:18;;;8124:4;8118;8108:21;8185:24;;;-1:-1:-1;8108:21:8;;6913:1312;-1:-1:-1;6913:1312:8:o;2598:147:10:-;2671:34;2703:1;2671:31;:34::i;:::-;;2715:23;2729:2;2733:4;2715:13;:23::i;13723:249:2:-;13869:26;13882:4;13888:2;13892;13869:12;:26::i;:::-;35982:14;;13905:60;;;13923:42;13946:4;13952:2;13956;13960:4;;13923:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;13923:22:2;;-1:-1:-1;;;13923:42:2:i;1510:668:11:-;1579:16;1607:42;1113:13:8;1638:3:11;1607:16;:42::i;:::-;1884:10;1853:12;1946:3;1932:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1932:25:11;;1906:51;;1972:9;1967:182;1983:14;;;1967:182;;;2026:29;2042:3;;2046:1;2042:6;;;;;;;:::i;:::-;;;;;;;2050:4;2026:15;:29::i;:::-;2014:6;2021:1;2014:9;;;;;;;;:::i;:::-;;;;;;;;;;:41;2097:3;2118:6;;;;2097:3;1967:182;;;-1:-1:-1;2165:6:11;1510:668;-1:-1:-1;;;;1510:668:11:o;666:210:9:-;734:13;759:15;771:2;759:11;:15::i;:::-;838:30;844:14;855:2;844:10;:14::i;:::-;838:28;:30::i;:::-;791:78;;;;;;;;:::i;:::-;;;;;;;;;;;;;784:85;;666:210;;;:::o;2764:186:11:-;2857:33;1113:13:8;2888:1:11;2857:16;:33::i;:::-;2900:43;2924:5;2931;2938:4;2900:23;:43::i;4709:321::-;4813:46;1242:13:8;4857:1:11;4813:16;:46::i;:::-;4870:43;4894:5;4901;4908:4;4870:23;:43::i;:::-;12084:14;;;;:10;:14;;;;;;;:26;;;;5012:10;12084:26;;;18050:24:2;18044:4;18037:38;18125:21;;;18117:30;;18109:39;;18175:20;;18266:3;18262:16;;;18251:28;;;18242:38;;;18230:51;;;18208:74;;9956:2995;;;:::o;947:167:9:-;991:13;1070:36;1076:20;:18;:20::i;1070:36::-;1023:84;;;;;;;;:::i;:::-;;;;;;;;;;;;;1016:91;;947:167;:::o;5434:469:11:-;5590:13;5619:73;5650:6;;5658;;1242:13:8;5619:30:11;:73::i;:::-;5702:26;5731:48;5754:10;5766:6;;5774:4;5731:22;:48::i;:::-;5702:77;;5789:42;5812:18;5789:22;:42::i;:::-;5848:48;5881:6;;5889;;5848:32;:48::i;8166:708:1:-;10527:13;:11;:13::i;:::-;8400:19:::1;8394:4;8387:33;8446:12;8440:4;8433:26;8508:4;8502;8492:21;8614:12;8608:19;8595:11;8592:36;8589:157;;;8660:10;8654:4;8647:24;8727:4;8721;8714:18;8589:157;8823:1;8802:23:::0;;8844::::1;8854:12:::0;8844:9:::1;:23::i;6149:349::-:0;10527:13;:11;:13::i;:::-;6321:8:::1;6317:2;6313:17;6303:150;;6363:10;6357:4;6350:24;6434:4;6428;6421:18;6303:150;6472:19;6482:8;6472:9;:19::i;12027:345:8:-:0;12289:8;12277:9;:20;12263:9;:35;12259:97;;12325:16;;;;;;;;;;;;;;11485:244:11;11535:38;11570:2;11535:34;:38::i;:::-;12084:14;;;;:10;:14;;;;;;;:26;;;;11632:10;12084:26;;;18050:24:2;18044:4;18037:38;18125:21;;;18117:30;;18109:39;;18175:20;;18266:3;18262:16;;;18251:28;;;18242:38;;;18230:51;;;18208:74;;11704:18:11;;4145:25:16;;;11704:18:11;;4133:2:16;4118:18;11704::11;;;;;;;11485:244;:::o;14979:382:8:-;15130:20;;;15126:79;;15173:21;;;;;;;;;;;;;;15126:79;15229:23;15218:34;;15214:94;;;15275:22;;;;;;;;;;;;;;15214:94;15317:37;15334:1;15344:9;15317:16;:37::i;1249:1134:14:-;1445:10;1404:26;1433:23;;;;;;;;;;;:47;;;;;;;;;1663:15;:36;;;1748:19;1803:16;2115:32;;;2149:29;;;2112:67;2192:175;;;;2241:27;2238:1;2231:38;2299:18;2293:4;2286:32;2348:4;2342;2335:18;7383:831:11;7502:13;7531:22;7567:6;7556:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7556:25:11;;7531:50;;7591:24;7630:9;7625:310;7641:17;;;7625:310;;;7679:48;7706:6;;7713:1;7706:9;;;;;;;:::i;:::-;;;;;;;7717:6;;7724:1;7717:9;;;;;;;:::i;:::-;;;;;;;7679:26;:48::i;:::-;7675:191;;;7761:4;7747:8;7756:1;7747:11;;;;;;;;:::i;:::-;:18;;;:11;;;;;;;;;;;:18;7815;;;;;7675:191;7907:3;;7625:310;;;;8018:16;8038:1;8018:21;8014:74;;8062:15;;;;;;;;;;;;;;8014:74;8131:50;1113:13:8;8164:16:11;8131:18;:50::i;:::-;-1:-1:-1;8199:8:11;7383:831;-1:-1:-1;;;;;7383:831:11:o;26552:1436:2:-;26735:1;26731:6;26727:2;26723:15;26782:7;26766:14;26762:28;26751:39;;26829:2;26813:14;26809:23;26803:29;;26902:2;26896:4;26889:16;26960:2;26934:24;26931:32;26925:4;26918:46;27030:4;27024;27014:21;27010:2;27006:30;27002:2;26998:39;27089:13;27083:20;27067:14;27063:41;27050:54;;27178:5;27168:134;;27216:10;27210:4;27203:24;27283:4;27277;27270:18;27168:134;27485:5;27481:2;27478:13;27473:2;27466:10;27463:29;27453:280;;27525:5;27519:4;27512:19;27580:4;27574;27564:21;27558:28;27548:171;;27623:10;27617:4;27610:24;27696:4;27690;27683:18;27548:171;27827:1;27823:21;27816:38;;;27969:2;27846:7;27953:5;27926:25;27920:4;;27909:63;;26552:1436;;;:::o;6005:602:10:-;6091:7;6132:24;6114:15;:42;6110:92;;;6179:12;;;;;;;;;;;;;;6110:92;6211:47;1057:11:8;6240:17:10;6211:16;:47::i;:::-;-1:-1:-1;6493:10:10;;;;;;;;;;:39;;;6552:22;;;;;;;;;;;6005:602::o;10974:791:8:-;11038:7;11105:15;;;11146:12;11140:4;11133:26;11337:4;11331;11321:21;11344:10;11317:38;11522:213;13924:14;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;;14180:46;11529:34;11522:213;;11695:1;11685:11;1392:8;11684:26;11522:213;;18786:1676:2;19014:20;;;;;;19098:135;;19143:10;19137:4;19130:24;19214:4;19208;19201:18;19098:135;19299:2;19293:4;19286:16;19328:24;19322:4;19315:38;19419:4;19413;19403:21;19399:2;19395:30;19391:2;19387:39;19468:13;19462:20;19557:15;19553:2;19549:24;19546:146;;;19605:10;19599:4;19592:24;19673:4;19667;19660:18;19546:146;19765:23;;19743:46;;19878:4;19871:16;;;19939:4;19933;19923:21;19990:18;;20010:1;19986:26;20062:20;20039:44;;20029:190;;20120:10;20114:4;20107:24;20196:4;20190;20183:18;20029:190;20236:38;;20394:2;20390;20387:1;20360:25;20387:1;;20343:54;8147:119;;:::o;9751:298:11:-;9823:7;9842:41;9877:5;9842:34;:41::i;:::-;9919:12;9925:5;9919;:12::i;:::-;9941:15;9959:23;9977:4;9959:17;:23::i;:::-;9941:41;;9992:26;9998:10;10010:7;9992:5;:26::i;21497:82:2:-;21551:21;21565:1;21569:2;21551:5;:21::i;11830:136:8:-;1392:8;11895:6;:19;11891:69;;;11937:12;;;;;;;;;;;;;;5425:364:1;5637:20;5631:27;5621:8;5618:41;5608:165;;5692:10;5686:4;5679:24;5754:4;5748;5741:18;36203:1405:2;36440:4;36434:11;36490:10;36523:24;36520:1;36513:35;36582:8;36575:4;36572:1;36568:12;36561:30;36690:4;36686:2;36682:13;36678:2;36674:22;36667:4;36664:1;36660:12;36653:44;36731:2;36724:4;36721:1;36717:12;36710:24;36768:4;36761;36758:1;36754:12;36747:26;36801:4;36795:11;36840:1;36833:4;36830:1;36826:12;36819:23;36858:1;36855:71;;;36921:1;36914:4;36911:1;36907:12;36904:1;36897:4;36891;36887:15;36884:1;36877:5;36866:57;36862:62;36855:71;37042:4;37039:1;37032:4;37029:1;37025:12;37018:4;37015:1;37011:12;37008:1;37004:2;36997:5;36992:55;36982:348;;37070:16;37067:220;;;37201:16;37195:4;37189;37174:44;37252:16;37246:4;37239:30;37067:220;37314:1;37311;37304:12;36982:348;;37426:24;37421:3;37417:34;37413:1;37407:8;37404:48;37394:198;;37485:10;37479:4;37472:24;37573:4;37567;37560:18;3818:258:10;3888:16;3959:17;3979:41;4011:8;3979:31;:41::i;:::-;3959:61;;4037:32;4045:2;4049:8;4059:9;4037:7;:32::i;:::-;4030:39;3818:258;-1:-1:-1;;;;3818:258:10:o;7436:341::-;7546:24;7528:15;:42;7524:92;;;7593:12;;;;;;;;;;;;;;7524:92;7642:23;7629:36;;7625:96;;;7688:22;;;;;;;;;;;;;;7625:96;7730:40;7747:3;1057:11:8;7730:16:10;:40::i;4999:658::-;5082:13;5107:20;5141:3;5130:22;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5130:22:10;;5107:45;;5162:22;5199:9;5194:287;5210:14;;;5194:287;;;5245:32;5270:3;;5274:1;5270:6;;;;;;;:::i;:::-;;;;;;;5245:24;:32::i;:::-;5241:171;;;5309:4;5297:6;5304:1;5297:9;;;;;;;;:::i;:::-;:16;;;:9;;;;;;;;;;;:16;5363;;;;;5241:171;5453:3;;5194:287;;;;5494:14;5512:1;5494:19;5490:72;;5536:15;;;;;;;;;;;;;;5490:72;5572:55;5612:14;5572:39;:55::i;:::-;-1:-1:-1;5644:6:10;;4999:658;-1:-1:-1;;;;4999:658:10:o;4288:500::-;4372:16;4400:25;4442:8;4428:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4428:23:10;;4400:51;;4466:9;4461:296;4481:8;4477:1;:12;4461:296;;;4545:15;4563:28;4581:9;4563:17;:28::i;:::-;4545:46;;4605:18;4611:2;4615:7;4605:5;:18::i;:::-;4651:7;4637:8;4646:1;4637:11;;;;;;;;:::i;:::-;;;;;;;;;;:21;-1:-1:-1;4700:3:10;4721:11;;;;4700:3;4461:296;;4872:495:1;5019:20;5248:16;;5104:26;;;;;;;5208:38;5205:1;;5197:78;5324:27;4872:495::o;21868:2121:2:-;21934:13;21950:11;21958:2;21950:7;:11::i;:::-;21934:27;;-1:-1:-1;22216:4:2;22209:16;;;22136:20;;;;22254:24;22251:32;;22245:4;22238:46;22350:4;22334:21;;;22326:30;;22318:39;;22393:20;;22136;;22516:33;;;;22613:134;;22661:10;22655:4;22648:24;22728:4;22722;22715:18;22613:134;22841:5;22835:4;22828:19;22900:13;22897:1;22893:21;22887:28;23132:15;23128:2;23125:23;23117:5;23113:2;23110:13;23107:42;23102:2;23095:10;23092:58;23082:293;;23206:4;23200;23190:21;23184:28;23174:183;;23253:10;23247:4;23240:24;23330:4;23324;23317:18;23174:183;23450:15;23447:55;;;23498:1;23482:13;23479:1;23475:21;23468:32;23447:55;-1:-1:-1;23583:27:2;;23561:50;;23726:4;23720;23710:21;23772:18;;23768:26;;23748:47;;23918:2;-1:-1:-1;23604:5:2;23881:25;-1:-1:-1;;23864:57:2;9956:2995;;;:::o;8490:518:8:-;8605:10;5994:2;5990:15;6015:10;6007:19;;5987:40;8558:26;5977:51;6048:4;6041:18;;;6182:4;6176;6166:21;8688:15;6007:19;8688:11;:15::i;:::-;8769:42;8792:18;8769:22;:42::i;:::-;8903:16;8916:2;13302:4;17500:16:2;;;17542:24;17536:4;17529:38;17637:4;17621:21;;17613:30;;17605:39;17599:46;17594:3;17590:56;1434:1:8;13325:30;;13245:117;8903:16;8899:72;;;8942:18;;;;;;;;;;;;;;8899:72;8980:21;8986:10;8998:2;8980:5;:21::i;1165:589:9:-;1228:13;1304:22;;;;;;;;;;;;;;;;;;1316:9;1322:2;1316:5;:9::i;:::-;1304:3;:22::i;:::-;1357:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:3;:42::i;:::-;1430:168;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:3;:168::i;:::-;1629:27;;;;;;;;;;;;;;;;;;1642:13;1652:2;1642:9;:13::i;1629:27::-;1687:33;;;;;;;;;;;;;;;;;;1708:11;1716:2;1708:7;:11::i;:::-;1687:6;:33::i;:::-;1260:487;;;;;;;;;;;;:::i;3416:132:3:-;3474:20;3515:26;3522:4;3528:5;3535;3515:6;:26::i;10380:228:11:-;10476:41;10511:5;10476:34;:41::i;:::-;10553:12;10559:5;10553;:12::i;:::-;10575:26;10589:5;10596:4;10575:13;:26::i;1808:224:9:-;1861:13;1886:139;;;;;;;;;;;;;;;;;;;1808:224;:::o;8611:902:11:-;8741:13;8770:22;8806:6;8795:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;8795:25:11;;8770:50;;8830:24;8869:9;8864:365;8880:17;;;8864:365;;;8918:48;8945:6;;8952:1;8945:9;;;;;;;:::i;8918:48::-;8914:246;;;8986:37;9001:6;;9008:1;9001:9;;;;;;;:::i;:::-;;;;;;;;;;12084:14;;;;:10;:14;;;;;;:26;;;;9012:10;12084:26;;;18050:24:2;18044:4;18037:38;18125:21;;;18117:30;;18109:39;18175:20;;18266:3;18262:16;;;18251:28;;;18242:38;;;18230:51;;;18208:74;;;-1:-1:-1;8147:119:2;8986:37:11;9055:4;9041:8;9050:1;9041:11;;;;;;;;:::i;:::-;:18;;;:11;;;;;;;;;;;:18;9109;;;;;8914:246;9201:3;;8864:365;;;;9304:16;9324:1;9304:21;9300:74;;9348:15;;;;;;;;;;;;;;9300:74;9417:63;1242:13:8;9463:16:11;9417:18;:63::i;14248:440:8:-;14328:18;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;;14180:46;;14493:17;14478:32;;14474:88;;;14533:18;;;;;;;;;;;;;;14474:88;14613:19;;;14622:10;14613:19;14609:73;;14655:16;;;;;;;;;;;;;;10924:327:11;11008:4;11024:41;11059:5;11024:34;:41::i;:::-;11128:31;11153:5;11128:24;:31::i;:::-;11124:99;;;11175:12;11181:5;11175;:12::i;:::-;-1:-1:-1;11208:4:11;11201:11;;11124:99;-1:-1:-1;11239:5:11;10924:327;;;;:::o;12618:464:8:-;12911:29;;;12898:9;:43;;;;12959:16;12955:111;;12995:56;13027:10;13039:11;12995:31;:56::i;9932:455::-;9996:4;10063:15;10075:2;10063:11;:15::i;:::-;13924:14;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;;14180:46;10178:29;10174:119;;-1:-1:-1;10277:5:8;;9932:455;-1:-1:-1;9932:455:8:o;10174:119::-;10338:21;10344:10;10356:2;10338:5;:21::i;:::-;-1:-1:-1;10376:4:8;;9932:455;-1:-1:-1;9932:455:8:o;6766:532:10:-;7144:10;;;7185:30;7144:10;;;;;;;:21;;7185:30;;;;;;;;;;;;;6851:7;;7225:40;1057:11:8;7157:8:10;7225:18;:40::i;2079:144:9:-;2129:13;2180:35;:2;2212:1;2180:22;:35::i;:::-;2161:55;;;;;;;;:::i;3719:157::-;3795:13;3846:3;3858:5;3827:42;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3820:49;;3719:157;;;;:::o;2291:162::-;2345:13;2421:24;2427:8;2432:2;2427:4;:8::i;2421:24::-;2377:69;;;;;;;;:::i;2921:500::-;2973:13;2998:19;3020:26;;;;;;;;;;;;;;;;;;3036:9;3042:2;3036:5;:9::i;:::-;3020:6;:26::i;:::-;2998:48;;3060:15;3072:2;3060:11;:15::i;:::-;3056:359;;;3091:27;3121:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:6;:26::i;:::-;3236:49;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3256:14:9;;;:10;:14;;;;;;;;3091:56;;-1:-1:-1;3204:5:9;;3091:56;;3236:49;;3256:28;;:14;;:26;:28::i;3236:49::-;3168:136;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3161:143;;;;2921:500;;;:::o;3056:359::-;3361:5;3373:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:6;:25::i;:::-;3342:62;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3335:69;;;2921:500;;;:::o;3942:154::-;4021:13;4072:3;4083:5;4053:36;;;;;;;;;:::i;722:2559:3:-;835:20;961:4;955:11;983:10;980:2285;;;1183:1;1179;1167:10;1163:18;1159:26;1156:1;1152:34;1294:4;1288:11;1278:21;;1666:34;1660:4;1653:48;1793:6;1782:8;1775:16;1771:29;1735:34;1731:70;1725:4;1718:84;1908:4;1900:6;1896:17;1950:13;1945:3;1941:23;2040:623;2092:1;2086:4;2082:12;2074:20;;2154:4;2148:11;2294:4;2286:5;2282:2;2278:14;2274:25;2268:32;2265:1;2257:44;2359:4;2351:5;2347:2;2343:14;2339:25;2333:32;2330:1;2322:44;2423:4;2415:5;2412:1;2408:13;2404:24;2398:31;2395:1;2387:43;2479:4;2472:5;2468:16;2462:23;2459:1;2451:35;;2525:4;2519:11;2514:3;2507:24;2569:1;2564:3;2560:11;2553:18;;2630:3;2625;2622:12;2040:623;2612:33;2702:4;2693:14;2687:4;2680:28;2971:16;2847:1;2831:18;;2828:1;2824:26;2958:11;;;2951:37;;;;3136:1;3077:17;;3070:25;3066:33;;;3123:11;;;;3116:22;3208:21;;3193:37;;980:2285;;722:2559;;;;;:::o;2187:478:5:-;2434:1;2431;2428;2425;2417:6;2413:2;2406:5;2401:35;2391:258;;2542:10;2536:4;2529:24;2630:4;2624;2617:18;5429:1991:4;5536:17;5997:4;5993:9;5986:4;5977:6;5974:1;5970:14;5966:25;5962:41;5955:4;5949:11;5945:59;5938:66;;6075:4;6070:3;6066:14;6060:4;6053:28;6156:1;6151:3;6144:14;6243:3;6330:34;6324:4;6317:48;6413:6;6405;6401:19;6396:3;6392:29;6447:1;6443:6;6482:5;6668:288;6788:2;6778:13;;6772:20;6705:11;;;;6768:1;6759:11;;6751:42;6847:2;6840:4;6837:1;6833:12;6829:21;6823:28;6818:3;6810:42;6881:1;6877:12;6906:36;;;6668:288;6906:36;6973:4;6970:223;;;7086:10;7080:4;7073:24;7174:4;7168;7161:18;6970:223;-1:-1:-1;;;7268:13:4;;;7355:14;;;;7382:22;;;7355:14;5429:1991;-1:-1:-1;;5429:1991:4:o;2532:305:9:-;2581:13;2759:35;:2;2791:1;2759:22;:35::i;:::-;2613:217;;;;;;;;:::i;3490:183::-;3569:13;3634:3;3654:5;3601:65;;;;;;;;;:::i;11189:436:4:-;11248:17;11283:26;11303:5;11283:19;:26::i;:::-;11406:10;;11468:6;11456:19;;11418:1;11402:18;11521:11;;;;11566:22;;;11521:11;11189:436;-1:-1:-1;;11189:436:4:o;11762:1294::-;11829:17;11937:4;11931:11;11924:18;;12264:4;12259:3;12255:14;12249:4;12242:28;12355:34;12349:4;12342:48;12420:1;12415:3;12411:11;12404:18;;12447:2;12442:3;12435:15;12482:4;12477:3;12473:14;12519:1;12514:2;12511:1;12507:10;12500:21;12552:5;12548:2;12544:14;12535:23;;12755:1;12740:300;12805:1;12802;12798:9;12795:1;12791:17;12845:5;12842:1;12837:14;12903:2;12897:4;12893:13;12887:20;12883:1;12880;12876:9;12868:40;12949:4;12946:1;12942:12;12936:19;12933:1;12925:31;-1:-1:-1;;12985:1:4;12978:9;;;13007;;12740:300;13004:22;12744:14;;11762:1294;;;:::o;14:332:16:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;180:9;167:23;230:66;223:5;219:78;212:5;209:89;199:117;;312:1;309;302:12;543:180;602:6;655:2;643:9;634:7;630:23;626:32;623:52;;;671:1;668;661:12;623:52;-1:-1:-1;694:23:16;;543:180;-1:-1:-1;543:180:16:o;728:367::-;791:8;801:6;855:3;848:4;840:6;836:17;832:27;822:55;;873:1;870;863:12;822:55;-1:-1:-1;896:20:16;;939:18;928:30;;925:50;;;971:1;968;961:12;925:50;1008:4;1000:6;996:17;984:29;;1068:3;1061:4;1051:6;1048:1;1044:14;1036:6;1032:27;1028:38;1025:47;1022:67;;;1085:1;1082;1075:12;1022:67;728:367;;;;;:::o;1100:841::-;1231:6;1239;1247;1255;1263;1316:2;1304:9;1295:7;1291:23;1287:32;1284:52;;;1332:1;1329;1322:12;1284:52;1372:9;1359:23;1401:18;1442:2;1434:6;1431:14;1428:34;;;1458:1;1455;1448:12;1428:34;1497:70;1559:7;1550:6;1539:9;1535:22;1497:70;:::i;:::-;1586:8;;-1:-1:-1;1471:96:16;-1:-1:-1;1674:2:16;1659:18;;1646:32;;-1:-1:-1;1690:16:16;;;1687:36;;;1719:1;1716;1709:12;1687:36;;1758:72;1822:7;1811:8;1800:9;1796:24;1758:72;:::i;:::-;1100:841;;;;-1:-1:-1;1849:8:16;1931:2;1916:18;1903:32;;1100:841;-1:-1:-1;;;;1100:841:16:o;1946:642::-;2111:2;2163:21;;;2233:13;;2136:18;;;2255:22;;;2082:4;;2111:2;2334:15;;;;2308:2;2293:18;;;2082:4;2377:185;2391:6;2388:1;2385:13;2377:185;;;2466:13;;2459:21;2452:29;2440:42;;2537:15;;;;2502:12;;;;2413:1;2406:9;2377:185;;;-1:-1:-1;2579:3:16;;1946:642;-1:-1:-1;;;;;;1946:642:16:o;2593:250::-;2678:1;2688:113;2702:6;2699:1;2696:13;2688:113;;;2778:11;;;2772:18;2759:11;;;2752:39;2724:2;2717:10;2688:113;;;-1:-1:-1;;2835:1:16;2817:16;;2810:27;2593:250::o;2848:455::-;2997:2;2986:9;2979:21;2960:4;3029:6;3023:13;3072:6;3067:2;3056:9;3052:18;3045:34;3088:79;3160:6;3155:2;3144:9;3140:18;3135:2;3127:6;3123:15;3088:79;:::i;:::-;3219:2;3207:15;3224:66;3203:88;3188:104;;;;3294:2;3184:113;;2848:455;-1:-1:-1;;2848:455:16:o;3539:196::-;3607:20;;3667:42;3656:54;;3646:65;;3636:93;;3725:1;3722;3715:12;3740:254;3808:6;3816;3869:2;3857:9;3848:7;3844:23;3840:32;3837:52;;;3885:1;3882;3875:12;3837:52;3908:29;3927:9;3908:29;:::i;:::-;3898:39;3984:2;3969:18;;;;3956:32;;-1:-1:-1;;;3740:254:16:o;4181:160::-;4246:20;;4302:13;;4295:21;4285:32;;4275:60;;4331:1;4328;4321:12;4346:505;4438:6;4446;4454;4507:2;4495:9;4486:7;4482:23;4478:32;4475:52;;;4523:1;4520;4513:12;4475:52;4563:9;4550:23;4596:18;4588:6;4585:30;4582:50;;;4628:1;4625;4618:12;4582:50;4667:70;4729:7;4720:6;4709:9;4705:22;4667:70;:::i;:::-;4756:8;;-1:-1:-1;4641:96:16;-1:-1:-1;4810:35:16;;-1:-1:-1;4841:2:16;4826:18;;4810:35;:::i;:::-;4800:45;;4346:505;;;;;:::o;4856:328::-;4933:6;4941;4949;5002:2;4990:9;4981:7;4977:23;4973:32;4970:52;;;5018:1;5015;5008:12;4970:52;5041:29;5060:9;5041:29;:::i;:::-;5031:39;;5089:38;5123:2;5112:9;5108:18;5089:38;:::i;:::-;5079:48;;5174:2;5163:9;5159:18;5146:32;5136:42;;4856:328;;;;;:::o;5189:322::-;5266:6;5274;5282;5335:2;5323:9;5314:7;5310:23;5306:32;5303:52;;;5351:1;5348;5341:12;5303:52;5374:29;5393:9;5374:29;:::i;:::-;5364:39;5450:2;5435:18;;5422:32;;-1:-1:-1;5501:2:16;5486:18;;;5473:32;;5189:322;-1:-1:-1;;;5189:322:16:o;5698:632::-;5869:2;5921:21;;;5991:13;;5894:18;;;6013:22;;;5840:4;;5869:2;6092:15;;;;6066:2;6051:18;;;5840:4;6135:169;6149:6;6146:1;6143:13;6135:169;;;6210:13;;6198:26;;6279:15;;;;6244:12;;;;6171:1;6164:9;6135:169;;6335:505;6430:6;6438;6446;6499:2;6487:9;6478:7;6474:23;6470:32;6467:52;;;6515:1;6512;6505:12;6467:52;6555:9;6542:23;6588:18;6580:6;6577:30;6574:50;;;6620:1;6617;6610:12;6574:50;6659:70;6721:7;6712:6;6701:9;6697:22;6659:70;:::i;:::-;6748:8;;6633:96;;-1:-1:-1;6830:2:16;6815:18;;;;6802:32;;6335:505;-1:-1:-1;;;;6335:505:16:o;6845:186::-;6904:6;6957:2;6945:9;6936:7;6932:23;6928:32;6925:52;;;6973:1;6970;6963:12;6925:52;6996:29;7015:9;6996:29;:::i;7619:248::-;7684:6;7692;7745:2;7733:9;7724:7;7720:23;7716:32;7713:52;;;7761:1;7758;7751:12;7713:52;7797:9;7784:23;7774:33;;7826:35;7857:2;7846:9;7842:18;7826:35;:::i;:::-;7816:45;;7619:248;;;;;:::o;7872:254::-;7937:6;7945;7998:2;7986:9;7977:7;7973:23;7969:32;7966:52;;;8014:1;8011;8004:12;7966:52;8037:29;8056:9;8037:29;:::i;:::-;8027:39;;8085:35;8116:2;8105:9;8101:18;8085:35;:::i;8131:437::-;8217:6;8225;8278:2;8266:9;8257:7;8253:23;8249:32;8246:52;;;8294:1;8291;8284:12;8246:52;8334:9;8321:23;8367:18;8359:6;8356:30;8353:50;;;8399:1;8396;8389:12;8353:50;8438:70;8500:7;8491:6;8480:9;8476:22;8438:70;:::i;:::-;8527:8;;8412:96;;-1:-1:-1;8131:437:16;-1:-1:-1;;;;8131:437:16:o;8573:579::-;8677:6;8685;8693;8701;8754:2;8742:9;8733:7;8729:23;8725:32;8722:52;;;8770:1;8767;8760:12;8722:52;8793:29;8812:9;8793:29;:::i;:::-;8783:39;;8873:2;8862:9;8858:18;8845:32;8900:18;8892:6;8889:30;8886:50;;;8932:1;8929;8922:12;8886:50;8971:70;9033:7;9024:6;9013:9;9009:22;8971:70;:::i;:::-;8573:579;;9060:8;;-1:-1:-1;8945:96:16;;9142:2;9127:18;9114:32;;8573:579;-1:-1:-1;;;;8573:579:16:o;9157:248::-;9225:6;9233;9286:2;9274:9;9265:7;9261:23;9257:32;9254:52;;;9302:1;9299;9292:12;9254:52;-1:-1:-1;;9325:23:16;;;9395:2;9380:18;;;9367:32;;-1:-1:-1;9157:248:16:o;9410:808::-;9507:6;9515;9523;9531;9539;9592:3;9580:9;9571:7;9567:23;9563:33;9560:53;;;9609:1;9606;9599:12;9560:53;9632:29;9651:9;9632:29;:::i;:::-;9622:39;;9680:38;9714:2;9703:9;9699:18;9680:38;:::i;:::-;9670:48;;9765:2;9754:9;9750:18;9737:32;9727:42;;9820:2;9809:9;9805:18;9792:32;9843:18;9884:2;9876:6;9873:14;9870:34;;;9900:1;9897;9890:12;9870:34;9938:6;9927:9;9923:22;9913:32;;9983:7;9976:4;9972:2;9968:13;9964:27;9954:55;;10005:1;10002;9995:12;9954:55;10045:2;10032:16;10071:2;10063:6;10060:14;10057:34;;;10087:1;10084;10077:12;10057:34;10132:7;10127:2;10118:6;10114:2;10110:15;10106:24;10103:37;10100:57;;;10153:1;10150;10143:12;10100:57;9410:808;;;;-1:-1:-1;9410:808:16;;-1:-1:-1;10184:2:16;10176:11;;10206:6;9410:808;-1:-1:-1;;;9410:808:16:o;10223:316::-;10300:6;10308;10316;10369:2;10357:9;10348:7;10344:23;10340:32;10337:52;;;10385:1;10382;10375:12;10337:52;-1:-1:-1;;10408:23:16;;;10478:2;10463:18;;10450:32;;-1:-1:-1;10529:2:16;10514:18;;;10501:32;;10223:316;-1:-1:-1;10223:316:16:o;10749:260::-;10817:6;10825;10878:2;10866:9;10857:7;10853:23;10849:32;10846:52;;;10894:1;10891;10884:12;10846:52;10917:29;10936:9;10917:29;:::i;:::-;10907:39;;10965:38;10999:2;10988:9;10984:18;10965:38;:::i;11199:354::-;11268:34;11335:10;;;11323;;;11319:27;;11358:12;;;11355:192;;;11403:77;11400:1;11393:88;11504:4;11501:1;11494:15;11532:4;11529:1;11522:15;11355:192;;11199:354;;;;:::o;11558:184::-;11610:77;11607:1;11600:88;11707:4;11704:1;11697:15;11731:4;11728:1;11721:15;12845:184;12897:77;12894:1;12887:88;12994:4;12991:1;12984:15;13018:4;13015:1;13008:15;13034:451;13286:31;13281:3;13274:44;13256:3;13347:6;13341:13;13363:75;13431:6;13426:2;13421:3;13417:12;13410:4;13402:6;13398:17;13363:75;:::i;:::-;13458:16;;;;13476:2;13454:25;;13034:451;-1:-1:-1;;13034:451:16:o;13563:1911::-;14456:3;14451;14444:16;14426:3;14489:6;14483:13;14505:74;14572:6;14568:1;14563:3;14559:11;14552:4;14544:6;14540:17;14505:74;:::i;:::-;14607:6;14602:3;14598:16;14588:26;;14633:3;14664:2;14660:1;14656:2;14652:10;14645:22;14698:6;14692:13;14714:75;14780:8;14776:1;14772:2;14768:10;14761:4;14753:6;14749:17;14714:75;:::i;:::-;14849:1;14808:17;;14841:10;;;14834:22;;;14881:13;;14903:75;14881:13;14965:1;14957:10;;14950:4;14938:17;;14903:75;:::i;:::-;15038:1;14997:17;;15030:10;;;15023:22;;;15070:13;;15092:75;15070:13;15154:1;15146:10;;15139:4;15127:17;;15092:75;:::i;:::-;15227:1;15186:17;;15219:10;;;15212:22;15259:13;;15281:75;15259:13;15343:1;15335:10;;15328:4;15316:17;;15281:75;:::i;:::-;15375:17;15401:41;15439:1;15431:10;;13552:3;13540:16;;13490:68;15401:41;15466:1;15458:10;;13563:1911;-1:-1:-1;;;;;;;13563:1911:16:o;15479:420::-;15730:3;15725;15718:16;15700:3;15763:6;15757:13;15779:74;15846:6;15842:1;15837:3;15833:11;15826:4;15818:6;15814:17;15779:74;:::i;:::-;15873:16;;;;15891:1;15869:24;;15479:420;-1:-1:-1;;15479:420:16:o;15904:1037::-;16353:3;16381:66;16468:2;16463:3;16456:15;16500:6;16494:13;16516:74;16583:6;16579:1;16574:3;16570:11;16563:4;16555:6;16551:17;16516:74;:::i;:::-;16653:66;16649:1;16609:16;;;16641:10;;;16634:86;16745:13;;16767:75;16745:13;16829:1;16821:10;;16814:4;16802:17;;16767:75;:::i;:::-;16861:17;16902:1;16894:10;;16887:22;;;;16933:1;16925:10;;;-1:-1:-1;;;;15904:1037:16:o;16946:448::-;17198:28;17193:3;17186:41;17168:3;17256:6;17250:13;17272:75;17340:6;17335:2;17330:3;17326:12;17319:4;17311:6;17307:17;17272:75;:::i;:::-;17367:16;;;;17385:2;17363:25;;16946:448;-1:-1:-1;;16946:448:16:o;17399:1239::-;18016:3;18011;18004:16;17986:3;18049:6;18043:13;18065:74;18132:6;18128:1;18123:3;18119:11;18112:4;18104:6;18100:17;18065:74;:::i;:::-;18167:6;18162:3;18158:16;18148:26;;18193:3;18224:2;18220:1;18216:2;18212:10;18205:22;18258:6;18252:13;18274:75;18340:8;18336:1;18332:2;18328:10;18321:4;18313:6;18309:17;18274:75;:::i;:::-;18409:1;18368:17;;18401:10;;;18394:22;18441:13;;18463:75;18441:13;18525:1;18517:10;;18510:4;18498:17;;18463:75;:::i;:::-;18602:3;18598:1;18557:17;;;;18590:10;;;18583:23;18630:1;18622:10;;17399:1239;-1:-1:-1;;;;;17399:1239:16:o;18643:891::-;19122:3;19117;19110:16;19092:3;19155:6;19149:13;19171:74;19238:6;19234:1;19229:3;19225:11;19218:4;19210:6;19206:17;19171:74;:::i;:::-;19308:3;19304:1;19264:16;;;19296:10;;;19289:23;19337:13;;19359:75;19337:13;19421:1;19413:10;;19406:4;19394:17;;19359:75;:::i;:::-;19498:3;19494:1;19453:17;;;;19486:10;;;19479:23;19526:1;19518:10;;18643:891;-1:-1:-1;;;;18643:891:16:o;19539:874::-;19928:66;19923:3;19916:79;19898:3;20024:6;20018:13;20040:74;20107:6;20103:1;20098:3;20094:11;20087:4;20079:6;20075:17;20040:74;:::i;:::-;20177:66;20173:1;20133:16;;;20165:10;;;20158:86;20269:13;;20291:75;20269:13;20353:1;20345:10;;20338:4;20326:17;;20291:75;:::i;:::-;20386:17;20405:1;20382:25;;19539:874;-1:-1:-1;;;;19539:874:16:o;20418:996::-;20771:66;20766:3;20759:79;20868:66;20863:2;20858:3;20854:12;20847:88;20965:66;20960:2;20955:3;20951:12;20944:88;21062:66;21057:2;21052:3;21048:12;21041:88;20741:3;21158:6;21152:13;21174:74;21241:6;21235:3;21230;21226:13;21221:2;21213:6;21209:15;21174:74;:::i;:::-;21313:66;21307:3;21267:16;;;;21299:12;;;21292:88;-1:-1:-1;21404:3:16;21396:12;;20418:996;-1:-1:-1;20418:996:16:o;21419:1087::-;21900:66;21895:3;21888:79;21870:3;21996:6;21990:13;22012:75;22080:6;22075:2;22070:3;22066:12;22059:4;22051:6;22047:17;22012:75;:::i;:::-;22151:66;22146:2;22106:16;;;22138:11;;;22131:87;22243:13;;22265:76;22243:13;22327:2;22319:11;;22312:4;22300:17;;22265:76;:::i;:::-;22406:66;22401:2;22360:17;;;;22393:11;;;22386:87;22497:2;22489:11;;21419:1087;-1:-1:-1;;;;21419:1087:16:o
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.