ERC-721
Overview
Max Total Supply
3,413 MD
Holders
326
Market
Volume (24H)
N/A
Min Price (24H)
N/A
Max Price (24H)
N/A
Other Info
Token Contract
Balance
11 MDLoading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Source Code Verified (Exact Match)
Contract Name:
Metadatea
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
No with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /* @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ */ import "erc721a/contracts/ERC721A.sol"; import "erc721a/contracts/extensions/ERC721AQueryable.sol"; import "erc721a/contracts/extensions/ERC721ABurnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "./IERC4906.sol"; import "./Structs.sol"; import "./Art.sol"; import "./Utils.sol"; /// @title an ERC721A contract named Metadate/a /// @author parker thompson /// @notice NFTs with fully on-chain art generation /// @dev implements ERC4906 to notify marketplaces of metadata updates contract Metadatea is ERC721A, ERC721AQueryable, ERC721ABurnable, Ownable, IERC4906 { /// @notice variable to store an instance of the Art contract Art private _art; /// @notice deploys the Metadate/a contract and an instance of the Art contract constructor() ERC721A("Metadate/a", "MD") Ownable(msg.sender) { _art = new Art(); // mint the first token to the deployer address _mint(msg.sender, 1); _mintArt(1, 0); } /// @notice get the start timestamp for collector claiming /// @dev 1705932000 == 9AM EST 1/22/24 uint256 private constant _collectorsStart = 1705932000; /// @notice get the price to mint a token in wei /// @dev 5000000000000000 wei is 0.005 eth, ~$12.50 USD at time of deployment uint256 private constant _mintPrice = 5000000000000000; /// @notice get the status of super rare distribution /// @dev can only be updated by calling the distributeSuperRares function bool private _superRaresDistributed = false; /// @notice get the timestamp when minting closes /// @return mintCloseTime timestamp /// @dev will be updated when totalSupply is greater than _blanksToMint uint256 public mintCloseTime = 55555555555; /// @notice get the number of blanks that can be minted uint256 private constant _blanksToMint = 1000; /// @notice get the number of seconds to add to mintCloseTime after all blanks are minted /// @dev 259200 seconds == 72 hours uint256 private constant _mintSecondsTimerAfterBlanks = 259200; /// @notice get the count of token edits that have been made /// @return totalEdits count /// @dev stored as a public variable for accuracy and convenience uint256 public totalEdits = 0; /// @notice initialize an instance of the Abstract Glitch contract IERC721 private constant _abstractGlitch = IERC721(0xF9f27AA7718dCcF4b4306e0321c2Ce85215fA902); /// @notice map token IDs to a token struct to store metadata mapping(uint256 => Structs.Token) public tokens; /// @notice map wallet addresses that have claimed free tokens mapping(uint256 => bool) public claimedAbstractGlitchTokens; /// @notice map wallet addresses to the allow list mapping(address => bool) public allowListWallets; /// @notice check the active mint progression /// @dev use to qualify other contract functions function checkMintProgression() public view returns (uint256) { if (block.timestamp < _collectorsStart) { return 0; // minting hasn't started } else if (block.timestamp < _collectorsStart + 3 hours) { return 1; // abstract glitch holders claim } else if (block.timestamp < _collectorsStart + 7 hours) { return 2; // allow list mint } else if (block.timestamp < mintCloseTime) { return 3; // public mint } else { return 4; // mint ended } } /// @notice mint Metadate/a (MD) tokens /// @param quantity of tokens to mint /// @dev when _blanksToMint is reached, the minting window is updated function mint(uint256 quantity) public payable { uint256 mintProgression = checkMintProgression(); if (msg.value < quantity * _mintPrice) { revert Utils.NotEnoughEth(); } if (mintProgression == 2) { if ( allowListWallets[msg.sender] || _abstractGlitch.balanceOf(msg.sender) > 0 ) { _handleMint(quantity); } else { revert Utils.NotAllowed(); } } else if (mintProgression == 3) { _handleMint(quantity); } else { revert Utils.NotAllowed(); } } /// @notice claim free Metadate/a (MD) tokens /// @param abstractGlitchId owned by message sender function claim(uint256 abstractGlitchId) public { if ( _abstractGlitch.ownerOf(abstractGlitchId) == msg.sender && !claimedAbstractGlitchTokens[abstractGlitchId] && checkMintProgression() > 0 && checkMintProgression() < 4 ) { _handleMint(1); claimedAbstractGlitchTokens[abstractGlitchId] = true; } else { revert Utils.ClaimNotAvailable(); } } /// @notice internal function to mint Metadate/a (MD) tokens /// @param quantity of tokens to mint function _handleMint(uint256 quantity) internal { for (uint256 i = 0; i < quantity; i++) { uint256 index = _nextTokenId() + i; if (index > _blanksToMint) { _mintArt( index, uint8(Utils.random(index + block.timestamp, 1, 6)) ); } else { if (index == _blanksToMint) { mintCloseTime = block.timestamp + _mintSecondsTimerAfterBlanks; } _mintArt(index, 0); } } if (quantity <= 10) { _mint(msg.sender, quantity); } else { for (uint256 i = 0; i < quantity / 10; i++) { _mint(msg.sender, 10); } if (quantity % 10 > 0) { _mint(msg.sender, quantity % 10); } } } /// @notice saves the initial art of the token minted /// @dev uses the index of the _defaults mapping in the Art contract /// @dev only time the originalArtIndex is updated function _mintArt(uint256 id, uint256 originalArtIndex) internal { tokens[id].originalArtIndex = originalArtIndex; tokens[id].currentArtIndex = originalArtIndex; } /// @notice get the base64 encoded token URI /// @param tokenId of the token /// @return base64 encoded token URI function tokenURI(uint256 tokenId) public view virtual override(ERC721A, IERC721A) returns (string memory) { return _art.getTokenURI(tokenId, tokens[tokenId]); } /// @notice get the SVG art for a token /// @param tokenId of the token /// @return SVG art function getSvg(uint256 tokenId) public view returns (string memory) { return _art.renderSvg(tokens[tokenId]); } /// @notice edit a Metadate/a (MD) token /// @notice blank tokens can be edited once without burning any tokens /// @notice super rare tokens can be edited infinitely without burning any tokens /// @notice common tokens can be edited once by burning 4 other common tokens /// @notice only text, metadata, and text size can be edited for super rare tokens /// @param tokenIds array of token IDs to burn, including the token to keep as the FIRST token in the array /// @param artRules struct of art to update /// @param meta string of metadata to update function edit( uint256[] calldata tokenIds, Structs.ArtRules memory artRules, string memory meta ) public { uint256 tokenIdToKeep = tokenIds[0]; if (!_ownsAllTokens(tokenIds, msg.sender)) { revert Utils.NotAllTokensOwned(); } // checks to see if the token needs to be a blank or a super rare if (tokenIds.length == 1) { // checks to see if the token is a super rare if ( tokens[tokenIdToKeep].currentArtIndex > 7 && tokens[tokenIdToKeep].currentArtIndex < 11 ) { if ( !Utils.validateTextSize( artRules.labelText, artRules.textSize ) ) { revert Utils.InvalidText(); } if (Utils.countCharacters(meta) > 100) { revert Utils.MetaTooLong(); } tokens[tokenIdToKeep].artRules.labelText = artRules.labelText; tokens[tokenIdToKeep].artRules.textSize = artRules.textSize; tokens[tokenIdToKeep].meta = meta; if (!tokens[tokenIdToKeep].edited) { tokens[tokenIdToKeep].edited = true; } } else { if ( tokenIdToKeep > _blanksToMint || tokens[tokenIdToKeep].edited ) { revert Utils.InvalidToken(); } _updateArt(tokenIdToKeep, artRules, meta); } emit MetadataUpdate(tokenIdToKeep); } else if (tokenIds.length == 5) { if (tokens[tokenIdToKeep].edited) { revert Utils.InvalidToken(); } for (uint256 i = 0; i < 5; i++) { if ( Utils.isSuperRareOrBlank( tokens[tokenIds[i]].currentArtIndex ) ) { revert Utils.InvalidToken(); } if (tokenIds[i] == tokenIdToKeep) { _updateArt(tokenIdToKeep, artRules, meta); emit MetadataUpdate(tokenIdToKeep); } else { burn(tokenIds[i]); emit MetadataUpdate(tokenIds[i]); } } } else { revert Utils.InvalidQuantity(); } totalEdits++; } /// @notice updates the metadata of a token /// @dev checks for art validity, but not if a token should be edited /// @dev the edit function checks if a token should be edited /// @dev this function is skipped for super rare tokens function _updateArt( uint256 tokenId, Structs.ArtRules memory artRules, string memory meta ) internal { if (artRules.spectrumIndex > 15) { revert Utils.InvalidSpectrum(); } if (artRules.labelIndex > 7) { revert Utils.InvalidLabel(); } if (!Utils.validateTextSize(artRules.labelText, artRules.textSize)) { revert Utils.InvalidText(); } if (Utils.countCharacters(meta) > 100) { revert Utils.MetaTooLong(); } tokens[tokenId].meta = meta; tokens[tokenId].artRules = Structs.ArtRules({ stroke: artRules.stroke, vertical: artRules.vertical, spectrumIndex: artRules.spectrumIndex, labelIndex: artRules.labelIndex, labelText: artRules.labelText, textSize: artRules.textSize, whiteText: artRules.whiteText }); // only update these if token has not been edited yet if (!tokens[tokenId].edited) { tokens[tokenId].edited = true; tokens[tokenId].currentArtIndex = 11; } } /// @notice check if all tokens in an array are owned by the same address function _ownsAllTokens(uint256[] calldata ids, address addy) internal view returns (bool) { for (uint256 i = 0; i < ids.length; i++) { if (ownerOf(ids[i]) != addy) { return false; } } return true; } /// @notice get the total number of tokens burned /// @return totalBurned count /// @dev will not include tokens manually sent to a 'burn' or 0x address function totalBurned() public view returns (uint256) { return _totalBurned(); } /// @notice starts the token ID at 1 instead of 0 function _startTokenId() internal view virtual override returns (uint256) { return 1; } /// @notice updates artwork for tokens that are burned function _beforeTokenTransfers( address from, address to, uint256 startTokenId, uint256 quantity ) internal virtual override { super._beforeTokenTransfers(from, to, startTokenId, quantity); if (to == address(0)) { tokens[startTokenId].currentArtIndex = 7; } } /// @notice add wallets to the allow list /// @param wallets to add function addAllowlistWallets(address[] calldata wallets) public onlyOwner { if (checkMintProgression() > 0) { revert Utils.NotAllowed(); } for (uint256 i = 0; i < wallets.length; i++) { allowListWallets[wallets[i]] = true; } } /* @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ */ /// @notice distributes super rare tokens /// @dev can only be called once after mint is closed function distributeSuperRares() public onlyOwner { if (checkMintProgression() < 4 || _superRaresDistributed) { revert Utils.NotAllowed(); } for (uint256 i = 0; i < 200; i++) { // checks 200 common tokens for upgradability // edited and burned tokens will be skipped // it is likely that less than 200 super rares will exist uint256 random = Utils.random(i + 2024, _blanksToMint + 1, _totalMinted()); if (!tokens[random].edited && tokens[random].currentArtIndex != 7) { tokens[random].currentArtIndex = Utils.random(i, 8, 10); } } _superRaresDistributed = true; emit BatchMetadataUpdate(_blanksToMint, _totalMinted()); } /// @notice withdraw 50% of contract funds to each of two addresses function withdraw() external onlyOwner { uint256 bal = address(this).balance; if (bal == 0) { revert Utils.ZeroBalance(); } uint256 amt1 = bal / 2; uint256 amt2 = bal - amt1; require( payable(0x9390333197446eC19B1B4Dc7dC7fc5ae4957ebBa).send(amt1) && payable(0x38f0dBbA41258639ba0b2cdDC753E46157CE14d0).send(amt2) ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; library Utils { /// @notice get a pseudo random number /// @param input to use as a randomness seed /// @param min value that can be returned /// @param max value that can be returned /// @return number that is randomized based on the params /// @dev the same input will always return the same output /// @dev min and max are inclusive values and can be returned function random( uint256 input, uint256 min, uint256 max ) internal pure returns (uint256) { uint256 randRange = max - min + 1; return min + (uint256(keccak256(abi.encodePacked(input))) % randRange); } /// @notice convert a number to a string /// @param number number to convert /// @return result string from the converted number function uint2string( uint256 number ) internal pure returns (string memory result) { if (number == 0) { return "0"; } uint256 x = number; uint256 length; while (x != 0) { length++; x /= 10; } bytes memory conversion = new bytes(length); uint256 y = length; while (number != 0) { y = y - 1; uint8 temp = (48 + uint8(number - (number / 10) * 10)); bytes1 b1 = bytes1(temp); conversion[y] = b1; number /= 10; } return string(conversion); } /// @notice convert a number to a hex formatted color /// @param color number to convert /// @return hex formatted color string function uint2hex(uint24 color) internal pure returns (string memory) { uint8 red = uint8(color >> 16); uint8 green = uint8(color >> 8); uint8 blue = uint8(color); return string( abi.encodePacked( "#", uint2string(red), uint2string(green), uint2string(blue) ) ); } /// @notice count the characters in a string /// @param input string to count /// @return number of characters in the string /// @dev counts the number of characters in a string /// @dev some complex characters (like emojis) may count as more than one function countCharacters( string memory input ) internal pure returns (uint256) { uint256 length = 0; uint256 i = 0; while (i < bytes(input).length) { if ((uint8(bytes(input)[i]) & 0xC0) != 0x80) { length++; } i++; } return length; } /// @notice validate text size for artwork /// @param textString string value of the text /// @param textSize number size of the text /// @return whether or not the text and size combo are valid function validateTextSize( string memory textString, uint256 textSize ) internal pure returns (bool) { if (textSize < 6 || textSize > 29) { return false; } else { uint256 length = countCharacters(textString); uint256 max; if (length <= 7) { max = 29; } else if (length <= 16) { max = 11; } else if (length <= 22) { max = 9; } else { revert("invalid length"); } return (textSize <= max); } } /// @notice formats characters in a string for SVG code /// @param content string to format /// @return formatted string /// @dev checks for known characters that need to be converted for SVG (<, >, &) function formatCharacters( string memory content ) internal pure returns (string memory) { bytes memory contentBytes = bytes(content); bytes memory result = new bytes(contentBytes.length * 5); uint256 j = 0; for (uint256 i = 0; i < contentBytes.length; i++) { if (contentBytes[i] == "&") { result[j++] = bytes("&")[0]; result[j++] = bytes("a")[0]; result[j++] = bytes("m")[0]; result[j++] = bytes("p")[0]; result[j++] = bytes(";")[0]; } else if (contentBytes[i] == "<") { result[j++] = bytes("&")[0]; result[j++] = bytes("l")[0]; result[j++] = bytes("t")[0]; result[j++] = bytes(";")[0]; } else if (contentBytes[i] == ">") { result[j++] = bytes("&")[0]; result[j++] = bytes("g")[0]; result[j++] = bytes("t")[0]; result[j++] = bytes(";")[0]; } else { result[j++] = contentBytes[i]; } } bytes memory trimmedResult = new bytes(j); for (uint256 k = 0; k < j; k++) { trimmedResult[k] = result[k]; } return string(trimmedResult); } /// @notice check if a token is a super rare or blank /// @param index of the current token art /// @return whether or not the token is a super rare or blank function isSuperRareOrBlank(uint256 index) internal pure returns (bool) { if (index == 0) { return true; } else if (index > 7 && index < 11) { return true; } else { return false; } } error NotEnoughEth(); error MintEnded(); error NotTime(); error ClaimNotAvailable(); error NotAllTokensOwned(); error TokenIdMismatch(); error InvalidToken(); error InvalidText(); error InvalidSpectrum(); error InvalidLabel(); error MetaTooLong(); error InvalidQuantity(); error NotAllowed(); error ZeroBalance(); error AlreadyCalled(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /* ---::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::--- -:. . . . .:- :. .....:..............::::.:: . . . . . . .- :. .+=-+*-=-:.-+:--:=+.###%-%* . . . .- :. .+=-+*-=-:.-+:--:=+.###%-%* . . . . . .- :. .+=-+*-=-:.-+:--:=+.###%-%* . .- :. .+=-+*-=-:.-+:--:=+.###%-%* . . . .- :. .....:..............::::.:: . . . .. .- :. . . . . .- :. . . . . . . . .- :. . . . . . . . .- :. . . . . . . .- :. . . . .- :. . . . . .- :.. . . . .. .- :. . . . . ..- :. . . . . .- :. . . . . . .- :. . . . .- :. . . . . . .- :. .. . . .- :. . ..:-==============================================================-:. .- :.. . .+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%= . .- :. .+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%= .- :. . .#%%%%%%%%%%%%%%%%+-*%%%#--%#------+%--#%%%%%--%%%%%%%+-::-*%%%%%%%%%%%%%%%%%%+. .- :. .+%%%%%%%%%%%%%%%%%= +%%%# .%* .****#% #%%%%%. %%%%%*..=#*:..#%%%%%%%%%%%%%%%%%- .- :.... .%%%%%%%%%%%%%%%%%%= -++++ .%* .****%% #%%%%%. %%%%%: *%%%%-.=%%%%%%%%%%%%%%%%%+. .- :. .%%%%%%%%%%%%%%%%%%= :===- .%* .====%% #%%%%%. %%%%#. #%%%%=.-%%%%%%%%%%%%%%%%%+ .- :. +%%%%%%%%%%%%%%%%%= +%%%# .%* .%%%%%% #%%%%%..%%%%%= .*%%=. *%%%%%%%%%%%%%%%%%- .- :. .#%%%%%%%%%%%%%%%%=.+%%%#.:%#......=%......-%......=%#-...:=%%%%%%%%%%%%%%%%%%+. .- :. ..*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%= ..- :. .. .*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%= .- :. ..:-=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=-:. .- :. .. . . . . .- :. . . . . . . . . .- :. . . . . . . . . . . . .- :. . . . . . . .- :. . . . . . .- :. . . . .- :. . . . . .- :. . .- :. . . .- :.. . . . . .- :. . . . . . . .- :. . . . . . .- :. . . . . .- :. . . . . .- :. . . . . .- :. . ... ... .. .... ... ... .- :. . . . . ................::::.:::..::::. .- :.. . . . . . ................::::::::::::::. .- :. . . . .............. .............. .- -:. .. . . . .:: ---::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::--- */ import "@openzeppelin/contracts/utils/Base64.sol"; import "./Structs.sol"; import "./Utils.sol"; contract Art { /// @notice index predefined label options mapping(uint256 => Structs.Label) private _labels; /// @notice index predefined color spectrums mapping(uint256 => Structs.Spectrum) private _spectrums; /// @notice index predefined art rules mapping(uint256 => Structs.ArtRules) private _defaults; /// @notice horizontal barcode SVG string constant horizontalBarcode = '<path d="M9 26.32H10.12V9H9V26.32Z"/><path d="M10.84 26.32H11.32V9H10.84V26.32Z"/><path d="M12.77 26.32H13.25V9H12.77V26.32Z"/><path d="M14.05 26.32H14.53V9H14.05V26.32Z"/><path d="M15.9 26.32H18.3V9H15.9V26.32Z"/><path d="M19.75 26.32H20.23V9H19.75V26.32Z"/><path d="M21.03 26.32H22.07V9H21.03V26.32Z"/><path d="M23.52 26.32H24V9H23.52V26.32Z"/><path d="M26.65 26.32H27.13V9H26.65V26.32Z"/><path d="M29.78 26.32H30.26V9H29.78V26.32Z"/><path d="M31.71 26.32H32.83V9H31.71V26.32Z"/><path d="M33.63 26.32H34.11V9H33.63V26.32Z"/><path d="M36.76 26.32H37.88V9H36.76V26.32Z"/><path d="M38.69 26.32H39.17V9H38.69V26.32Z"/><path d="M40.57 26.32H41.01V9H40.57V26.32Z"/><path d="M42.46 26.32H42.94V9H42.46V26.32Z"/><path d="M43.74 26.32H44.86V9H43.74V26.32Z"/><path d="M47.51 26.32H47.99V9H47.51V26.32Z"/><path d="M48.8 26.32H51.12V9H48.8V26.32Z"/><path d="M51.93 26.32H53.69V9H51.93V26.32Z"/><path d="M54.49 26.32H55.61V9H54.49V26.32Z"/><path d="M57.62 26.32H59.38V9H57.62V26.32Z"/><path d="M60.11 26.32H60.59V9H60.11V26.32Z"/><path d="M61.4 26.32H62.52V9H61.4V26.32Z"/>'; /// @notice vertical barcode SVG string constant verticalBarcode = '<path d="M9 9L9 10.12L26.32 10.12V9L9 9Z"/><path d="M9 10.84L9 11.32L26.32 11.32V10.84L9 10.84Z"/><path d="M9 12.77L9 13.25L26.32 13.25V12.77L9 12.77Z"/><path d="M9 14.05L9 14.53L26.32 14.53V14.05L9 14.05Z"/><path d="M9 15.9L9 18.3L26.32 18.3V15.9L9 15.9Z"/><path d="M9 19.75L9 20.23L26.32 20.23V19.75L9 19.75Z"/><path d="M9 21.03L9 22.07L26.32 22.07V21.03L9 21.03Z"/><path d="M9 23.52L9 24L26.32 24V23.52L9 23.52Z"/><path d="M9 26.65L9 27.13H26.32V26.65H9Z"/><path d="M9 29.78L9 30.26H26.32V29.78H9Z"/><path d="M9 31.71L9 32.83H26.32V31.71H9Z"/><path d="M9 33.63L9 34.11H26.32V33.63H9Z"/><path d="M9 36.76L9 37.88H26.32V36.76H9Z"/><path d="M9 38.69L9 39.17H26.32V38.69H9Z"/><path d="M9 40.57L9 41.01H26.32L26.32 40.57H9Z"/><path d="M9 42.46L9 42.94H26.32V42.46H9Z"/><path d="M9 43.74L9 44.86H26.32V43.74H9Z"/><path d="M9 47.51L9 47.99H26.32V47.51H9Z"/><path d="M9 48.8L9 51.12H26.32V48.8H9Z"/><path d="M9 51.93L9 53.69H26.32V51.93H9Z"/><path d="M9 54.49L9 55.61H26.32V54.49H9Z"/><path d="M9 57.62L9 59.38H26.32V57.62H9Z"/><path d="M9 60.11L9 60.59H26.32V60.11H9Z"/><path d="M9 61.4L9 62.52H26.32V61.4H9Z"/>'; constructor() { /// @notice populates all of the predefined art mappings _populateData(); } /// @notice generate art rules for a token /// @param token for which to generate art rules /// @return artRules for a given token function _generateArtRules(Structs.Token memory token) internal view returns (Structs.ArtRules memory artRules) { uint256 artIndex = token.currentArtIndex; if (artIndex == 7) { // token is burned artRules = _defaults[7]; } else if (artIndex > 7 && artIndex < 11) { // token is a super rare if (token.edited) { artRules = _defaults[artIndex]; artRules.labelText = token.artRules.labelText; artRules.textSize = token.artRules.textSize; } else { artRules = _defaults[artIndex]; } } else { // token is a blank or a common if (token.edited) { artRules = token.artRules; } else { artRules = _defaults[artIndex]; } } } /// @notice render SVG code for a given token /// @param token for which to render SVG code /// @return svg code function renderSvg(Structs.Token memory token) public view returns (string memory svg) { string memory bg = "#FFF"; string memory barcode; Structs.ArtRules memory artRules = _generateArtRules(token); uint256 artIndex = token.currentArtIndex; if (artIndex > 7 && artIndex < 11) { // token is a super rare // update the background color if (artIndex == 8) { bg = "#F6871F"; } else if (artIndex == 9) { bg = "#00A651"; } else if (artIndex == 10) { bg = "#004E7C"; } } if (artIndex != 7) { barcode = artRules.vertical ? verticalBarcode : horizontalBarcode; } svg = string( abi.encodePacked( '<svg width="800" height="800" viewBox="0 0 202 202" fill="none" xmlns="http://www.w3.org/2000/svg"><rect id="bg" x="1" y="1" rx="6" width="200" height="200" fill="', bg, '"/><rect id="label" ', _labels[artRules.labelIndex].rounded ? 'rx="21"' : "", ' x="19" y="80" width="164" height="42" fill="#212121"/><path id="textpath" d="M19 101L183 101"/><text fill="#231F20" font-family="system-ui" font-size="', Utils.uint2string(artRules.textSize), '" font-weight="bold" text-anchor="middle" letter-spacing="0em" dy=".36em"><textPath href="#textpath" startOffset="50%">', Utils.formatCharacters(artRules.labelText), '</textPath></text><circle id="c6" cx="189" cy="189" r="5"/><circle id="c5" cx="179" cy="189" r="5"/><circle id="c4" cx="169" cy="189" r="5"/><circle id="c3" cx="159" cy="189" r="5"/><circle id="c2" cx="149" cy="189" r="5"/><circle id="c1" cx="139" cy="189" r="5"/><g id="barcode">', barcode, "</g><defs><style>#barcode path{fill:#010101}#label{fill:", _labels[artRules.labelIndex].color, "}text{fill:", artRules.whiteText ? "#FFF" : "#000", "}#bg{", artRules.stroke ? "stroke:#231F20" : "", "}", _spectrums[artRules.spectrumIndex].styles, "</style></defs></svg>" ) ); } /// @notice generate metadata for a given token /// @param tokenId of the token for which to generate metadata /// @param token for which to generate metadata /// @return metadata as a json string function _generateMetadata(uint256 tokenId, Structs.Token memory token) internal view returns (string memory) { return string( abi.encodePacked( '{"name": "Metadate/a ', Utils.uint2string(tokenId), '", "description": "An immersive art installation recording the convergence of data.", "attributes": [', _generateAttributes(token), '], "image": "data:image/svg+xml;base64,', Base64.encode(bytes(renderSvg(token))), '"}' ) ); } /// @notice generate attributes for a given token /// @param token for which to generate attributes /// @return attributes for a given token function _generateAttributes(Structs.Token memory token) internal view returns (bytes memory) { if (token.currentArtIndex != 7) { Structs.ArtRules memory artRules = _generateArtRules(token); return abi.encodePacked( _attribute("Burned", '"false"', true), _attribute( "Edited", token.edited ? '"true"' : '"false"', true ), _attribute( "Original", string(abi.encodePacked('"',_defaults[token.originalArtIndex].labelText,'"')), true ), _attribute( "Super Rare", token.currentArtIndex > 7 && token.currentArtIndex < 11 ? '"true"' : '"false"', true ), _attribute( "Stroke", artRules.stroke ? '"true"' : '"false"', true ), _attribute( "Barcode", artRules.vertical ? '"Vertical"' : '"Horizontal"', true ), _attribute( "Label Style", _labels[artRules.labelIndex].rounded ? '"Round"' : '"Square"', true ), _attribute( "Color Spectrum", string(abi.encodePacked('"',_spectrums[artRules.spectrumIndex].name,'"')), true ), _attribute( "Label Color", string(abi.encodePacked('"',_labels[artRules.labelIndex].name,'"')), true ), _attribute( "Text Color", artRules.whiteText ? '"White"' : '"Black"', true ), _attribute( "Text Size", Utils.uint2string(artRules.textSize), true ), _attribute("Metadata", string(abi.encodePacked('"',token.meta,'"')), false) ); } else { return abi.encodePacked(_attribute("Burned", '"true"', false)); } } /// @notice generate a formatted attribute /// @param attType type of attribute /// @param attValue value of attribute /// @param trailingComma whether or not to include a trailing comma function _attribute( string memory attType, string memory attValue, bool trailingComma ) internal pure returns (string memory) { return string( abi.encodePacked( "{", '"trait_type": "', attType, '",' '"value": ', attValue, "}", trailingComma ? "," : "" ) ); } /// @notice get the base64 encoded token URI /// @param tokenId of the token for which to get the base64 encoded token URI /// @param token for which to get the base64 encoded token URI /// @return base64 encoded token URI function getTokenURI(uint256 tokenId, Structs.Token memory token) public view returns (string memory) { return string( abi.encodePacked( "data:application/json;base64,", Base64.encode(bytes(_generateMetadata(tokenId, token))) ) ); } /// @notice initialize the art labels, spectrums, and defaults /// @dev only called once during deployment function _populateData() internal { _labels[0] = Structs.Label({ name: "White", color: "#FFFEF7", rounded: true }); _labels[1] = Structs.Label({ name: "White", color: "#FFFEF7", rounded: false }); _labels[2] = Structs.Label({ name: "Red", color: "#E60020", rounded: true }); _labels[3] = Structs.Label({ name: "Red", color: "#E60020", rounded: false }); _labels[4] = Structs.Label({ name: "Yellow", color: "#FFAE00", rounded: true }); _labels[5] = Structs.Label({ name: "Yellow", color: "#FFAE00", rounded: false }); _labels[6] = Structs.Label({ name: "Gray", color: "#252425", rounded: true }); _labels[7] = Structs.Label({ name: "Gray", color: "#252425", rounded: false }); _spectrums[0] = Structs.Spectrum({ name: "Lagos", styles: "#c1{fill:#F16690;opacity:80%;}#c2{fill:#F16690;opacity:100%;}#c3{fill:#EFC3A3;opacity:80%;}#c4{fill:#EFC3A3;opacity:100%;}#c5{fill:#FFCF01;opacity:80%;}#c6{fill:#FFCF01;opacity:100%;}" }); _spectrums[1] = Structs.Spectrum({ name: "San Paulo", styles: "#c1{fill:#F15D2A;opacity:80%;}#c2{fill:#F15D2A;opacity:100%;}#c3{fill:#B2A9A7;opacity:80%;}#c4{fill:#B2A9A7;opacity:100%;}#c5{fill:#2D6CB5;opacity:80%;}#c6{fill:#2D6CB5;opacity:100%;}" }); _spectrums[2] = Structs.Spectrum({ name: "Bogota", styles: "#c1{fill:#F26D61;opacity:80%;}#c2{fill:#F26D61;opacity:100%;}#c3{fill:#A3C7E9;opacity:80%;}#c4{fill:#A3C7E9;opacity:100%;}#c5{fill:#669060;opacity:80%;}#c6{fill:#669060;opacity:100%;}" }); _spectrums[3] = Structs.Spectrum({ name: "Johannesburg", styles: "#c1{fill:#EE202E;opacity:80%;}#c2{fill:#EE202E;opacity:100%;}#c3{fill:#F5E1D3;opacity:80%;}#c4{fill:#F5E1D3;opacity:100%;}#c5{fill:#333132;opacity:80%;}#c6{fill:#333132;opacity:100%;}" }); _spectrums[4] = Structs.Spectrum({ name: "Vancouver", styles: "#c1{fill:#231F20;opacity:15%;}#c2{fill:#231F20;opacity:30%;}#c3{fill:#231F20;opacity:45%;}#c4{fill:#231F20;opacity:60%;}#c5{fill:#231F20;opacity:75%;}#c6{fill:#231F20;opacity:90%;}" }); _spectrums[5] = Structs.Spectrum({ name: "Arizona", styles: "#c1{fill:#FFCF01;opacity:15%;}#c2{fill:#FFCF01;opacity:30%;}#c3{fill:#FFCF01;opacity:45%;}#c4{fill:#FFCF01;opacity:60%;}#c5{fill:#FFCF01;opacity:75%;}#c6{fill:#FFCF01;opacity:90%;}" }); _spectrums[6] = Structs.Spectrum({ name: "Miami", styles: "#c1{fill:#EF4277;opacity:80%;}#c2{fill:#EF4277;opacity:100%;}#c3{fill:#26A191;opacity:80%;}#c4{fill:#26A191;opacity:100%;}#c5{fill:#FFCF01;opacity:80%;}#c6{fill:#FFCF01;opacity:100%;}" }); _spectrums[7] = Structs.Spectrum({ name: "San Antonio", styles: "#c1{fill:#FB8C05;opacity:100%;}#c2{fill:#990037;opacity:100%;}#c3{fill:#00315E;opacity:100%;}#c4{fill:#00315E;opacity:100%;}#c5{fill:#990037;opacity:100%;}#c6{fill:#FB8C05;opacity:100%;}" }); _spectrums[8] = Structs.Spectrum({ name: "New York", styles: "#c1{fill:#002850;opacity:15%;}#c2{fill:#002850;opacity:30%;}#c3{fill:#002850;opacity:45%;}#c4{fill:#002850;opacity:60%;}#c5{fill:#002850;opacity:75%;}#c6{fill:#002850;opacity:90%;}" }); _spectrums[9] = Structs.Spectrum({ name: "Maryland", styles: "#c1{fill:#FA8A24;opacity:100%;}#c2{fill:#1A1818;opacity:100%;}#c3{fill:#E60020;opacity:100%;}#c4{fill:#E60020;opacity:100%;}#c5{fill:#1A1818;opacity:100%;}#c6{fill:#FA8A24;opacity:100%;}" }); _spectrums[10] = Structs.Spectrum({ name: "Singapore", styles: "#c1{fill:#B58B7F;opacity:100%;}#c2{fill:#1A1818;opacity:100%;}#c3{fill:#00A4A5;opacity:100%;}#c4{fill:#00A4A5;opacity:100%;}#c5{fill:#1A1818;opacity:100%;}#c6{fill:#B58B7F;opacity:100%;}" }); _spectrums[11] = Structs.Spectrum({ name: "Wyoming", styles: "#c1{fill:#B17E54;opacity:15%;}#c2{fill:#A26C41;opacity:30%;}#c3{fill:#955D31;opacity:45%;}#c4{fill:#874D1F;opacity:60%;}#c5{fill:#783E0C;opacity:75%;}#c6{fill:#6A3100;opacity:90%;}" }); _spectrums[12] = Structs.Spectrum({ name: "Cluj", styles: "#c1{fill:#FFF032;opacity:100%;}#c2{fill:#003E6A;opacity:100%;}#c3{fill:#4E4E49;opacity:100%;}#c4{fill:#4E4E49;opacity:100%;}#c5{fill:#003E6A;opacity:100%;}#c6{fill:#FFF032;opacity:100%;}" }); _spectrums[13] = Structs.Spectrum({ name: "Chicago", styles: "#c1{fill:#E60020;opacity:15%;}#c2{fill:#E60020;opacity:30%;}#c3{fill:#E60020;opacity:45%;}#c4{fill:#E60020;opacity:60%;}#c5{fill:#E60020;opacity:75%;}#c6{fill:#E60020;opacity:90%;}" }); _spectrums[14] = Structs.Spectrum({ name: "Portland", styles: "#c1{fill:#568861;opacity:100%;}#c2{fill:#457B53;opacity:100%;}#c3{fill:#326E43;opacity:100%;}#c4{fill:#206134;opacity:100%;}#c5{fill:#005526;opacity:100%;}#c6{fill:#004B1C;opacity:100%;}" }); _spectrums[15] = Structs.Spectrum({name: "None", styles: ""}); // blank _defaults[0] = Structs.ArtRules({ stroke: false, vertical: false, spectrumIndex: 0, labelIndex: 0, labelText: "", textSize: 9, whiteText: false }); _defaults[1] = Structs.ArtRules({ stroke: false, vertical: false, spectrumIndex: 5, labelIndex: 0, labelText: "July 11th, 2022", textSize: 11, whiteText: false }); _defaults[2] = Structs.ArtRules({ stroke: false, vertical: true, spectrumIndex: 4, labelIndex: 0, labelText: "PAIN BEFORE PROGRESS", textSize: 9, whiteText: false }); _defaults[3] = Structs.ArtRules({ stroke: false, vertical: false, spectrumIndex: 9, labelIndex: 0, labelText: "November 6th, 1990", textSize: 9, whiteText: false }); _defaults[4] = Structs.ArtRules({ stroke: false, vertical: false, spectrumIndex: 14, labelIndex: 0, labelText: "October 31st, 2008", textSize: 9, whiteText: false }); _defaults[5] = Structs.ArtRules({ stroke: false, vertical: true, spectrumIndex: 8, labelIndex: 0, labelText: "THE MARATHON CONTINUES", textSize: 9, whiteText: false }); _defaults[6] = Structs.ArtRules({ stroke: true, vertical: true, spectrumIndex: 3, labelIndex: 0, labelText: "MAMBA MENTALITY", textSize: 9, whiteText: false }); // burned _defaults[7] = Structs.ArtRules({ stroke: false, vertical: false, spectrumIndex: 15, labelIndex: 2, labelText: "*NOT FOR RESALE", textSize: 14, whiteText: true }); // super rare: orange _defaults[8] = Structs.ArtRules({ stroke: false, vertical: true, spectrumIndex: 8, labelIndex: 0, labelText: "", textSize: 9, whiteText: false }); // super rare: green _defaults[9] = Structs.ArtRules({ stroke: false, vertical: false, spectrumIndex: 1, labelIndex: 0, labelText: "", textSize: 9, whiteText: false }); // super rare: blue _defaults[10] = Structs.ArtRules({ stroke: false, vertical: false, spectrumIndex: 13, labelIndex: 2, labelText: "", textSize: 9, whiteText: false }); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; library Structs { struct Label { string name; string color; bool rounded; } struct Spectrum { string name; string styles; } struct ArtRules { bool stroke; bool vertical; bool whiteText; string labelText; uint256 spectrumIndex; uint256 labelIndex; uint256 textSize; } struct Token { bool edited; uint256 originalArtIndex; uint256 currentArtIndex; string meta; ArtRules artRules; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /// @title EIP-721 Metadata Update Extension interface IERC4906 { /// @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 // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or * {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import './IERC721ABurnable.sol'; import '../ERC721A.sol'; /** * @title ERC721ABurnable. * * @dev ERC721A token that can be irreversibly burned (destroyed). */ abstract contract ERC721ABurnable is ERC721A, IERC721ABurnable { /** * @dev Burns `tokenId`. See {ERC721A-_burn}. * * Requirements: * * - The caller must own `tokenId` or be an approved operator. */ function burn(uint256 tokenId) public virtual override { _burn(tokenId, true); } }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import './IERC721AQueryable.sol'; import '../ERC721A.sol'; /** * @title ERC721AQueryable. * * @dev ERC721A subclass with convenience query functions. */ abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable { /** * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. * * If the `tokenId` is out of bounds: * * - `addr = address(0)` * - `startTimestamp = 0` * - `burned = false` * - `extraData = 0` * * If the `tokenId` is burned: * * - `addr = <Address of owner before token was burned>` * - `startTimestamp = <Timestamp when token was burned>` * - `burned = true` * - `extraData = <Extra data when token was burned>` * * Otherwise: * * - `addr = <Address of owner>` * - `startTimestamp = <Timestamp of start of ownership>` * - `burned = false` * - `extraData = <Extra data at start of ownership>` */ function explicitOwnershipOf(uint256 tokenId) public view virtual override returns (TokenOwnership memory) { TokenOwnership memory ownership; if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) { return ownership; } ownership = _ownershipAt(tokenId); if (ownership.burned) { return ownership; } return _ownershipOf(tokenId); } /** * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order. * See {ERC721AQueryable-explicitOwnershipOf} */ function explicitOwnershipsOf(uint256[] calldata tokenIds) external view virtual override returns (TokenOwnership[] memory) { unchecked { uint256 tokenIdsLength = tokenIds.length; TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength); for (uint256 i; i != tokenIdsLength; ++i) { ownerships[i] = explicitOwnershipOf(tokenIds[i]); } return ownerships; } } /** * @dev Returns an array of token IDs owned by `owner`, * in the range [`start`, `stop`) * (i.e. `start <= tokenId < stop`). * * This function allows for tokens to be queried if the collection * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. * * Requirements: * * - `start < stop` */ function tokensOfOwnerIn( address owner, uint256 start, uint256 stop ) external view virtual override returns (uint256[] memory) { unchecked { if (start >= stop) revert InvalidQueryRange(); uint256 tokenIdsIdx; uint256 stopLimit = _nextTokenId(); // Set `start = max(start, _startTokenId())`. if (start < _startTokenId()) { start = _startTokenId(); } // Set `stop = min(stop, stopLimit)`. if (stop > stopLimit) { stop = stopLimit; } uint256 tokenIdsMaxLength = balanceOf(owner); // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`, // to cater for cases where `balanceOf(owner)` is too big. if (start < stop) { uint256 rangeLength = stop - start; if (rangeLength < tokenIdsMaxLength) { tokenIdsMaxLength = rangeLength; } } else { tokenIdsMaxLength = 0; } uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength); if (tokenIdsMaxLength == 0) { return tokenIds; } // We need to call `explicitOwnershipOf(start)`, // because the slot at `start` may not be initialized. TokenOwnership memory ownership = explicitOwnershipOf(start); address currOwnershipAddr; // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`. // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range. if (!ownership.burned) { currOwnershipAddr = ownership.addr; } for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) { ownership = _ownershipAt(i); if (ownership.burned) { continue; } if (ownership.addr != address(0)) { currOwnershipAddr = ownership.addr; } if (currOwnershipAddr == owner) { tokenIds[tokenIdsIdx++] = i; } } // Downsize the array to fit. assembly { mstore(tokenIds, tokenIdsIdx) } return tokenIds; } } /** * @dev Returns an array of token IDs owned by `owner`. * * This function scans the ownership mapping and is O(`totalSupply`) in complexity. * It is meant to be called off-chain. * * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into * multiple smaller scans if the collection is large enough to cause * an out-of-gas error (10K collections should be fine). */ function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) { unchecked { uint256 tokenIdsIdx; address currOwnershipAddr; uint256 tokenIdsLength = balanceOf(owner); uint256[] memory tokenIds = new uint256[](tokenIdsLength); TokenOwnership memory ownership; for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) { ownership = _ownershipAt(i); if (ownership.burned) { continue; } if (ownership.addr != address(0)) { currOwnershipAddr = ownership.addr; } if (currOwnershipAddr == owner) { tokenIds[tokenIdsIdx++] = i; } } return tokenIds; } } }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import './IERC721A.sol'; /** * @dev Interface of ERC721 token receiver. */ interface ERC721A__IERC721Receiver { function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); } /** * @title ERC721A * * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721) * Non-Fungible Token Standard, including the Metadata extension. * Optimized for lower gas during batch mints. * * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...) * starting from `_startTokenId()`. * * Assumptions: * * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply. * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256). */ contract ERC721A is IERC721A { // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364). struct TokenApprovalRef { address value; } // ============================================================= // CONSTANTS // ============================================================= // Mask of an entry in packed address data. uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1; // The bit position of `numberMinted` in packed address data. uint256 private constant _BITPOS_NUMBER_MINTED = 64; // The bit position of `numberBurned` in packed address data. uint256 private constant _BITPOS_NUMBER_BURNED = 128; // The bit position of `aux` in packed address data. uint256 private constant _BITPOS_AUX = 192; // Mask of all 256 bits in packed address data except the 64 bits for `aux`. uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1; // The bit position of `startTimestamp` in packed ownership. uint256 private constant _BITPOS_START_TIMESTAMP = 160; // The bit mask of the `burned` bit in packed ownership. uint256 private constant _BITMASK_BURNED = 1 << 224; // The bit position of the `nextInitialized` bit in packed ownership. uint256 private constant _BITPOS_NEXT_INITIALIZED = 225; // The bit mask of the `nextInitialized` bit in packed ownership. uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225; // The bit position of `extraData` in packed ownership. uint256 private constant _BITPOS_EXTRA_DATA = 232; // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`. uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1; // The mask of the lower 160 bits for addresses. uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1; // The maximum `quantity` that can be minted with {_mintERC2309}. // This limit is to prevent overflows on the address data entries. // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309} // is required to cause an overflow, which is unrealistic. uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000; // The `Transfer` event signature is given by: // `keccak256(bytes("Transfer(address,address,uint256)"))`. bytes32 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; // ============================================================= // STORAGE // ============================================================= // The next token ID to be minted. uint256 private _currentIndex; // The number of tokens burned. uint256 private _burnCounter; // Token name string private _name; // Token symbol string private _symbol; // Mapping from token ID to ownership details // An empty struct value does not necessarily mean the token is unowned. // See {_packedOwnershipOf} implementation for details. // // Bits Layout: // - [0..159] `addr` // - [160..223] `startTimestamp` // - [224] `burned` // - [225] `nextInitialized` // - [232..255] `extraData` mapping(uint256 => uint256) private _packedOwnerships; // Mapping owner address to address data. // // Bits Layout: // - [0..63] `balance` // - [64..127] `numberMinted` // - [128..191] `numberBurned` // - [192..255] `aux` mapping(address => uint256) private _packedAddressData; // Mapping from token ID to approved address. mapping(uint256 => TokenApprovalRef) private _tokenApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; // ============================================================= // CONSTRUCTOR // ============================================================= constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; _currentIndex = _startTokenId(); } // ============================================================= // TOKEN COUNTING OPERATIONS // ============================================================= /** * @dev Returns the starting token ID. * To change the starting token ID, please override this function. */ function _startTokenId() internal view virtual returns (uint256) { return 0; } /** * @dev Returns the next token ID to be minted. */ function _nextTokenId() internal view virtual returns (uint256) { return _currentIndex; } /** * @dev Returns the total number of tokens in existence. * Burned tokens will reduce the count. * To get the total number of tokens minted, please see {_totalMinted}. */ function totalSupply() public view virtual override returns (uint256) { // Counter underflow is impossible as _burnCounter cannot be incremented // more than `_currentIndex - _startTokenId()` times. unchecked { return _currentIndex - _burnCounter - _startTokenId(); } } /** * @dev Returns the total amount of tokens minted in the contract. */ function _totalMinted() internal view virtual returns (uint256) { // Counter underflow is impossible as `_currentIndex` does not decrement, // and it is initialized to `_startTokenId()`. unchecked { return _currentIndex - _startTokenId(); } } /** * @dev Returns the total number of tokens burned. */ function _totalBurned() internal view virtual returns (uint256) { return _burnCounter; } // ============================================================= // ADDRESS DATA OPERATIONS // ============================================================= /** * @dev Returns the number of tokens in `owner`'s account. */ function balanceOf(address owner) public view virtual override returns (uint256) { if (owner == address(0)) revert BalanceQueryForZeroAddress(); return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY; } /** * Returns the number of tokens minted by `owner`. */ function _numberMinted(address owner) internal view returns (uint256) { return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY; } /** * Returns the number of tokens burned by or on behalf of `owner`. */ function _numberBurned(address owner) internal view returns (uint256) { return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY; } /** * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used). */ function _getAux(address owner) internal view returns (uint64) { return uint64(_packedAddressData[owner] >> _BITPOS_AUX); } /** * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used). * If there are multiple variables, please pack them into a uint64. */ function _setAux(address owner, uint64 aux) internal virtual { uint256 packed = _packedAddressData[owner]; uint256 auxCasted; // Cast `aux` with assembly to avoid redundant masking. assembly { auxCasted := aux } packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX); _packedAddressData[owner] = packed; } // ============================================================= // IERC165 // ============================================================= /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) * to learn more about how these ids are created. * * This function call must use less than 30000 gas. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { // The interface IDs are constants representing the first 4 bytes // of the XOR of all function selectors in the interface. // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165) // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`) return interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721. interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata. } // ============================================================= // IERC721Metadata // ============================================================= /** * @dev Returns the token collection name. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the token collection symbol. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); string memory baseURI = _baseURI(); return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : ''; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, it can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ''; } // ============================================================= // OWNERSHIPS OPERATIONS // ============================================================= /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { return address(uint160(_packedOwnershipOf(tokenId))); } /** * @dev Gas spent here starts off proportional to the maximum mint batch size. * It gradually moves to O(1) as tokens get transferred around over time. */ function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) { return _unpackedOwnership(_packedOwnershipOf(tokenId)); } /** * @dev Returns the unpacked `TokenOwnership` struct at `index`. */ function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) { return _unpackedOwnership(_packedOwnerships[index]); } /** * @dev Initializes the ownership slot minted at `index` for efficiency purposes. */ function _initializeOwnershipAt(uint256 index) internal virtual { if (_packedOwnerships[index] == 0) { _packedOwnerships[index] = _packedOwnershipOf(index); } } /** * Returns the packed ownership data of `tokenId`. */ function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) { uint256 curr = tokenId; unchecked { if (_startTokenId() <= curr) if (curr < _currentIndex) { uint256 packed = _packedOwnerships[curr]; // If not burned. if (packed & _BITMASK_BURNED == 0) { // Invariant: // There will always be an initialized ownership slot // (i.e. `ownership.addr != address(0) && ownership.burned == false`) // before an unintialized ownership slot // (i.e. `ownership.addr == address(0) && ownership.burned == false`) // Hence, `curr` will not underflow. // // We can directly compare the packed value. // If the address is zero, packed will be zero. while (packed == 0) { packed = _packedOwnerships[--curr]; } return packed; } } } revert OwnerQueryForNonexistentToken(); } /** * @dev Returns the unpacked `TokenOwnership` struct from `packed`. */ function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) { ownership.addr = address(uint160(packed)); ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP); ownership.burned = packed & _BITMASK_BURNED != 0; ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA); } /** * @dev Packs ownership data into a single uint256. */ function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) { assembly { // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean. owner := and(owner, _BITMASK_ADDRESS) // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`. result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags)) } } /** * @dev Returns the `nextInitialized` flag set if `quantity` equals 1. */ function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) { // For branchless setting of the `nextInitialized` flag. assembly { // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`. result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1)) } } // ============================================================= // APPROVAL OPERATIONS // ============================================================= /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the * zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) public payable virtual override { address owner = ownerOf(tokenId); if (_msgSenderERC721A() != owner) if (!isApprovedForAll(owner, _msgSenderERC721A())) { revert ApprovalCallerNotOwnerNorApproved(); } _tokenApprovals[tokenId].value = to; emit Approval(owner, to, tokenId); } /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) public view virtual override returns (address) { if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken(); return _tokenApprovals[tokenId].value; } /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} * for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) public virtual override { _operatorApprovals[_msgSenderERC721A()][operator] = approved; emit ApprovalForAll(_msgSenderERC721A(), operator, approved); } /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted. See {_mint}. */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _startTokenId() <= tokenId && tokenId < _currentIndex && // If within bounds, _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned. } /** * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`. */ function _isSenderApprovedOrOwner( address approvedAddress, address owner, address msgSender ) private pure returns (bool result) { assembly { // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean. owner := and(owner, _BITMASK_ADDRESS) // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean. msgSender := and(msgSender, _BITMASK_ADDRESS) // `msgSender == owner || msgSender == approvedAddress`. result := or(eq(msgSender, owner), eq(msgSender, approvedAddress)) } } /** * @dev Returns the storage slot and value for the approved address of `tokenId`. */ function _getApprovedSlotAndAddress(uint256 tokenId) private view returns (uint256 approvedAddressSlot, address approvedAddress) { TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId]; // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`. assembly { approvedAddressSlot := tokenApproval.slot approvedAddress := sload(approvedAddressSlot) } } // ============================================================= // TRANSFER OPERATIONS // ============================================================= /** * @dev Transfers `tokenId` from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token * by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) public payable virtual override { uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId); if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner(); (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId); // The nested ifs save around 20+ gas over a compound boolean condition. if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A())) if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved(); if (to == address(0)) revert TransferToZeroAddress(); _beforeTokenTransfers(from, to, tokenId, 1); // Clear approvals from the previous owner. assembly { if approvedAddress { // This is equivalent to `delete _tokenApprovals[tokenId]`. sstore(approvedAddressSlot, 0) } } // Underflow of the sender's balance is impossible because we check for // ownership above and the recipient's balance can't realistically overflow. // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256. unchecked { // We can directly increment and decrement the balances. --_packedAddressData[from]; // Updates: `balance -= 1`. ++_packedAddressData[to]; // Updates: `balance += 1`. // Updates: // - `address` to the next owner. // - `startTimestamp` to the timestamp of transfering. // - `burned` to `false`. // - `nextInitialized` to `true`. _packedOwnerships[tokenId] = _packOwnershipData( to, _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked) ); // If the next slot may not have been initialized (i.e. `nextInitialized == false`) . if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) { uint256 nextTokenId = tokenId + 1; // If the next slot's address is zero and not burned (i.e. packed value is zero). if (_packedOwnerships[nextTokenId] == 0) { // If the next slot is within bounds. if (nextTokenId != _currentIndex) { // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`. _packedOwnerships[nextTokenId] = prevOwnershipPacked; } } } } emit Transfer(from, to, tokenId); _afterTokenTransfers(from, to, tokenId, 1); } /** * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`. */ function safeTransferFrom( address from, address to, uint256 tokenId ) public payable virtual override { safeTransferFrom(from, to, tokenId, ''); } /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token * by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory _data ) public payable virtual override { transferFrom(from, to, tokenId); if (to.code.length != 0) if (!_checkContractOnERC721Received(from, to, tokenId, _data)) { revert TransferToNonERC721ReceiverImplementer(); } } /** * @dev Hook that is called before a set of serially-ordered token IDs * are about to be transferred. This includes minting. * And also called before burning one token. * * `startTokenId` - the first token ID to be transferred. * `quantity` - the amount to be transferred. * * Calling conditions: * * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ function _beforeTokenTransfers( address from, address to, uint256 startTokenId, uint256 quantity ) internal virtual {} /** * @dev Hook that is called after a set of serially-ordered token IDs * have been transferred. This includes minting. * And also called after one token has been burned. * * `startTokenId` - the first token ID to be transferred. * `quantity` - the amount to be transferred. * * Calling conditions: * * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been * transferred to `to`. * - When `from` is zero, `tokenId` has been minted for `to`. * - When `to` is zero, `tokenId` has been burned by `from`. * - `from` and `to` are never both zero. */ function _afterTokenTransfers( address from, address to, uint256 startTokenId, uint256 quantity ) internal virtual {} /** * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract. * * `from` - Previous owner of the given token ID. * `to` - Target address that will receive the token. * `tokenId` - Token ID to be transferred. * `_data` - Optional data to send along with the call. * * Returns whether the call correctly returned the expected magic value. */ function _checkContractOnERC721Received( address from, address to, uint256 tokenId, bytes memory _data ) private returns (bool) { try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns ( bytes4 retval ) { return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert TransferToNonERC721ReceiverImplementer(); } else { assembly { revert(add(32, reason), mload(reason)) } } } } // ============================================================= // MINT OPERATIONS // ============================================================= /** * @dev Mints `quantity` tokens and transfers them to `to`. * * Requirements: * * - `to` cannot be the zero address. * - `quantity` must be greater than 0. * * Emits a {Transfer} event for each mint. */ function _mint(address to, uint256 quantity) internal virtual { uint256 startTokenId = _currentIndex; if (quantity == 0) revert MintZeroQuantity(); _beforeTokenTransfers(address(0), to, startTokenId, quantity); // Overflows are incredibly unrealistic. // `balance` and `numberMinted` have a maximum limit of 2**64. // `tokenId` has a maximum limit of 2**256. unchecked { // Updates: // - `balance += quantity`. // - `numberMinted += quantity`. // // We can directly add to the `balance` and `numberMinted`. _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1); // Updates: // - `address` to the owner. // - `startTimestamp` to the timestamp of minting. // - `burned` to `false`. // - `nextInitialized` to `quantity == 1`. _packedOwnerships[startTokenId] = _packOwnershipData( to, _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0) ); uint256 toMasked; uint256 end = startTokenId + quantity; // Use assembly to loop and emit the `Transfer` event for gas savings. // The duplicated `log4` removes an extra check and reduces stack juggling. // The assembly, together with the surrounding Solidity code, have been // delicately arranged to nudge the compiler into producing optimized opcodes. assembly { // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean. toMasked := and(to, _BITMASK_ADDRESS) // Emit the `Transfer` event. log4( 0, // Start of data (0, since no data). 0, // End of data (0, since no data). _TRANSFER_EVENT_SIGNATURE, // Signature. 0, // `address(0)`. toMasked, // `to`. startTokenId // `tokenId`. ) // The `iszero(eq(,))` check ensures that large values of `quantity` // that overflows uint256 will make the loop run out of gas. // The compiler will optimize the `iszero` away for performance. for { let tokenId := add(startTokenId, 1) } iszero(eq(tokenId, end)) { tokenId := add(tokenId, 1) } { // Emit the `Transfer` event. Similar to above. log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId) } } if (toMasked == 0) revert MintToZeroAddress(); _currentIndex = end; } _afterTokenTransfers(address(0), to, startTokenId, quantity); } /** * @dev Mints `quantity` tokens and transfers them to `to`. * * This function is intended for efficient minting only during contract creation. * * It emits only one {ConsecutiveTransfer} as defined in * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309), * instead of a sequence of {Transfer} event(s). * * Calling this function outside of contract creation WILL make your contract * non-compliant with the ERC721 standard. * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309 * {ConsecutiveTransfer} event is only permissible during contract creation. * * Requirements: * * - `to` cannot be the zero address. * - `quantity` must be greater than 0. * * Emits a {ConsecutiveTransfer} event. */ function _mintERC2309(address to, uint256 quantity) internal virtual { uint256 startTokenId = _currentIndex; if (to == address(0)) revert MintToZeroAddress(); if (quantity == 0) revert MintZeroQuantity(); if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit(); _beforeTokenTransfers(address(0), to, startTokenId, quantity); // Overflows are unrealistic due to the above check for `quantity` to be below the limit. unchecked { // Updates: // - `balance += quantity`. // - `numberMinted += quantity`. // // We can directly add to the `balance` and `numberMinted`. _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1); // Updates: // - `address` to the owner. // - `startTimestamp` to the timestamp of minting. // - `burned` to `false`. // - `nextInitialized` to `quantity == 1`. _packedOwnerships[startTokenId] = _packOwnershipData( to, _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0) ); emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to); _currentIndex = startTokenId + quantity; } _afterTokenTransfers(address(0), to, startTokenId, quantity); } /** * @dev Safely mints `quantity` tokens and transfers them to `to`. * * Requirements: * * - If `to` refers to a smart contract, it must implement * {IERC721Receiver-onERC721Received}, which is called for each safe transfer. * - `quantity` must be greater than 0. * * See {_mint}. * * Emits a {Transfer} event for each mint. */ function _safeMint( address to, uint256 quantity, bytes memory _data ) internal virtual { _mint(to, quantity); unchecked { if (to.code.length != 0) { uint256 end = _currentIndex; uint256 index = end - quantity; do { if (!_checkContractOnERC721Received(address(0), to, index++, _data)) { revert TransferToNonERC721ReceiverImplementer(); } } while (index < end); // Reentrancy protection. if (_currentIndex != end) revert(); } } } /** * @dev Equivalent to `_safeMint(to, quantity, '')`. */ function _safeMint(address to, uint256 quantity) internal virtual { _safeMint(to, quantity, ''); } // ============================================================= // BURN OPERATIONS // ============================================================= /** * @dev Equivalent to `_burn(tokenId, false)`. */ function _burn(uint256 tokenId) internal virtual { _burn(tokenId, false); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId, bool approvalCheck) internal virtual { uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId); address from = address(uint160(prevOwnershipPacked)); (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId); if (approvalCheck) { // The nested ifs save around 20+ gas over a compound boolean condition. if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A())) if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved(); } _beforeTokenTransfers(from, address(0), tokenId, 1); // Clear approvals from the previous owner. assembly { if approvedAddress { // This is equivalent to `delete _tokenApprovals[tokenId]`. sstore(approvedAddressSlot, 0) } } // Underflow of the sender's balance is impossible because we check for // ownership above and the recipient's balance can't realistically overflow. // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256. unchecked { // Updates: // - `balance -= 1`. // - `numberBurned += 1`. // // We can directly decrement the balance, and increment the number burned. // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`. _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1; // Updates: // - `address` to the last owner. // - `startTimestamp` to the timestamp of burning. // - `burned` to `true`. // - `nextInitialized` to `true`. _packedOwnerships[tokenId] = _packOwnershipData( from, (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked) ); // If the next slot may not have been initialized (i.e. `nextInitialized == false`) . if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) { uint256 nextTokenId = tokenId + 1; // If the next slot's address is zero and not burned (i.e. packed value is zero). if (_packedOwnerships[nextTokenId] == 0) { // If the next slot is within bounds. if (nextTokenId != _currentIndex) { // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`. _packedOwnerships[nextTokenId] = prevOwnershipPacked; } } } } emit Transfer(from, address(0), tokenId); _afterTokenTransfers(from, address(0), tokenId, 1); // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times. unchecked { _burnCounter++; } } // ============================================================= // EXTRA DATA OPERATIONS // ============================================================= /** * @dev Directly sets the extra data for the ownership data `index`. */ function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual { uint256 packed = _packedOwnerships[index]; if (packed == 0) revert OwnershipNotInitializedForExtraData(); uint256 extraDataCasted; // Cast `extraData` with assembly to avoid redundant masking. assembly { extraDataCasted := extraData } packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA); _packedOwnerships[index] = packed; } /** * @dev Called during each token transfer to set the 24bit `extraData` field. * Intended to be overridden by the cosumer contract. * * `previousExtraData` - the value of `extraData` before transfer. * * Calling conditions: * * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ function _extraData( address from, address to, uint24 previousExtraData ) internal view virtual returns (uint24) {} /** * @dev Returns the next extra data for the packed ownership data. * The returned result is shifted into position. */ function _nextExtraData( address from, address to, uint256 prevOwnershipPacked ) private view returns (uint256) { uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA); return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA; } // ============================================================= // OTHER OPERATIONS // ============================================================= /** * @dev Returns the message sender (defaults to `msg.sender`). * * If you are writing GSN compatible contracts, you need to override this function. */ function _msgSenderERC721A() internal view virtual returns (address) { return msg.sender; } /** * @dev Converts a uint256 to its ASCII string decimal representation. */ function _toString(uint256 value) internal pure virtual returns (string memory str) { 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. Total: 5 * 0x20 = 0xa0. let m := add(mload(0x40), 0xa0) // Update the free memory pointer to allocate. mstore(0x40, m) // Assign the `str` to the end. str := sub(m, 0x20) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. // prettier-ignore for { let temp := value } 1 {} { str := 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) // prettier-ignore 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) } } }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import '../IERC721A.sol'; /** * @dev Interface of ERC721AQueryable. */ interface IERC721AQueryable is IERC721A { /** * Invalid query range (`start` >= `stop`). */ error InvalidQueryRange(); /** * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. * * If the `tokenId` is out of bounds: * * - `addr = address(0)` * - `startTimestamp = 0` * - `burned = false` * - `extraData = 0` * * If the `tokenId` is burned: * * - `addr = <Address of owner before token was burned>` * - `startTimestamp = <Timestamp when token was burned>` * - `burned = true` * - `extraData = <Extra data when token was burned>` * * Otherwise: * * - `addr = <Address of owner>` * - `startTimestamp = <Timestamp of start of ownership>` * - `burned = false` * - `extraData = <Extra data at start of ownership>` */ function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory); /** * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order. * See {ERC721AQueryable-explicitOwnershipOf} */ function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory); /** * @dev Returns an array of token IDs owned by `owner`, * in the range [`start`, `stop`) * (i.e. `start <= tokenId < stop`). * * This function allows for tokens to be queried if the collection * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. * * Requirements: * * - `start < stop` */ function tokensOfOwnerIn( address owner, uint256 start, uint256 stop ) external view returns (uint256[] memory); /** * @dev Returns an array of token IDs owned by `owner`. * * This function scans the ownership mapping and is O(`totalSupply`) in complexity. * It is meant to be called off-chain. * * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into * multiple smaller scans if the collection is large enough to cause * an out-of-gas error (10K collections should be fine). */ function tokensOfOwner(address owner) external view returns (uint256[] memory); }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; import '../IERC721A.sol'; /** * @dev Interface of ERC721ABurnable. */ interface IERC721ABurnable is IERC721A { /** * @dev Burns `tokenId`. See {ERC721A-_burn}. * * Requirements: * * - The caller must own `tokenId` or be an approved operator. */ function burn(uint256 tokenId) external; }
// SPDX-License-Identifier: MIT // ERC721A Contracts v4.2.3 // Creator: Chiru Labs pragma solidity ^0.8.4; /** * @dev Interface of ERC721A. */ interface IERC721A { /** * The caller must own the token or be an approved operator. */ error ApprovalCallerNotOwnerNorApproved(); /** * The token does not exist. */ error ApprovalQueryForNonexistentToken(); /** * Cannot query the balance for the zero address. */ error BalanceQueryForZeroAddress(); /** * Cannot mint to the zero address. */ error MintToZeroAddress(); /** * The quantity of tokens minted must be more than zero. */ error MintZeroQuantity(); /** * The token does not exist. */ error OwnerQueryForNonexistentToken(); /** * The caller must own the token or be an approved operator. */ error TransferCallerNotOwnerNorApproved(); /** * The token must be owned by `from`. */ error TransferFromIncorrectOwner(); /** * Cannot safely transfer to a contract that does not implement the * ERC721Receiver interface. */ error TransferToNonERC721ReceiverImplementer(); /** * Cannot transfer to the zero address. */ error TransferToZeroAddress(); /** * The token does not exist. */ error URIQueryForNonexistentToken(); /** * The `quantity` minted with ERC2309 exceeds the safety limit. */ error MintERC2309QuantityExceedsLimit(); /** * The `extraData` cannot be set on an unintialized ownership slot. */ error OwnershipNotInitializedForExtraData(); // ============================================================= // STRUCTS // ============================================================= struct TokenOwnership { // The address of the owner. address addr; // Stores the start time of ownership with minimal overhead for tokenomics. uint64 startTimestamp; // Whether the token has been burned. bool burned; // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}. uint24 extraData; } // ============================================================= // TOKEN COUNTERS // ============================================================= /** * @dev Returns the total number of tokens in existence. * Burned tokens will reduce the count. * To get the total number of tokens minted, please see {_totalMinted}. */ function totalSupply() external view returns (uint256); // ============================================================= // IERC165 // ============================================================= /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) * to learn more about how these ids are created. * * This function call must use less than 30000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); // ============================================================= // IERC721 // ============================================================= /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables * (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in `owner`'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, * checking first that contract recipients are aware of the ERC721 protocol * to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move * this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external payable; /** * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external payable; /** * @dev Transfers `tokenId` from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} * whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token * by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external payable; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the * zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external payable; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} * for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll}. */ function isApprovedForAll(address owner, address operator) external view returns (bool); // ============================================================= // IERC721Metadata // ============================================================= /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); // ============================================================= // IERC2309 // ============================================================= /** * @dev Emitted when tokens in `fromTokenId` to `toTokenId` * (inclusive) is transferred from `from` to `to`, as defined in the * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard. * * See {_mintERC2309} for more details. */ event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Base64.sol) pragma solidity ^0.8.20; /** * @dev Provides a set of functions to operate with Base64 strings. */ library Base64 { /** * @dev Base64 Encoding/Decoding Table */ string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * @dev Converts a `bytes` to its Bytes64 `string` representation. */ function encode(bytes memory data) internal pure returns (string memory) { /** * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol */ if (data.length == 0) return ""; // Loads the table into memory string memory table = _TABLE; // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter // and split into 4 numbers of 6 bits. // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up // - `data.length + 2` -> Round up // - `/ 3` -> Number of 3-bytes chunks // - `4 *` -> 4 characters for each chunk string memory result = new string(4 * ((data.length + 2) / 3)); /// @solidity memory-safe-assembly assembly { // Prepare the lookup table (skip the first "length" byte) let tablePtr := add(table, 1) // Prepare result pointer, jump over length let resultPtr := add(result, 32) // Run over the input, 3 bytes at a time for { let dataPtr := data let endPtr := add(data, mload(data)) } lt(dataPtr, endPtr) { } { // Advance 3 bytes dataPtr := add(dataPtr, 3) let input := mload(dataPtr) // To write each character, shift the 3 bytes (18 bits) chunk // 4 times in blocks of 6 bits for each character (18, 12, 6, 0) // and apply logical AND with 0x3F which is the number of // the previous character in the ASCII table prior to the Base64 Table // The result is then added to the table to get the character to write, // and finally write it in the result pointer but with a left shift // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F)))) resultPtr := add(resultPtr, 1) // Advance } // When data `bytes` is not exactly 3 bytes long // it is padded with `=` characters at the end switch mod(mload(data), 3) case 1 { mstore8(sub(resultPtr, 1), 0x3d) mstore8(sub(resultPtr, 2), 0x3d) } case 2 { mstore8(sub(resultPtr, 1), 0x3d) } } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
{ "optimizer": { "enabled": false, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"ClaimNotAvailable","type":"error"},{"inputs":[],"name":"InvalidLabel","type":"error"},{"inputs":[],"name":"InvalidQuantity","type":"error"},{"inputs":[],"name":"InvalidQueryRange","type":"error"},{"inputs":[],"name":"InvalidSpectrum","type":"error"},{"inputs":[],"name":"InvalidText","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"MetaTooLong","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NotAllTokensOwned","type":"error"},{"inputs":[],"name":"NotAllowed","type":"error"},{"inputs":[],"name":"NotEnoughEth","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ZeroBalance","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","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":"approved","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":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","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":"previousOwner","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":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address[]","name":"wallets","type":"address[]"}],"name":"addAllowlistWallets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowListWallets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkMintProgression","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"abstractGlitchId","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimedAbstractGlitchTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"distributeSuperRares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"components":[{"internalType":"bool","name":"stroke","type":"bool"},{"internalType":"bool","name":"vertical","type":"bool"},{"internalType":"bool","name":"whiteText","type":"bool"},{"internalType":"string","name":"labelText","type":"string"},{"internalType":"uint256","name":"spectrumIndex","type":"uint256"},{"internalType":"uint256","name":"labelIndex","type":"uint256"},{"internalType":"uint256","name":"textSize","type":"uint256"}],"internalType":"struct Structs.ArtRules","name":"artRules","type":"tuple"},{"internalType":"string","name":"meta","type":"string"}],"name":"edit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"explicitOwnershipOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"explicitOwnershipsOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintCloseTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","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":"tokenId","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":"approved","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":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokens","outputs":[{"internalType":"bool","name":"edited","type":"bool"},{"internalType":"uint256","name":"originalArtIndex","type":"uint256"},{"internalType":"uint256","name":"currentArtIndex","type":"uint256"},{"internalType":"string","name":"meta","type":"string"},{"components":[{"internalType":"bool","name":"stroke","type":"bool"},{"internalType":"bool","name":"vertical","type":"bool"},{"internalType":"bool","name":"whiteText","type":"bool"},{"internalType":"string","name":"labelText","type":"string"},{"internalType":"uint256","name":"spectrumIndex","type":"uint256"},{"internalType":"uint256","name":"labelIndex","type":"uint256"},{"internalType":"uint256","name":"textSize","type":"uint256"}],"internalType":"struct Structs.ArtRules","name":"artRules","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"stop","type":"uint256"}],"name":"tokensOfOwnerIn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBurned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEdits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040525f600960146101000a81548160ff021916908315150217905550640cef5e80e3600a555f600b5534801562000037575f80fd5b50336040518060400160405280600a81526020017f4d657461646174652f61000000000000000000000000000000000000000000008152506040518060400160405280600281526020017f4d440000000000000000000000000000000000000000000000000000000000008152508160029081620000b6919062000826565b508060039081620000c8919062000826565b50620000d9620001f960201b60201c565b5f8190555050505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160362000153575f6040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016200014a91906200094d565b60405180910390fd5b62000164816200020160201b60201c565b506040516200017390620005b4565b604051809103905ff0801580156200018d573d5f803e3d5ffd5b5060095f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550620001e0336001620002c460201b60201c565b620001f360015f6200049960201b60201c565b62000968565b5f6001905090565b5f60085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b5f805490505f820362000303576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b620003175f848385620004cf60201b60201c565b600160406001901b17820260055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282540192505081905550620003a183620003835f865f6200053860201b60201c565b62000394856200056760201b60201c565b176200057660201b60201c565b60045f8381526020019081526020015f20819055505f80838301905073ffffffffffffffffffffffffffffffffffffffff8516915082825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4600183015b8181146200043d5780835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a460018101905062000402565b505f820362000478576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f819055505050620004945f848385620005a060201b60201c565b505050565b80600c5f8481526020019081526020015f206001018190555080600c5f8481526020019081526020015f20600201819055505050565b620004e384848484620005a660201b60201c565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160362000532576007600c5f8481526020019081526020015f20600201819055505b50505050565b5f8060e883901c905060e862000556868684620005ac60201b60201c565b62ffffff16901b9150509392505050565b5f6001821460e11b9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff83169250814260a01b178317905092915050565b50505050565b50505050565b5f9392505050565b61680b80620057f183390190565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200063e57607f821691505b602082108103620006545762000653620005f9565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620006b87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826200067b565b620006c486836200067b565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f6200070e620007086200070284620006dc565b620006e5565b620006dc565b9050919050565b5f819050919050565b6200072983620006ee565b62000741620007388262000715565b84845462000687565b825550505050565b5f90565b6200075762000749565b620007648184846200071e565b505050565b5b818110156200078b576200077f5f826200074d565b6001810190506200076a565b5050565b601f821115620007da57620007a4816200065a565b620007af846200066c565b81016020851015620007bf578190505b620007d7620007ce856200066c565b83018262000769565b50505b505050565b5f82821c905092915050565b5f620007fc5f1984600802620007df565b1980831691505092915050565b5f620008168383620007eb565b9150826002028217905092915050565b6200083182620005c2565b67ffffffffffffffff8111156200084d576200084c620005cc565b5b62000859825462000626565b620008668282856200078f565b5f60209050601f8311600181146200089c575f841562000887578287015190505b62000893858262000809565b86555062000902565b601f198416620008ac866200065a565b5f5b82811015620008d557848901518255600182019150602085019450602081019050620008ae565b86831015620008f55784890151620008f1601f891682620007eb565b8355505b6001600288020188555050505b505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f62000935826200090a565b9050919050565b620009478162000929565b82525050565b5f602082019050620009625f8301846200093c565b92915050565b614e7b80620009765f395ff3fe60806040526004361061020e575f3560e01c80638462151c11610117578063bd296d231161009f578063d89135cd1161006e578063d89135cd1461078a578063d9e54e24146107b4578063dac2e804146107de578063e985e9c514610808578063f2fde38b146108445761020e565b8063bd296d23146106c2578063c23dc68f146106ea578063c87b56dd14610726578063d1f7a9c5146107625761020e565b806399a2557a116100e657806399a2557a146105ea578063a0712d6814610626578063a22cb46514610642578063b0dc78fa1461066a578063b88d4fde146106a65761020e565b80638462151c146105305780638a0275fe1461056c5780638da5cb5b1461059657806395d89b41146105c05761020e565b806342842e0e1161019a5780635bbb2177116101695780635bbb21771461042a5780636352211e146104665780636511cc77146104a257806370a08231146104de578063715018a61461051a5761020e565b806342842e0e1461036a57806342966c68146103865780634f64b2be146103ae5780635a72bfa9146103ee5761020e565b806318160ddd116101e157806318160ddd146102d057806323b872dd146102fa578063379607f5146103165780633ccfd60b1461033e5780634063151d146103545761020e565b806301ffc9a71461021257806306fdde031461024e578063081812fc14610278578063095ea7b3146102b4575b5f80fd5b34801561021d575f80fd5b5061023860048036038101906102339190613732565b61086c565b6040516102459190613777565b60405180910390f35b348015610259575f80fd5b506102626108fd565b60405161026f919061381a565b60405180910390f35b348015610283575f80fd5b5061029e6004803603810190610299919061386d565b61098d565b6040516102ab91906138d7565b60405180910390f35b6102ce60048036038101906102c9919061391a565b610a07565b005b3480156102db575f80fd5b506102e4610b46565b6040516102f19190613967565b60405180910390f35b610314600480360381019061030f9190613980565b610b5b565b005b348015610321575f80fd5b5061033c6004803603810190610337919061386d565b610e69565b005b348015610349575f80fd5b50610352610fe1565b005b34801561035f575f80fd5b506103686110ea565b005b610384600480360381019061037f9190613980565b611261565b005b348015610391575f80fd5b506103ac60048036038101906103a7919061386d565b611280565b005b3480156103b9575f80fd5b506103d460048036038101906103cf919061386d565b61128e565b6040516103e5959493929190613acf565b60405180910390f35b3480156103f9575f80fd5b50610414600480360381019061040f919061386d565b611457565b6040516104219190613777565b60405180910390f35b348015610435575f80fd5b50610450600480360381019061044b9190613b8f565b611474565b60405161045d9190613d23565b60405180910390f35b348015610471575f80fd5b5061048c6004803603810190610487919061386d565b611534565b60405161049991906138d7565b60405180910390f35b3480156104ad575f80fd5b506104c860048036038101906104c39190613d43565b611545565b6040516104d59190613777565b60405180910390f35b3480156104e9575f80fd5b5061050460048036038101906104ff9190613d43565b611562565b6040516105119190613967565b60405180910390f35b348015610525575f80fd5b5061052e611617565b005b34801561053b575f80fd5b5061055660048036038101906105519190613d43565b61162a565b6040516105639190613e16565b60405180910390f35b348015610577575f80fd5b50610580611766565b60405161058d9190613967565b60405180910390f35b3480156105a1575f80fd5b506105aa61176c565b6040516105b791906138d7565b60405180910390f35b3480156105cb575f80fd5b506105d4611794565b6040516105e1919061381a565b60405180910390f35b3480156105f5575f80fd5b50610610600480360381019061060b9190613e36565b611824565b60405161061d9190613e16565b60405180910390f35b610640600480360381019061063b919061386d565b611a23565b005b34801561064d575f80fd5b5061066860048036038101906106639190613eb0565b611bf8565b005b348015610675575f80fd5b50610690600480360381019061068b919061386d565b611cfe565b60405161069d919061381a565b60405180910390f35b6106c060048036038101906106bb9190614016565b611db3565b005b3480156106cd575f80fd5b506106e860048036038101906106e39190614209565b611e25565b005b3480156106f5575f80fd5b50610710600480360381019061070b919061386d565b6122d9565b60405161071d9190614305565b60405180910390f35b348015610731575f80fd5b5061074c6004803603810190610747919061386d565b612343565b604051610759919061381a565b60405180910390f35b34801561076d575f80fd5b5061078860048036038101906107839190614373565b6123fa565b005b348015610795575f80fd5b5061079e6124de565b6040516107ab9190613967565b60405180910390f35b3480156107bf575f80fd5b506107c86124ec565b6040516107d59190613967565b60405180910390f35b3480156107e9575f80fd5b506107f2612560565b6040516107ff9190613967565b60405180910390f35b348015610813575f80fd5b5061082e600480360381019061082991906143be565b612566565b60405161083b9190613777565b60405180910390f35b34801561084f575f80fd5b5061086a60048036038101906108659190613d43565b6125f4565b005b5f6301ffc9a760e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806108c657506380ac58cd60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806108f65750635b5e139f60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b60606002805461090c90614429565b80601f016020809104026020016040519081016040528092919081815260200182805461093890614429565b80156109835780601f1061095a57610100808354040283529160200191610983565b820191905f5260205f20905b81548152906001019060200180831161096657829003601f168201915b5050505050905090565b5f61099782612678565b6109cd576040517fcf4700e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60065f8381526020019081526020015f205f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b5f610a1182611534565b90508073ffffffffffffffffffffffffffffffffffffffff16610a326126d2565b73ffffffffffffffffffffffffffffffffffffffff1614610a9557610a5e81610a596126d2565b612566565b610a94576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b8260065f8481526020019081526020015f205f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b5f610b4f6126d9565b6001545f540303905090565b5f610b65826126e1565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610bcc576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80610bd7846127a4565b91509150610bed8187610be86126d2565b6127c7565b610c3957610c0286610bfd6126d2565b612566565b610c38576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603610c9e576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cab868686600161280a565b8015610cb5575f82555b60055f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8154600190039190508190555060055f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f815460010191905081905550610d7d85610d5988888761286a565b7c020000000000000000000000000000000000000000000000000000000017612891565b60045f8681526020019081526020015f20819055505f7c0200000000000000000000000000000000000000000000000000000000841603610df9575f6001850190505f60045f8381526020019081526020015f205403610df7575f548114610df6578360045f8381526020019081526020015f20819055505b5b505b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4610e6186868660016128bb565b505050505050565b3373ffffffffffffffffffffffffffffffffffffffff1673f9f27aa7718dccf4b4306e0321c2ce85215fa90273ffffffffffffffffffffffffffffffffffffffff16636352211e836040518263ffffffff1660e01b8152600401610ecd9190613967565b602060405180830381865afa158015610ee8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f0c919061446d565b73ffffffffffffffffffffffffffffffffffffffff16148015610f4a5750600d5f8281526020019081526020015f205f9054906101000a900460ff16155b8015610f5c57505f610f5a6124ec565b115b8015610f6f57506004610f6d6124ec565b105b15610fac57610f7e60016128c1565b6001600d5f8381526020019081526020015f205f6101000a81548160ff021916908315150217905550610fde565b6040517f3c21f90f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b610fe96129c2565b5f4790505f8103611026576040517f669567ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60028261103491906144f2565b90505f81836110439190614522565b9050739390333197446ec19b1b4dc7dc7fc5ae4957ebba73ffffffffffffffffffffffffffffffffffffffff166108fc8390811502906040515f60405180830381858888f1935050505080156110dd57507338f0dbba41258639ba0b2cddc753e46157ce14d073ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050505b6110e5575f80fd5b505050565b6110f26129c2565b60046110fc6124ec565b10806111145750600960149054906101000a900460ff165b1561114b576040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b60c8811015611201575f6111846107e8836111689190614555565b60016103e86111779190614555565b61117f612a49565b612a5a565b9050600c5f8281526020019081526020015f205f015f9054906101000a900460ff161580156111c857506007600c5f8381526020019081526020015f206002015414155b156111f3576111da826008600a612a5a565b600c5f8381526020019081526020015f20600201819055505b50808060010191505061114d565b506001600960146101000a81548160ff0219169083151502179055507f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c6103e8611249612a49565b604051611257929190614588565b60405180910390a1565b61127b83838360405180602001604052805f815250611db3565b505050565b61128b816001612abf565b50565b600c602052805f5260405f205f91509050805f015f9054906101000a900460ff16908060010154908060020154908060030180546112cb90614429565b80601f01602080910402602001604051908101604052809291908181526020018280546112f790614429565b80156113425780601f1061131957610100808354040283529160200191611342565b820191905f5260205f20905b81548152906001019060200180831161132557829003601f168201915b505050505090806004016040518060e00160405290815f82015f9054906101000a900460ff161515151581526020015f820160019054906101000a900460ff161515151581526020015f820160029054906101000a900460ff161515151581526020016001820180546113b490614429565b80601f01602080910402602001604051908101604052809291908181526020018280546113e090614429565b801561142b5780601f106114025761010080835404028352916020019161142b565b820191905f5260205f20905b81548152906001019060200180831161140e57829003601f168201915b505050505081526020016002820154815260200160038201548152602001600482015481525050905085565b600d602052805f5260405f205f915054906101000a900460ff1681565b60605f8383905090505f8167ffffffffffffffff81111561149857611497613ef2565b5b6040519080825280602002602001820160405280156114d157816020015b6114be613681565b8152602001906001900390816114b65790505b5090505f5b828114611528576114ff8686838181106114f3576114f26145af565b5b905060200201356122d9565b828281518110611512576115116145af565b5b60200260200101819052508060010190506114d6565b50809250505092915050565b5f61153e826126e1565b9050919050565b600e602052805f5260405f205f915054906101000a900460ff1681565b5f8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036115c8576040517f8f4eb60400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff60055f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054169050919050565b61161f6129c2565b6116285f612cfb565b565b60605f805f61163885611562565b90505f8167ffffffffffffffff81111561165557611654613ef2565b5b6040519080825280602002602001820160405280156116835781602001602082028036833780820191505090505b50905061168e613681565b5f6116976126d9565b90505b838614611758576116aa81612dbe565b9150816040015161174d575f73ffffffffffffffffffffffffffffffffffffffff16825f015173ffffffffffffffffffffffffffffffffffffffff16146116f257815f015194505b8773ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff160361174c578083878060010198508151811061173f5761173e6145af565b5b6020026020010181815250505b5b80600101905061169a565b508195505050505050919050565b600b5481565b5f60085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600380546117a390614429565b80601f01602080910402602001604051908101604052809291908181526020018280546117cf90614429565b801561181a5780601f106117f15761010080835404028352916020019161181a565b820191905f5260205f20905b8154815290600101906020018083116117fd57829003601f168201915b5050505050905090565b606081831061185f576040517f32c1995a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80611869612de7565b90506118736126d9565b851015611885576118826126d9565b94505b80841115611891578093505b5f61189b87611562565b9050848610156118bd575f8686039050818110156118b7578091505b506118c1565b5f90505b5f8167ffffffffffffffff8111156118dc576118db613ef2565b5b60405190808252806020026020018201604052801561190a5781602001602082028036833780820191505090505b5090505f82036119205780945050505050611a1c565b5f61192a886122d9565b90505f816040015161193d57815f015190505b5f8990505b8881141580156119525750848714155b15611a0e5761196081612dbe565b92508260400151611a03575f73ffffffffffffffffffffffffffffffffffffffff16835f015173ffffffffffffffffffffffffffffffffffffffff16146119a857825f015191505b8a73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611a0257808488806001019950815181106119f5576119f46145af565b5b6020026020010181815250505b5b806001019050611942565b508583528296505050505050505b9392505050565b5f611a2c6124ec565b90506611c37937e0800082611a4191906145dc565b341015611a7a576040517ff14a42b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028103611bab57600e5f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1680611b6157505f73f9f27aa7718dccf4b4306e0321c2ce85215fa90273ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401611b2091906138d7565b602060405180830381865afa158015611b3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b5f9190614631565b115b15611b7457611b6f826128c1565b611ba6565b6040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bf4565b60038103611bc157611bbc826128c1565b611bf3565b6040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5050565b8060075f611c046126d2565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16611cad6126d2565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611cf29190613777565b60405180910390a35050565b606060095f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663489320a4600c5f8581526020019081526020015f206040518263ffffffff1660e01b8152600401611d6a91906148f4565b5f60405180830381865afa158015611d84573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190611dac9190614982565b9050919050565b611dbe848484610b5b565b5f8373ffffffffffffffffffffffffffffffffffffffff163b14611e1f57611de884848484612def565b611e1e576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b50505050565b5f84845f818110611e3957611e386145af565b5b905060200201359050611e4d858533612f3a565b611e83576040517f97792c3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185859050036120c1576007600c5f8381526020019081526020015f2060020154118015611ec65750600b600c5f8381526020019081526020015f2060020154105b1561201657611edd83606001518460c00151612fc2565b611f13576040517f7a2a029c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6064611f1e8361306b565b1115611f56576040517f69ed1ea500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260600151600c5f8381526020019081526020015f206004016001019081611f7e9190614b54565b508260c00151600c5f8381526020019081526020015f206004016004018190555081600c5f8381526020019081526020015f206003019081611fc09190614b54565b50600c5f8281526020019081526020015f205f015f9054906101000a900460ff16612011576001600c5f8381526020019081526020015f205f015f6101000a81548160ff0219169083151502179055505b612085565b6103e88111806120425750600c5f8281526020019081526020015f205f015f9054906101000a900460ff165b15612079576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120848184846130d5565b5b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7816040516120b49190613967565b60405180910390a16122bb565b6005858590500361228857600c5f8281526020019081526020015f205f015f9054906101000a900460ff1615612123576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b600581101561228257612164600c5f888885818110612147576121466145af565b5b9050602002013581526020019081526020015f2060020154613369565b1561219b576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818686838181106121af576121ae6145af565b5b9050602002013503612202576121c68285856130d5565b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7826040516121f59190613967565b60405180910390a1612275565b612224868683818110612218576122176145af565b5b90506020020135611280565b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7868683818110612258576122576145af565b5b9050602002013560405161226c9190613967565b60405180910390a15b8080600101915050612125565b506122ba565b6040517f524f409b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b600b5f8154809291906122cd90614c23565b91905055505050505050565b6122e1613681565b6122e9613681565b6122f16126d9565b8310806123055750612301612de7565b8310155b15612313578091505061233e565b61231c83612dbe565b9050806040015115612331578091505061233e565b61233a836133a1565b9150505b919050565b606060095f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a3915e6583600c5f8681526020019081526020015f206040518363ffffffff1660e01b81526004016123b1929190614c6a565b5f60405180830381865afa1580156123cb573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906123f39190614982565b9050919050565b6124026129c2565b5f61240b6124ec565b1115612443576040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b828290508110156124d9576001600e5f858585818110612468576124676145af565b5b905060200201602081019061247d9190613d43565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508080600101915050612445565b505050565b5f6124e76133c1565b905090565b5f6365ae74e0421015612501575f905061255d565b612a306365ae74e06125139190614555565b421015612523576001905061255d565b6162706365ae74e06125359190614555565b421015612545576002905061255d565b600a54421015612558576003905061255d565b600490505b90565b600a5481565b5f60075f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b6125fc6129c2565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361266c575f6040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161266391906138d7565b60405180910390fd5b61267581612cfb565b50565b5f816126826126d9565b1115801561269057505f5482105b80156126cb57505f7c010000000000000000000000000000000000000000000000000000000060045f8581526020019081526020015f205416145b9050919050565b5f33905090565b5f6001905090565b5f80829050806126ef6126d9565b1161276d575f5481101561276c575f60045f8381526020019081526020015f205490505f7c010000000000000000000000000000000000000000000000000000000082160361276a575b5f81036127605760045f836001900393508381526020019081526020015f20549050612739565b809250505061279f565b505b5b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b5f805f60065f8581526020019081526020015f2090508092508254915050915091565b5f73ffffffffffffffffffffffffffffffffffffffff8316925073ffffffffffffffffffffffffffffffffffffffff821691508382148383141790509392505050565b612816848484846133ca565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603612864576007600c5f8481526020019081526020015f20600201819055505b50505050565b5f8060e883901c905060e86128808686846133d0565b62ffffff16901b9150509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff83169250814260a01b178317905092915050565b50505050565b5f5b8181101561294c575f816128d5612de7565b6128df9190614555565b90506103e88111156129145761290f8161290742846128fe9190614555565b60016006612a5a565b60ff166133d8565b61293e565b6103e88103612933576203f4804261292c9190614555565b600a819055505b61293d815f6133d8565b5b5080806001019150506128c3565b50600a81116129645761295f338261340e565b6129bf565b5f5b600a8261297391906144f2565b8110156129925761298533600a61340e565b8080600101915050612966565b505f600a826129a19190614c98565b11156129be576129bd33600a836129b89190614c98565b61340e565b5b5b50565b6129ca6135b7565b73ffffffffffffffffffffffffffffffffffffffff166129e861176c565b73ffffffffffffffffffffffffffffffffffffffff1614612a4757612a0b6135b7565b6040517f118cdaa7000000000000000000000000000000000000000000000000000000008152600401612a3e91906138d7565b60405180910390fd5b565b5f612a526126d9565b5f5403905090565b5f8060018484612a6a9190614522565b612a749190614555565b90508085604051602001612a889190614ce8565b604051602081830303815290604052805190602001205f1c612aaa9190614c98565b84612ab59190614555565b9150509392505050565b5f612ac9836126e1565b90505f8190505f80612ada866127a4565b915091508415612b4357612af68184612af16126d2565b6127c7565b612b4257612b0b83612b066126d2565b612566565b612b41576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5b612b50835f88600161280a565b8015612b5a575f82555b600160806001901b0360055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282540192505081905550612bfe83612bbb855f8861286a565b7c02000000000000000000000000000000000000000000000000000000007c01000000000000000000000000000000000000000000000000000000001717612891565b60045f8881526020019081526020015f20819055505f7c0200000000000000000000000000000000000000000000000000000000851603612c7a575f6001870190505f60045f8381526020019081526020015f205403612c78575f548114612c77578460045f8381526020019081526020015f20819055505b5b505b855f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4612ce2835f8860016128bb565b60015f8154809291906001019190505550505050505050565b5f60085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b612dc6613681565b612de060045f8481526020019081526020015f20546135be565b9050919050565b5f8054905090565b5f8373ffffffffffffffffffffffffffffffffffffffff1663150b7a02612e146126d2565b8786866040518563ffffffff1660e01b8152600401612e369493929190614d54565b6020604051808303815f875af1925050508015612e7157506040513d601f19601f82011682018060405250810190612e6e9190614db2565b60015b612ee7573d805f8114612e9f576040519150601f19603f3d011682016040523d82523d5f602084013e612ea4565b606091505b505f815103612edf576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050949350505050565b5f805f90505b84849050811015612fb5578273ffffffffffffffffffffffffffffffffffffffff16612f84868684818110612f7857612f776145af565b5b90506020020135611534565b73ffffffffffffffffffffffffffffffffffffffff1614612fa8575f915050612fbb565b8080600101915050612f40565b50600190505b9392505050565b5f6006821080612fd25750601d82115b15612fdf575f9050613065565b5f612fe98461306b565b90505f60078211612ffd57601d905061305c565b6010821161300e57600b905061305b565b6016821161301f576009905061305a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161305190614e27565b60405180910390fd5b5b5b80841115925050505b92915050565b5f805f90505f5b83518110156130cb57608060c0858381518110613092576130916145af565b5b602001015160f81c60f81b60f81c1660ff16146130b85781806130b490614c23565b9250505b80806130c390614c23565b915050613072565b8192505050919050565b600f82608001511115613114576040517f02e14b0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60078260a001511115613153576040517f3d36cb8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61316582606001518360c00151612fc2565b61319b576040517f7a2a029c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60646131a68261306b565b11156131de576040517f69ed1ea500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600c5f8581526020019081526020015f2060030190816131ff9190614b54565b506040518060e00160405280835f01511515815260200183602001511515815260200183604001511515815260200183606001518152602001836080015181526020018360a0015181526020018360c00151815250600c5f8581526020019081526020015f206004015f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff02191690831515021790555060608201518160010190816132d89190614b54565b506080820151816002015560a0820151816003015560c08201518160040155905050600c5f8481526020019081526020015f205f015f9054906101000a900460ff16613364576001600c5f8581526020019081526020015f205f015f6101000a81548160ff021916908315150217905550600b600c5f8581526020019081526020015f20600201819055505b505050565b5f80820361337a576001905061339c565b60078211801561338a5750600b82105b15613398576001905061339c565b5f90505b919050565b6133a9613681565b6133ba6133b5836126e1565b6135be565b9050919050565b5f600154905090565b50505050565b5f9392505050565b80600c5f8481526020019081526020015f206001018190555080600c5f8481526020019081526020015f20600201819055505050565b5f805490505f820361344c576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134585f84838561280a565b600160406001901b17820260055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055506134ca836134bb5f865f61286a565b6134c485613672565b17612891565b60045f8381526020019081526020015f20819055505f80838301905073ffffffffffffffffffffffffffffffffffffffff8516915082825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4600183015b8181146135645780835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a460018101905061352b565b505f820361359e576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f8190555050506135b25f8483856128bb565b505050565b5f33905090565b6135c6613681565b81815f019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060a082901c816020019067ffffffffffffffff16908167ffffffffffffffff16815250505f7c01000000000000000000000000000000000000000000000000000000008316141581604001901515908115158152505060e882901c816060019062ffffff16908162ffffff1681525050919050565b5f6001821460e11b9050919050565b60405180608001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f67ffffffffffffffff1681526020015f151581526020015f62ffffff1681525090565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b613711816136dd565b811461371b575f80fd5b50565b5f8135905061372c81613708565b92915050565b5f60208284031215613747576137466136d5565b5b5f6137548482850161371e565b91505092915050565b5f8115159050919050565b6137718161375d565b82525050565b5f60208201905061378a5f830184613768565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156137c75780820151818401526020810190506137ac565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6137ec82613790565b6137f6818561379a565b93506138068185602086016137aa565b61380f816137d2565b840191505092915050565b5f6020820190508181035f83015261383281846137e2565b905092915050565b5f819050919050565b61384c8161383a565b8114613856575f80fd5b50565b5f8135905061386781613843565b92915050565b5f60208284031215613882576138816136d5565b5b5f61388f84828501613859565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6138c182613898565b9050919050565b6138d1816138b7565b82525050565b5f6020820190506138ea5f8301846138c8565b92915050565b6138f9816138b7565b8114613903575f80fd5b50565b5f81359050613914816138f0565b92915050565b5f80604083850312156139305761392f6136d5565b5b5f61393d85828601613906565b925050602061394e85828601613859565b9150509250929050565b6139618161383a565b82525050565b5f60208201905061397a5f830184613958565b92915050565b5f805f60608486031215613997576139966136d5565b5b5f6139a486828701613906565b93505060206139b586828701613906565b92505060406139c686828701613859565b9150509250925092565b6139d98161375d565b82525050565b5f82825260208201905092915050565b5f6139f982613790565b613a0381856139df565b9350613a138185602086016137aa565b613a1c816137d2565b840191505092915050565b613a308161383a565b82525050565b5f60e083015f830151613a4b5f8601826139d0565b506020830151613a5e60208601826139d0565b506040830151613a7160408601826139d0565b5060608301518482036060860152613a8982826139ef565b9150506080830151613a9e6080860182613a27565b5060a0830151613ab160a0860182613a27565b5060c0830151613ac460c0860182613a27565b508091505092915050565b5f60a082019050613ae25f830188613768565b613aef6020830187613958565b613afc6040830186613958565b8181036060830152613b0e81856137e2565b90508181036080830152613b228184613a36565b90509695505050505050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112613b4f57613b4e613b2e565b5b8235905067ffffffffffffffff811115613b6c57613b6b613b32565b5b602083019150836020820283011115613b8857613b87613b36565b5b9250929050565b5f8060208385031215613ba557613ba46136d5565b5b5f83013567ffffffffffffffff811115613bc257613bc16136d9565b5b613bce85828601613b3a565b92509250509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b613c0c816138b7565b82525050565b5f67ffffffffffffffff82169050919050565b613c2e81613c12565b82525050565b5f62ffffff82169050919050565b613c4b81613c34565b82525050565b608082015f820151613c655f850182613c03565b506020820151613c786020850182613c25565b506040820151613c8b60408501826139d0565b506060820151613c9e6060850182613c42565b50505050565b5f613caf8383613c51565b60808301905092915050565b5f602082019050919050565b5f613cd182613bda565b613cdb8185613be4565b9350613ce683613bf4565b805f5b83811015613d16578151613cfd8882613ca4565b9750613d0883613cbb565b925050600181019050613ce9565b5085935050505092915050565b5f6020820190508181035f830152613d3b8184613cc7565b905092915050565b5f60208284031215613d5857613d576136d5565b5b5f613d6584828501613906565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f613da28383613a27565b60208301905092915050565b5f602082019050919050565b5f613dc482613d6e565b613dce8185613d78565b9350613dd983613d88565b805f5b83811015613e09578151613df08882613d97565b9750613dfb83613dae565b925050600181019050613ddc565b5085935050505092915050565b5f6020820190508181035f830152613e2e8184613dba565b905092915050565b5f805f60608486031215613e4d57613e4c6136d5565b5b5f613e5a86828701613906565b9350506020613e6b86828701613859565b9250506040613e7c86828701613859565b9150509250925092565b613e8f8161375d565b8114613e99575f80fd5b50565b5f81359050613eaa81613e86565b92915050565b5f8060408385031215613ec657613ec56136d5565b5b5f613ed385828601613906565b9250506020613ee485828601613e9c565b9150509250929050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b613f28826137d2565b810181811067ffffffffffffffff82111715613f4757613f46613ef2565b5b80604052505050565b5f613f596136cc565b9050613f658282613f1f565b919050565b5f67ffffffffffffffff821115613f8457613f83613ef2565b5b613f8d826137d2565b9050602081019050919050565b828183375f83830152505050565b5f613fba613fb584613f6a565b613f50565b905082815260208101848484011115613fd657613fd5613eee565b5b613fe1848285613f9a565b509392505050565b5f82601f830112613ffd57613ffc613b2e565b5b813561400d848260208601613fa8565b91505092915050565b5f805f806080858703121561402e5761402d6136d5565b5b5f61403b87828801613906565b945050602061404c87828801613906565b935050604061405d87828801613859565b925050606085013567ffffffffffffffff81111561407e5761407d6136d9565b5b61408a87828801613fe9565b91505092959194509250565b5f80fd5b5f80fd5b5f67ffffffffffffffff8211156140b8576140b7613ef2565b5b6140c1826137d2565b9050602081019050919050565b5f6140e06140db8461409e565b613f50565b9050828152602081018484840111156140fc576140fb613eee565b5b614107848285613f9a565b509392505050565b5f82601f83011261412357614122613b2e565b5b81356141338482602086016140ce565b91505092915050565b5f60e0828403121561415157614150614096565b5b61415b60e0613f50565b90505f61416a84828501613e9c565b5f83015250602061417d84828501613e9c565b602083015250604061419184828501613e9c565b604083015250606082013567ffffffffffffffff8111156141b5576141b461409a565b5b6141c18482850161410f565b60608301525060806141d584828501613859565b60808301525060a06141e984828501613859565b60a08301525060c06141fd84828501613859565b60c08301525092915050565b5f805f8060608587031215614221576142206136d5565b5b5f85013567ffffffffffffffff81111561423e5761423d6136d9565b5b61424a87828801613b3a565b9450945050602085013567ffffffffffffffff81111561426d5761426c6136d9565b5b6142798782880161413c565b925050604085013567ffffffffffffffff81111561429a576142996136d9565b5b6142a68782880161410f565b91505092959194509250565b608082015f8201516142c65f850182613c03565b5060208201516142d96020850182613c25565b5060408201516142ec60408501826139d0565b5060608201516142ff6060850182613c42565b50505050565b5f6080820190506143185f8301846142b2565b92915050565b5f8083601f84011261433357614332613b2e565b5b8235905067ffffffffffffffff8111156143505761434f613b32565b5b60208301915083602082028301111561436c5761436b613b36565b5b9250929050565b5f8060208385031215614389576143886136d5565b5b5f83013567ffffffffffffffff8111156143a6576143a56136d9565b5b6143b28582860161431e565b92509250509250929050565b5f80604083850312156143d4576143d36136d5565b5b5f6143e185828601613906565b92505060206143f285828601613906565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061444057607f821691505b602082108103614453576144526143fc565b5b50919050565b5f81519050614467816138f0565b92915050565b5f60208284031215614482576144816136d5565b5b5f61448f84828501614459565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6144fc8261383a565b91506145078361383a565b92508261451757614516614498565b5b828204905092915050565b5f61452c8261383a565b91506145378361383a565b925082820390508181111561454f5761454e6144c5565b5b92915050565b5f61455f8261383a565b915061456a8361383a565b9250828201905080821115614582576145816144c5565b5b92915050565b5f60408201905061459b5f830185613958565b6145a86020830184613958565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6145e68261383a565b91506145f18361383a565b92508282026145ff8161383a565b91508282048414831517614616576146156144c5565b5b5092915050565b5f8151905061462b81613843565b92915050565b5f60208284031215614646576146456136d5565b5b5f6146538482850161461d565b91505092915050565b5f815f1c9050919050565b5f60ff82169050919050565b5f6146856146808361465c565b614667565b9050919050565b5f819050919050565b5f6146a76146a28361465c565b61468c565b9050919050565b5f819050815f5260205f209050919050565b5f81546146cc81614429565b6146d681866139df565b9450600182165f81146146f0576001811461470657614738565b60ff198316865281151560200286019350614738565b61470f856146ae565b5f5b8381101561473057815481890152600182019150602081019050614711565b808801955050505b50505092915050565b5f8160081c9050919050565b5f61475f61475a83614741565b614667565b9050919050565b5f8160101c9050919050565b5f61478461477f83614766565b614667565b9050919050565b5f60e083015f8084015490506147a081614673565b6147ac5f8701826139d0565b506147b68161474d565b6147c360208701826139d0565b506147cd81614772565b6147da60408701826139d0565b506001840185830360608701526147f183826146c0565b9250506002840154905061480481614695565b6148116080870182613a27565b506003840154905061482281614695565b61482f60a0870182613a27565b506004840154905061484081614695565b61484d60c0870182613a27565b50819250505092915050565b5f60a083015f80840154905061486e81614673565b61487a5f8701826139d0565b506001840154905061488b81614695565b6148986020870182613a27565b50600284015490506148a981614695565b6148b66040870182613a27565b506003840185830360608701526148cd83826146c0565b9250506004840185830360808701526148e6838261478b565b925050819250505092915050565b5f6020820190508181035f83015261490c8184614859565b905092915050565b5f6149266149218461409e565b613f50565b90508281526020810184848401111561494257614941613eee565b5b61494d8482856137aa565b509392505050565b5f82601f83011261496957614968613b2e565b5b8151614979848260208601614914565b91505092915050565b5f60208284031215614997576149966136d5565b5b5f82015167ffffffffffffffff8111156149b4576149b36136d9565b5b6149c084828501614955565b91505092915050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302614a137fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826149d8565b614a1d86836149d8565b95508019841693508086168417925050509392505050565b5f819050919050565b5f614a58614a53614a4e8461383a565b614a35565b61383a565b9050919050565b5f819050919050565b614a7183614a3e565b614a85614a7d82614a5f565b8484546149e4565b825550505050565b5f90565b614a99614a8d565b614aa4818484614a68565b505050565b5b81811015614ac757614abc5f82614a91565b600181019050614aaa565b5050565b601f821115614b0c57614add816146ae565b614ae6846149c9565b81016020851015614af5578190505b614b09614b01856149c9565b830182614aa9565b50505b505050565b5f82821c905092915050565b5f614b2c5f1984600802614b11565b1980831691505092915050565b5f614b448383614b1d565b9150826002028217905092915050565b614b5d82613790565b67ffffffffffffffff811115614b7657614b75613ef2565b5b614b808254614429565b614b8b828285614acb565b5f60209050601f831160018114614bbc575f8415614baa578287015190505b614bb48582614b39565b865550614c1b565b601f198416614bca866146ae565b5f5b82811015614bf157848901518255600182019150602085019450602081019050614bcc565b86831015614c0e5784890151614c0a601f891682614b1d565b8355505b6001600288020188555050505b505050505050565b5f614c2d8261383a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614c5f57614c5e6144c5565b5b600182019050919050565b5f604082019050614c7d5f830185613958565b8181036020830152614c8f8184614859565b90509392505050565b5f614ca28261383a565b9150614cad8361383a565b925082614cbd57614cbc614498565b5b828206905092915050565b5f819050919050565b614ce2614cdd8261383a565b614cc8565b82525050565b5f614cf38284614cd1565b60208201915081905092915050565b5f81519050919050565b5f82825260208201905092915050565b5f614d2682614d02565b614d308185614d0c565b9350614d408185602086016137aa565b614d49816137d2565b840191505092915050565b5f608082019050614d675f8301876138c8565b614d7460208301866138c8565b614d816040830185613958565b8181036060830152614d938184614d1c565b905095945050505050565b5f81519050614dac81613708565b92915050565b5f60208284031215614dc757614dc66136d5565b5b5f614dd484828501614d9e565b91505092915050565b7f696e76616c6964206c656e6774680000000000000000000000000000000000005f82015250565b5f614e11600e8361379a565b9150614e1c82614ddd565b602082019050919050565b5f6020820190508181035f830152614e3e81614e05565b905091905056fea2646970667358221220372f96efb9bf8fd2c063fd42d3ae70e20116b2a331f1377a5970c58b7675af5064736f6c63430008170033608060405234801562000010575f80fd5b50620000216200002760201b60201c565b62002064565b60405180606001604052806040518060400160405280600581526020017f576869746500000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f23464646454637000000000000000000000000000000000000000000000000008152508152602001600115158152505f808081526020019081526020015f205f820151815f019081620000d4919062001f80565b506020820151816001019081620000ec919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180606001604052806040518060400160405280600581526020017f576869746500000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f234646464546370000000000000000000000000000000000000000000000000081525081526020015f15158152505f80600181526020019081526020015f205f820151815f019081620001bc919062001f80565b506020820151816001019081620001d4919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180606001604052806040518060400160405280600381526020017f526564000000000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f23453630303230000000000000000000000000000000000000000000000000008152508152602001600115158152505f80600281526020019081526020015f205f820151815f019081620002a5919062001f80565b506020820151816001019081620002bd919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180606001604052806040518060400160405280600381526020017f526564000000000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f234536303032300000000000000000000000000000000000000000000000000081525081526020015f15158152505f80600381526020019081526020015f205f820151815f0190816200038d919062001f80565b506020820151816001019081620003a5919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180606001604052806040518060400160405280600681526020017f59656c6c6f77000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f23464641453030000000000000000000000000000000000000000000000000008152508152602001600115158152505f80600481526020019081526020015f205f820151815f01908162000476919062001f80565b5060208201518160010190816200048e919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180606001604052806040518060400160405280600681526020017f59656c6c6f77000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f234646414530300000000000000000000000000000000000000000000000000081525081526020015f15158152505f80600581526020019081526020015f205f820151815f0190816200055e919062001f80565b50602082015181600101908162000576919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180606001604052806040518060400160405280600481526020017f477261790000000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f23323532343235000000000000000000000000000000000000000000000000008152508152602001600115158152505f80600681526020019081526020015f205f820151815f01908162000647919062001f80565b5060208201518160010190816200065f919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180606001604052806040518060400160405280600481526020017f477261790000000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600781526020017f233235323432350000000000000000000000000000000000000000000000000081525081526020015f15158152505f80600781526020019081526020015f205f820151815f0190816200072f919062001f80565b50602082015181600101908162000747919062001f80565b506040820151816002015f6101000a81548160ff02191690831515021790555090505060405180604001604052806040518060400160405280600581526020017f4c61676f7300000000000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b781526020016200619c60b7913981525060015f8081526020019081526020015f205f820151815f019081620007f3919062001f80565b5060208201518160010190816200080b919062001f80565b5090505060405180604001604052806040518060400160405280600981526020017f53616e205061756c6f000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b781526020016200653260b7913981525060015f600181526020019081526020015f205f820151815f01908162000899919062001f80565b506020820151816001019081620008b1919062001f80565b5090505060405180604001604052806040518060400160405280600681526020017f426f676f7461000000000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b7815260200162005d5260b7913981525060015f600281526020019081526020015f205f820151815f0190816200093f919062001f80565b50602082015181600101908162000957919062001f80565b5090505060405180604001604052806040518060400160405280600c81526020017f4a6f68616e6e657362757267000000000000000000000000000000000000000081525081526020016040518060e0016040528060b7815260200162005e0960b7913981525060015f600381526020019081526020015f205f820151815f019081620009e5919062001f80565b506020820151816001019081620009fd919062001f80565b5090505060405180604001604052806040518060400160405280600981526020017f56616e636f75766572000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b481526020016200630d60b4913981525060015f600481526020019081526020015f205f820151815f01908162000a8b919062001f80565b50602082015181600101908162000aa3919062001f80565b5090505060405180604001604052806040518060400160405280600781526020017f4172697a6f6e610000000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b48152602001620066a360b4913981525060015f600581526020019081526020015f205f820151815f01908162000b31919062001f80565b50602082015181600101908162000b49919062001f80565b5090505060405180604001604052806040518060400160405280600581526020017f4d69616d6900000000000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b78152602001620063c160b7913981525060015f600681526020019081526020015f205f820151815f01908162000bd7919062001f80565b50602082015181600101908162000bef919062001f80565b5090505060405180604001604052806040518060400160405280600b81526020017f53616e20416e746f6e696f00000000000000000000000000000000000000000081525081526020016040518060e0016040528060ba8152602001620060e260ba913981525060015f600781526020019081526020015f205f820151815f01908162000c7d919062001f80565b50602082015181600101908162000c95919062001f80565b5090505060405180604001604052806040518060400160405280600881526020017f4e657720596f726b00000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b481526020016200675760b4913981525060015f600881526020019081526020015f205f820151815f01908162000d23919062001f80565b50602082015181600101908162000d3b919062001f80565b5090505060405180604001604052806040518060400160405280600881526020017f4d6172796c616e6400000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060ba81526020016200647860ba913981525060015f600981526020019081526020015f205f820151815f01908162000dc9919062001f80565b50602082015181600101908162000de1919062001f80565b5090505060405180604001604052806040518060400160405280600981526020017f53696e6761706f7265000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060ba81526020016200625360ba913981525060015f600a81526020019081526020015f205f820151815f01908162000e6f919062001f80565b50602082015181600101908162000e87919062001f80565b5090505060405180604001604052806040518060400160405280600781526020017f57796f6d696e670000000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b481526020016200602e60b4913981525060015f600b81526020019081526020015f205f820151815f01908162000f15919062001f80565b50602082015181600101908162000f2d919062001f80565b5090505060405180604001604052806040518060400160405280600481526020017f436c756a0000000000000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060ba815260200162005f7460ba913981525060015f600c81526020019081526020015f205f820151815f01908162000fbb919062001f80565b50602082015181600101908162000fd3919062001f80565b5090505060405180604001604052806040518060400160405280600781526020017f4368696361676f0000000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060b4815260200162005ec060b4913981525060015f600d81526020019081526020015f205f820151815f01908162001061919062001f80565b50602082015181600101908162001079919062001f80565b5090505060405180604001604052806040518060400160405280600881526020017f506f72746c616e6400000000000000000000000000000000000000000000000081525081526020016040518060e0016040528060ba8152602001620065e960ba913981525060015f600e81526020019081526020015f205f820151815f01908162001107919062001f80565b5060208201518160010190816200111f919062001f80565b5090505060405180604001604052806040518060400160405280600481526020017f4e6f6e6500000000000000000000000000000000000000000000000000000000815250815260200160405180602001604052805f81525081525060015f600f81526020019081526020015f205f820151815f019081620011a2919062001f80565b506020820151816001019081620011ba919062001f80565b509050506040518060e001604052805f151581526020015f151581526020015f1515815260200160405180602001604052805f81525081526020015f81526020015f8152602001600981525060025f8081526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff021916908315150217905550606082015181600101908162001289919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020015f151581526020015f151581526020016040518060400160405280600f81526020017f4a756c7920313174682c203230323200000000000000000000000000000000008152508152602001600581526020015f8152602001600b81525060025f600181526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff02191690831515021790555060608201518160010190816200139f919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020016001151581526020015f151581526020016040518060400160405280601481526020017f5041494e204245464f52452050524f47524553530000000000000000000000008152508152602001600481526020015f8152602001600981525060025f600281526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff0219169083151502179055506060820151816001019081620014b6919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020015f151581526020015f151581526020016040518060400160405280601281526020017f4e6f76656d626572203674682c203139393000000000000000000000000000008152508152602001600981526020015f8152602001600981525060025f600381526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff0219169083151502179055506060820151816001019081620015cc919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020015f151581526020015f151581526020016040518060400160405280601281526020017f4f63746f62657220333173742c203230303800000000000000000000000000008152508152602001600e81526020015f8152602001600981525060025f600481526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff0219169083151502179055506060820151816001019081620016e2919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020016001151581526020015f151581526020016040518060400160405280601681526020017f544845204d41524154484f4e20434f4e54494e554553000000000000000000008152508152602001600881526020015f8152602001600981525060025f600581526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff0219169083151502179055506060820151816001019081620017f9919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052806001151581526020016001151581526020015f151581526020016040518060400160405280600f81526020017f4d414d4241204d454e54414c49545900000000000000000000000000000000008152508152602001600381526020015f8152602001600981525060025f600681526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff021916908315150217905550606082015181600101908162001911919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020015f151581526020016001151581526020016040518060400160405280600f81526020017f2a4e4f5420464f5220524553414c4500000000000000000000000000000000008152508152602001600f815260200160028152602001600e81525060025f600781526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff021916908315150217905550606082015181600101908162001a29919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020016001151581526020015f1515815260200160405180602001604052805f8152508152602001600881526020015f8152602001600981525060025f600881526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff021916908315150217905550606082015181600101908162001b19919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020015f151581526020015f1515815260200160405180602001604052805f8152508152602001600181526020015f8152602001600981525060025f600981526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff021916908315150217905550606082015181600101908162001c08919062001f80565b506080820151816002015560a0820151816003015560c082015181600401559050506040518060e001604052805f151581526020015f151581526020015f1515815260200160405180602001604052805f8152508152602001600d815260200160028152602001600981525060025f600a81526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff021916908315150217905550606082015181600101908162001cf8919062001f80565b506080820151816002015560a0820151816003015560c08201518160040155905050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168062001d9857607f821691505b60208210810362001dae5762001dad62001d53565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f6008830262001e127fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262001dd5565b62001e1e868362001dd5565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f62001e6862001e6262001e5c8462001e36565b62001e3f565b62001e36565b9050919050565b5f819050919050565b62001e838362001e48565b62001e9b62001e928262001e6f565b84845462001de1565b825550505050565b5f90565b62001eb162001ea3565b62001ebe81848462001e78565b505050565b5b8181101562001ee55762001ed95f8262001ea7565b60018101905062001ec4565b5050565b601f82111562001f345762001efe8162001db4565b62001f098462001dc6565b8101602085101562001f19578190505b62001f3162001f288562001dc6565b83018262001ec3565b50505b505050565b5f82821c905092915050565b5f62001f565f198460080262001f39565b1980831691505092915050565b5f62001f70838362001f45565b9150826002028217905092915050565b62001f8b8262001d1c565b67ffffffffffffffff81111562001fa75762001fa662001d26565b5b62001fb3825462001d80565b62001fc082828562001ee9565b5f60209050601f83116001811462001ff6575f841562001fe1578287015190505b62001fed858262001f63565b8655506200205c565b601f198416620020068662001db4565b5f5b828110156200202f5784890151825560018201915060208501945060208101905062002008565b868310156200204f57848901516200204b601f89168262001f45565b8355505b6001600288020188555050505b505050505050565b613ce080620020725f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063489320a414610038578063a3915e6514610068575b5f80fd5b610052600480360381019061004d9190612355565b610098565b60405161005f9190612416565b60405180910390f35b610082600480360381019061007d9190612436565b6103dc565b60405161008f9190612416565b60405180910390f35b60605f6040518060400160405280600481526020017f2346464600000000000000000000000000000000000000000000000000000000815250905060605f6100df85610417565b90505f856040015190506007811180156100f95750600b81105b156101cc5760088103610143576040518060400160405280600781526020017f234636383731460000000000000000000000000000000000000000000000000081525093506101cb565b60098103610188576040518060400160405280600781526020017f233030413635310000000000000000000000000000000000000000000000000081525093506101ca565b600a81036101c9576040518060400160405280600781526020017f233030344537430000000000000000000000000000000000000000000000000081525093505b5b5b5b6007811461021e5781602001516101fe576040518061046001604052806104268152602001613885610426913961021b565b60405180610480016040528061045381526020016133f261045391395b92505b835f808460a0015181526020019081526020015f206002015f9054906101000a900460ff1661025b5760405180602001604052805f815250610292565b6040518060400160405280600781526020017f72783d22323122000000000000000000000000000000000000000000000000008152505b61029f8460c00151610903565b6102ac8560600151610a81565b865f808860a0015181526020019081526020015f206001018760400151610308576040518060400160405280600481526020017f233030300000000000000000000000000000000000000000000000000000000081525061033f565b6040518060400160405280600481526020017f23464646000000000000000000000000000000000000000000000000000000008152505b885f015161035b5760405180602001604052805f815250610392565b6040518060400160405280600e81526020017f7374726f6b653a233233314632300000000000000000000000000000000000008152505b60015f8b6080015181526020019081526020015f206001016040516020016103c299989796959493929190612bbe565b604051602081830303815290604052945050505050919050565b60606103f06103eb84846115a0565b6115ee565b6040516020016104009190612cf4565b604051602081830303815290604052905092915050565b61041f611fcd565b5f826040015190506007810361054e5760025f600781526020019081526020015f206040518060e00160405290815f82015f9054906101000a900460ff161515151581526020015f820160019054906101000a900460ff161515151581526020015f820160029054906101000a900460ff161515151581526020016001820180546104a9906129d4565b80601f01602080910402602001604051908101604052809291908181526020018280546104d5906129d4565b80156105205780601f106104f757610100808354040283529160200191610520565b820191905f5260205f20905b81548152906001019060200180831161050357829003601f168201915b50505050508152602001600282015481526020016003820154815260200160048201548152505091506108fd565b60078111801561055e5750600b81105b156107cc57825f0151156106ad5760025f8281526020019081526020015f206040518060e00160405290815f82015f9054906101000a900460ff161515151581526020015f820160019054906101000a900460ff161515151581526020015f820160029054906101000a900460ff161515151581526020016001820180546105e5906129d4565b80601f0160208091040260200160405190810160405280929190818152602001828054610611906129d4565b801561065c5780601f106106335761010080835404028352916020019161065c565b820191905f5260205f20905b81548152906001019060200180831161063f57829003601f168201915b50505050508152602001600282015481526020016003820154815260200160048201548152505091508260800151606001518260600181905250826080015160c001518260c00181815250506107c7565b60025f8281526020019081526020015f206040518060e00160405290815f82015f9054906101000a900460ff161515151581526020015f820160019054906101000a900460ff161515151581526020015f820160029054906101000a900460ff16151515158152602001600182018054610726906129d4565b80601f0160208091040260200160405190810160405280929190818152602001828054610752906129d4565b801561079d5780601f106107745761010080835404028352916020019161079d565b820191905f5260205f20905b81548152906001019060200180831161078057829003601f168201915b50505050508152602001600282015481526020016003820154815260200160048201548152505091505b6108fc565b825f0151156107e157826080015191506108fb565b60025f8281526020019081526020015f206040518060e00160405290815f82015f9054906101000a900460ff161515151581526020015f820160019054906101000a900460ff161515151581526020015f820160029054906101000a900460ff1615151515815260200160018201805461085a906129d4565b80601f0160208091040260200160405190810160405280929190818152602001828054610886906129d4565b80156108d15780601f106108a8576101008083540402835291602001916108d1565b820191905f5260205f20905b8154815290600101906020018083116108b457829003601f168201915b50505050508152602001600282015481526020016003820154815260200160048201548152505091505b5b5b50919050565b60605f8203610949576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050610a7c565b5f8290505f5b5f821461097857808061096190612d42565b915050600a826109719190612db6565b915061094f565b5f8167ffffffffffffffff8111156109935761099261202f565b5b6040519080825280601f01601f1916602001820160405280156109c55781602001600182028036833780820191505090505b5090505f8290505b5f8614610a74576001816109e19190612de6565b90505f600a80886109f29190612db6565b6109fc9190612e19565b87610a079190612de6565b6030610a139190612e66565b90505f8160f81b905080848481518110610a3057610a2f612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600a88610a6b9190612db6565b975050506109cd565b819450505050505b919050565b60605f8290505f60058251610a969190612e19565b67ffffffffffffffff811115610aaf57610aae61202f565b5b6040519080825280601f01601f191660200182016040528015610ae15781602001600182028036833780820191505090505b5090505f805b83518110156114cc577f2600000000000000000000000000000000000000000000000000000000000000848281518110610b2457610b23612e9a565b5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610e7b576040518060400160405280600181526020017f26000000000000000000000000000000000000000000000000000000000000008152505f81518110610b9e57610b9d612e9a565b5b602001015160f81c60f81b838380610bb590612d42565b945081518110610bc857610bc7612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f61000000000000000000000000000000000000000000000000000000000000008152505f81518110610c3e57610c3d612e9a565b5b602001015160f81c60f81b838380610c5590612d42565b945081518110610c6857610c67612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f6d000000000000000000000000000000000000000000000000000000000000008152505f81518110610cde57610cdd612e9a565b5b602001015160f81c60f81b838380610cf590612d42565b945081518110610d0857610d07612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f70000000000000000000000000000000000000000000000000000000000000008152505f81518110610d7e57610d7d612e9a565b5b602001015160f81c60f81b838380610d9590612d42565b945081518110610da857610da7612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f3b000000000000000000000000000000000000000000000000000000000000008152505f81518110610e1e57610e1d612e9a565b5b602001015160f81c60f81b838380610e3590612d42565b945081518110610e4857610e47612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506114bf565b7f3c00000000000000000000000000000000000000000000000000000000000000848281518110610eaf57610eae612e9a565b5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603611166576040518060400160405280600181526020017f26000000000000000000000000000000000000000000000000000000000000008152505f81518110610f2957610f28612e9a565b5b602001015160f81c60f81b838380610f4090612d42565b945081518110610f5357610f52612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f6c000000000000000000000000000000000000000000000000000000000000008152505f81518110610fc957610fc8612e9a565b5b602001015160f81c60f81b838380610fe090612d42565b945081518110610ff357610ff2612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f74000000000000000000000000000000000000000000000000000000000000008152505f8151811061106957611068612e9a565b5b602001015160f81c60f81b83838061108090612d42565b94508151811061109357611092612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f3b000000000000000000000000000000000000000000000000000000000000008152505f8151811061110957611108612e9a565b5b602001015160f81c60f81b83838061112090612d42565b94508151811061113357611132612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506114be565b7f3e0000000000000000000000000000000000000000000000000000000000000084828151811061119a57611199612e9a565b5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603611451576040518060400160405280600181526020017f26000000000000000000000000000000000000000000000000000000000000008152505f8151811061121457611213612e9a565b5b602001015160f81c60f81b83838061122b90612d42565b94508151811061123e5761123d612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f67000000000000000000000000000000000000000000000000000000000000008152505f815181106112b4576112b3612e9a565b5b602001015160f81c60f81b8383806112cb90612d42565b9450815181106112de576112dd612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f74000000000000000000000000000000000000000000000000000000000000008152505f8151811061135457611353612e9a565b5b602001015160f81c60f81b83838061136b90612d42565b94508151811061137e5761137d612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506040518060400160405280600181526020017f3b000000000000000000000000000000000000000000000000000000000000008152505f815181106113f4576113f3612e9a565b5b602001015160f81c60f81b83838061140b90612d42565b94508151811061141e5761141d612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506114bd565b83818151811061146457611463612e9a565b5b602001015160f81c60f81b83838061147b90612d42565b94508151811061148e5761148d612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505b5b5b8080600101915050610ae7565b505f8167ffffffffffffffff8111156114e8576114e761202f565b5b6040519080825280601f01601f19166020018201604052801561151a5781602001600182028036833780820191505090505b5090505f5b828110156115935783818151811061153a57611539612e9a565b5b602001015160f81c60f81b82828151811061155857611557612e9a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350808060010191505061151f565b5080945050505050919050565b60606115ab83610903565b6115b48361174d565b6115c56115c085610098565b6115ee565b6040516020016115d7939291906130cb565b604051602081830303815290604052905092915050565b60605f82510361160e5760405180602001604052805f8152509050611748565b5f6040518060600160405280604081526020016138456040913990505f60036002855161163b9190613127565b6116459190612db6565b60046116519190612e19565b67ffffffffffffffff81111561166a5761166961202f565b5b6040519080825280601f01601f19166020018201604052801561169c5781602001600182028036833780820191505090505b509050600182016020820185865187015b80821015611708576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f81168501518453600184019350506116ad565b505060038651066001811461172457600281146117375761173f565b603d6001830353603d600283035361173f565b603d60018303535b50505080925050505b919050565b60606007826040015114611eb3575f61176583610417565b90506117dd6040518060400160405280600681526020017f4275726e656400000000000000000000000000000000000000000000000000008152506040518060400160405280600781526020017f2266616c736522000000000000000000000000000000000000000000000000008152506001611f4f565b6118976040518060400160405280600681526020017f4564697465640000000000000000000000000000000000000000000000000000815250855f0151611859576040518060400160405280600781526020017f2266616c73652200000000000000000000000000000000000000000000000000815250611890565b6040518060400160405280600681526020017f22747275652200000000000000000000000000000000000000000000000000008152505b6001611f4f565b61190e6040518060400160405280600881526020017f4f726967696e616c00000000000000000000000000000000000000000000000081525060025f886020015181526020019081526020015f206001016040516020016118f891906131a4565b6040516020818303038152906040526001611f4f565b6119dc6040518060400160405280600a81526020017f53757065722052617265000000000000000000000000000000000000000000008152506007886040015111801561195f5750600b8860400151105b61199e576040518060400160405280600781526020017f2266616c736522000000000000000000000000000000000000000000000000008152506119d5565b6040518060400160405280600681526020017f22747275652200000000000000000000000000000000000000000000000000008152505b6001611f4f565b611a966040518060400160405280600681526020017f5374726f6b650000000000000000000000000000000000000000000000000000815250865f0151611a58576040518060400160405280600781526020017f2266616c73652200000000000000000000000000000000000000000000000000815250611a8f565b6040518060400160405280600681526020017f22747275652200000000000000000000000000000000000000000000000000008152505b6001611f4f565b611b516040518060400160405280600781526020017f426172636f6465000000000000000000000000000000000000000000000000008152508760200151611b13576040518060400160405280600c81526020017f22486f72697a6f6e74616c220000000000000000000000000000000000000000815250611b4a565b6040518060400160405280600a81526020017f22566572746963616c22000000000000000000000000000000000000000000008152505b6001611f4f565b611c2b6040518060400160405280600b81526020017f4c6162656c205374796c650000000000000000000000000000000000000000008152505f808a60a0015181526020019081526020015f206002015f9054906101000a900460ff16611bed576040518060400160405280600881526020017f2253717561726522000000000000000000000000000000000000000000000000815250611c24565b6040518060400160405280600781526020017f22526f756e6422000000000000000000000000000000000000000000000000008152505b6001611f4f565b611ca16040518060400160405280600e81526020017f436f6c6f7220537065637472756d00000000000000000000000000000000000081525060015f8b6080015181526020019081526020015f205f01604051602001611c8b91906131a4565b6040516020818303038152906040526001611f4f565b611d166040518060400160405280600b81526020017f4c6162656c20436f6c6f720000000000000000000000000000000000000000008152505f808c60a0015181526020019081526020015f205f01604051602001611d0091906131a4565b6040516020818303038152906040526001611f4f565b611dd16040518060400160405280600a81526020017f5465787420436f6c6f72000000000000000000000000000000000000000000008152508b60400151611d93576040518060400160405280600781526020017f22426c61636b2200000000000000000000000000000000000000000000000000815250611dca565b6040518060400160405280600781526020017f22576869746522000000000000000000000000000000000000000000000000008152505b6001611f4f565b611e1e6040518060400160405280600981526020017f546578742053697a650000000000000000000000000000000000000000000000815250611e178d60c00151610903565b6001611f4f565b611e816040518060400160405280600881526020017f4d657461646174610000000000000000000000000000000000000000000000008152508f60600151604051602001611e6c91906131d0565b6040516020818303038152906040525f611f4f565b604051602001611e9c9c9b9a999897969594939291906131fc565b604051602081830303815290604052915050611f4a565b611f286040518060400160405280600681526020017f4275726e656400000000000000000000000000000000000000000000000000008152506040518060400160405280600681526020017f22747275652200000000000000000000000000000000000000000000000000008152505f611f4f565b604051602001611f3891906132a1565b60405160208183030381529060405290505b919050565b6060838383611f6c5760405180602001604052805f815250611fa3565b6040518060400160405280600181526020017f2c000000000000000000000000000000000000000000000000000000000000008152505b604051602001611fb593929190613395565b60405160208183030381529060405290509392505050565b6040518060e001604052805f151581526020015f151581526020015f15158152602001606081526020015f81526020015f81526020015f81525090565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6120658261201f565b810181811067ffffffffffffffff821117156120845761208361202f565b5b80604052505050565b5f61209661200a565b90506120a2828261205c565b919050565b5f80fd5b5f8115159050919050565b6120bf816120ab565b81146120c9575f80fd5b50565b5f813590506120da816120b6565b92915050565b5f819050919050565b6120f2816120e0565b81146120fc575f80fd5b50565b5f8135905061210d816120e9565b92915050565b5f80fd5b5f80fd5b5f67ffffffffffffffff8211156121355761213461202f565b5b61213e8261201f565b9050602081019050919050565b828183375f83830152505050565b5f61216b6121668461211b565b61208d565b90508281526020810184848401111561218757612186612117565b5b61219284828561214b565b509392505050565b5f82601f8301126121ae576121ad612113565b5b81356121be848260208601612159565b91505092915050565b5f60e082840312156121dc576121db61201b565b5b6121e660e061208d565b90505f6121f5848285016120cc565b5f830152506020612208848285016120cc565b602083015250604061221c848285016120cc565b604083015250606082013567ffffffffffffffff8111156122405761223f6120a7565b5b61224c8482850161219a565b6060830152506080612260848285016120ff565b60808301525060a0612274848285016120ff565b60a08301525060c0612288848285016120ff565b60c08301525092915050565b5f60a082840312156122a9576122a861201b565b5b6122b360a061208d565b90505f6122c2848285016120cc565b5f8301525060206122d5848285016120ff565b60208301525060406122e9848285016120ff565b604083015250606082013567ffffffffffffffff81111561230d5761230c6120a7565b5b6123198482850161219a565b606083015250608082013567ffffffffffffffff81111561233d5761233c6120a7565b5b612349848285016121c7565b60808301525092915050565b5f6020828403121561236a57612369612013565b5b5f82013567ffffffffffffffff81111561238757612386612017565b5b61239384828501612294565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156123d35780820151818401526020810190506123b8565b5f8484015250505050565b5f6123e88261239c565b6123f281856123a6565b93506124028185602086016123b6565b61240b8161201f565b840191505092915050565b5f6020820190508181035f83015261242e81846123de565b905092915050565b5f806040838503121561244c5761244b612013565b5b5f612459858286016120ff565b925050602083013567ffffffffffffffff81111561247a57612479612017565b5b61248685828601612294565b9150509250929050565b5f81905092915050565b7f3c7376672077696474683d2238303022206865696768743d22383030222076695f8201527f6577426f783d223020302032303220323032222066696c6c3d226e6f6e65222060208201527f786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f737660408201527f67223e3c726563742069643d2262672220783d22312220793d2231222072783d60608201527f2236222077696474683d2232303022206865696768743d22323030222066696c60808201527f6c3d22000000000000000000000000000000000000000000000000000000000060a082015250565b5f61258c60a383612490565b91506125978261249a565b60a382019050919050565b5f6125ac8261239c565b6125b68185612490565b93506125c68185602086016123b6565b80840191505092915050565b7f222f3e3c726563742069643d226c6162656c22200000000000000000000000005f82015250565b5f612606601483612490565b9150612611826125d2565b601482019050919050565b7f20783d2231392220793d223830222077696474683d22313634222068656967685f8201527f743d223432222066696c6c3d2223323132313231222f3e3c706174682069643d60208201527f2274657874706174682220643d224d3139203130314c31383320313031222f3e60408201527f3c746578742066696c6c3d22233233314632302220666f6e742d66616d696c7960608201527f3d2273797374656d2d75692220666f6e742d73697a653d220000000000000000608082015250565b5f6126e8609883612490565b91506126f38261261c565b609882019050919050565b7f2220666f6e742d7765696768743d22626f6c642220746578742d616e63686f725f8201527f3d226d6964646c6522206c65747465722d73706163696e673d2230656d22206460208201527f793d222e3336656d223e3c746578745061746820687265663d2223746578747060408201527f617468222073746172744f66667365743d22353025223e000000000000000000606082015250565b5f6127a4607783612490565b91506127af826126fe565b607782019050919050565b7f3c2f74657874506174683e3c2f746578743e3c636972636c652069643d2263365f8201527f222063783d22313839222063793d223138392220723d2235222f3e3c6369726360208201527f6c652069643d226335222063783d22313739222063793d223138392220723d2260408201527f35222f3e3c636972636c652069643d226334222063783d22313639222063793d60608201527f223138392220723d2235222f3e3c636972636c652069643d226333222063783d60808201527f22313539222063793d223138392220723d2235222f3e3c636972636c6520696460a08201527f3d226332222063783d22313439222063793d223138392220723d2235222f3e3c60c08201527f636972636c652069643d226331222063783d22313339222063793d223138392260e08201527f20723d2235222f3e3c672069643d22626172636f6465223e000000000000000061010082015250565b5f61292061011883612490565b915061292b826127ba565b61011882019050919050565b7f3c2f673e3c646566733e3c7374796c653e23626172636f646520706174687b665f8201527f696c6c3a233031303130317d236c6162656c7b66696c6c3a0000000000000000602082015250565b5f612991603883612490565b915061299c82612937565b603882019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806129eb57607f821691505b6020821081036129fe576129fd6129a7565b5b50919050565b5f819050815f5260205f209050919050565b5f8154612a22816129d4565b612a2c8186612490565b9450600182165f8114612a465760018114612a5b57612a8d565b60ff1983168652811515820286019350612a8d565b612a6485612a04565b5f5b83811015612a8557815481890152600182019150602081019050612a66565b838801955050505b50505092915050565b7f7d746578747b66696c6c3a0000000000000000000000000000000000000000005f82015250565b5f612aca600b83612490565b9150612ad582612a96565b600b82019050919050565b7f7d2362677b0000000000000000000000000000000000000000000000000000005f82015250565b5f612b14600583612490565b9150612b1f82612ae0565b600582019050919050565b7f7d000000000000000000000000000000000000000000000000000000000000005f82015250565b5f612b5e600183612490565b9150612b6982612b2a565b600182019050919050565b7f3c2f7374796c653e3c2f646566733e3c2f7376673e00000000000000000000005f82015250565b5f612ba8601583612490565b9150612bb382612b74565b601582019050919050565b5f612bc882612580565b9150612bd4828c6125a2565b9150612bdf826125fa565b9150612beb828b6125a2565b9150612bf6826126dc565b9150612c02828a6125a2565b9150612c0d82612798565b9150612c1982896125a2565b9150612c2482612913565b9150612c3082886125a2565b9150612c3b82612985565b9150612c478287612a16565b9150612c5282612abe565b9150612c5e82866125a2565b9150612c6982612b08565b9150612c7582856125a2565b9150612c8082612b52565b9150612c8c8284612a16565b9150612c9782612b9c565b91508190509a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000005f82015250565b5f612cde601d83612490565b9150612ce982612caa565b601d82019050919050565b5f612cfe82612cd2565b9150612d0a82846125a2565b915081905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f612d4c826120e0565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612d7e57612d7d612d15565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f612dc0826120e0565b9150612dcb836120e0565b925082612ddb57612dda612d89565b5b828204905092915050565b5f612df0826120e0565b9150612dfb836120e0565b9250828203905081811115612e1357612e12612d15565b5b92915050565b5f612e23826120e0565b9150612e2e836120e0565b9250828202612e3c816120e0565b91508282048414831517612e5357612e52612d15565b5b5092915050565b5f60ff82169050919050565b5f612e7082612e5a565b9150612e7b83612e5a565b9250828201905060ff811115612e9457612e93612d15565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f7b226e616d65223a20224d657461646174652f612000000000000000000000005f82015250565b5f612efb601583612490565b9150612f0682612ec7565b601582019050919050565b7f222c20226465736372697074696f6e223a2022416e20696d6d657273697665205f8201527f61727420696e7374616c6c6174696f6e207265636f7264696e6720746865206360208201527f6f6e76657267656e6365206f6620646174612e222c202261747472696275746560408201527f73223a205b000000000000000000000000000000000000000000000000000000606082015250565b5f612fb7606583612490565b9150612fc282612f11565b606582019050919050565b5f81519050919050565b5f81905092915050565b5f612feb82612fcd565b612ff58185612fd7565b93506130058185602086016123b6565b80840191505092915050565b7f5d2c2022696d616765223a2022646174613a696d6167652f7376672b786d6c3b5f8201527f6261736536342c00000000000000000000000000000000000000000000000000602082015250565b5f61306b602783612490565b915061307682613011565b602782019050919050565b7f227d0000000000000000000000000000000000000000000000000000000000005f82015250565b5f6130b5600283612490565b91506130c082613081565b600282019050919050565b5f6130d582612eef565b91506130e182866125a2565b91506130ec82612fab565b91506130f88285612fe1565b91506131038261305f565b915061310f82846125a2565b915061311a826130a9565b9150819050949350505050565b5f613131826120e0565b915061313c836120e0565b925082820190508082111561315457613153612d15565b5b92915050565b7f22000000000000000000000000000000000000000000000000000000000000005f82015250565b5f61318e600183612490565b91506131998261315a565b600182019050919050565b5f6131ae82613182565b91506131ba8284612a16565b91506131c582613182565b915081905092915050565b5f6131da82613182565b91506131e682846125a2565b91506131f182613182565b915081905092915050565b5f613207828f6125a2565b9150613213828e6125a2565b915061321f828d6125a2565b915061322b828c6125a2565b9150613237828b6125a2565b9150613243828a6125a2565b915061324f82896125a2565b915061325b82886125a2565b915061326782876125a2565b915061327382866125a2565b915061327f82856125a2565b915061328b82846125a2565b91508190509d9c50505050505050505050505050565b5f6132ac82846125a2565b915081905092915050565b7f7b000000000000000000000000000000000000000000000000000000000000005f82015250565b5f6132eb600183612490565b91506132f6826132b7565b600182019050919050565b7f2274726169745f74797065223a202200000000000000000000000000000000005f82015250565b5f613335600f83612490565b915061334082613301565b600f82019050919050565b7f222c2276616c7565223a200000000000000000000000000000000000000000005f82015250565b5f61337f600b83612490565b915061338a8261334b565b600b82019050919050565b5f61339f826132df565b91506133aa82613329565b91506133b682866125a2565b91506133c182613373565b91506133cd82856125a2565b91506133d882612b52565b91506133e482846125a2565b915081905094935050505056fe3c7061746820643d224d3920394c392031302e31324c32362e33322031302e313256394c3920395a222f3e3c7061746820643d224d392031302e38344c392031312e33324c32362e33322031312e33325631302e38344c392031302e38345a222f3e3c7061746820643d224d392031322e37374c392031332e32354c32362e33322031332e32355631322e37374c392031322e37375a222f3e3c7061746820643d224d392031342e30354c392031342e35334c32362e33322031342e35335631342e30354c392031342e30355a222f3e3c7061746820643d224d392031352e394c392031382e334c32362e33322031382e335631352e394c392031352e395a222f3e3c7061746820643d224d392031392e37354c392032302e32334c32362e33322032302e32335631392e37354c392031392e37355a222f3e3c7061746820643d224d392032312e30334c392032322e30374c32362e33322032322e30375632312e30334c392032312e30335a222f3e3c7061746820643d224d392032332e35324c392032344c32362e33322032345632332e35324c392032332e35325a222f3e3c7061746820643d224d392032362e36354c392032372e31334832362e33325632362e363548395a222f3e3c7061746820643d224d392032392e37384c392033302e32364832362e33325632392e373848395a222f3e3c7061746820643d224d392033312e37314c392033322e38334832362e33325633312e373148395a222f3e3c7061746820643d224d392033332e36334c392033342e31314832362e33325633332e363348395a222f3e3c7061746820643d224d392033362e37364c392033372e38384832362e33325633362e373648395a222f3e3c7061746820643d224d392033382e36394c392033392e31374832362e33325633382e363948395a222f3e3c7061746820643d224d392034302e35374c392034312e30314832362e33324c32362e33322034302e353748395a222f3e3c7061746820643d224d392034322e34364c392034322e39344832362e33325634322e343648395a222f3e3c7061746820643d224d392034332e37344c392034342e38364832362e33325634332e373448395a222f3e3c7061746820643d224d392034372e35314c392034372e39394832362e33325634372e353148395a222f3e3c7061746820643d224d392034382e384c392035312e31324832362e33325634382e3848395a222f3e3c7061746820643d224d392035312e39334c392035332e36394832362e33325635312e393348395a222f3e3c7061746820643d224d392035342e34394c392035352e36314832362e33325635342e343948395a222f3e3c7061746820643d224d392035372e36324c392035392e33384832362e33325635372e363248395a222f3e3c7061746820643d224d392036302e31314c392036302e35394832362e33325636302e313148395a222f3e3c7061746820643d224d392036312e344c392036322e35324832362e33325636312e3448395a222f3e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f3c7061746820643d224d392032362e33324831302e3132563948395632362e33325a222f3e3c7061746820643d224d31302e38342032362e33324831312e333256394831302e38345632362e33325a222f3e3c7061746820643d224d31322e37372032362e33324831332e323556394831322e37375632362e33325a222f3e3c7061746820643d224d31342e30352032362e33324831342e353356394831342e30355632362e33325a222f3e3c7061746820643d224d31352e392032362e33324831382e3356394831352e395632362e33325a222f3e3c7061746820643d224d31392e37352032362e33324832302e323356394831392e37355632362e33325a222f3e3c7061746820643d224d32312e30332032362e33324832322e303756394832312e30335632362e33325a222f3e3c7061746820643d224d32332e35322032362e333248323456394832332e35325632362e33325a222f3e3c7061746820643d224d32362e36352032362e33324832372e313356394832362e36355632362e33325a222f3e3c7061746820643d224d32392e37382032362e33324833302e323656394832392e37385632362e33325a222f3e3c7061746820643d224d33312e37312032362e33324833322e383356394833312e37315632362e33325a222f3e3c7061746820643d224d33332e36332032362e33324833342e313156394833332e36335632362e33325a222f3e3c7061746820643d224d33362e37362032362e33324833372e383856394833362e37365632362e33325a222f3e3c7061746820643d224d33382e36392032362e33324833392e313756394833382e36395632362e33325a222f3e3c7061746820643d224d34302e35372032362e33324834312e303156394834302e35375632362e33325a222f3e3c7061746820643d224d34322e34362032362e33324834322e393456394834322e34365632362e33325a222f3e3c7061746820643d224d34332e37342032362e33324834342e383656394834332e37345632362e33325a222f3e3c7061746820643d224d34372e35312032362e33324834372e393956394834372e35315632362e33325a222f3e3c7061746820643d224d34382e382032362e33324835312e313256394834382e385632362e33325a222f3e3c7061746820643d224d35312e39332032362e33324835332e363956394835312e39335632362e33325a222f3e3c7061746820643d224d35342e34392032362e33324835352e363156394835342e34395632362e33325a222f3e3c7061746820643d224d35372e36322032362e33324835392e333856394835372e36325632362e33325a222f3e3c7061746820643d224d36302e31312032362e33324836302e353956394836302e31315632362e33325a222f3e3c7061746820643d224d36312e342032362e33324836322e353256394836312e345632362e33325a222f3ea264697066735822122017ee73f3e74ea938da135dc6576e352914b004314d647d04c1c182a4bd187aa864736f6c634300081700332363317b66696c6c3a234632364436313b6f7061636974793a3830253b7d2363327b66696c6c3a234632364436313b6f7061636974793a313030253b7d2363337b66696c6c3a234133433745393b6f7061636974793a3830253b7d2363347b66696c6c3a234133433745393b6f7061636974793a313030253b7d2363357b66696c6c3a233636393036303b6f7061636974793a3830253b7d2363367b66696c6c3a233636393036303b6f7061636974793a313030253b7d2363317b66696c6c3a234545323032453b6f7061636974793a3830253b7d2363327b66696c6c3a234545323032453b6f7061636974793a313030253b7d2363337b66696c6c3a234635453144333b6f7061636974793a3830253b7d2363347b66696c6c3a234635453144333b6f7061636974793a313030253b7d2363357b66696c6c3a233333333133323b6f7061636974793a3830253b7d2363367b66696c6c3a233333333133323b6f7061636974793a313030253b7d2363317b66696c6c3a234536303032303b6f7061636974793a3135253b7d2363327b66696c6c3a234536303032303b6f7061636974793a3330253b7d2363337b66696c6c3a234536303032303b6f7061636974793a3435253b7d2363347b66696c6c3a234536303032303b6f7061636974793a3630253b7d2363357b66696c6c3a234536303032303b6f7061636974793a3735253b7d2363367b66696c6c3a234536303032303b6f7061636974793a3930253b7d2363317b66696c6c3a234646463033323b6f7061636974793a313030253b7d2363327b66696c6c3a233030334536413b6f7061636974793a313030253b7d2363337b66696c6c3a233445344534393b6f7061636974793a313030253b7d2363347b66696c6c3a233445344534393b6f7061636974793a313030253b7d2363357b66696c6c3a233030334536413b6f7061636974793a313030253b7d2363367b66696c6c3a234646463033323b6f7061636974793a313030253b7d2363317b66696c6c3a234231374535343b6f7061636974793a3135253b7d2363327b66696c6c3a234132364334313b6f7061636974793a3330253b7d2363337b66696c6c3a233935354433313b6f7061636974793a3435253b7d2363347b66696c6c3a233837344431463b6f7061636974793a3630253b7d2363357b66696c6c3a233738334530433b6f7061636974793a3735253b7d2363367b66696c6c3a233641333130303b6f7061636974793a3930253b7d2363317b66696c6c3a234642384330353b6f7061636974793a313030253b7d2363327b66696c6c3a233939303033373b6f7061636974793a313030253b7d2363337b66696c6c3a233030333135453b6f7061636974793a313030253b7d2363347b66696c6c3a233030333135453b6f7061636974793a313030253b7d2363357b66696c6c3a233939303033373b6f7061636974793a313030253b7d2363367b66696c6c3a234642384330353b6f7061636974793a313030253b7d2363317b66696c6c3a234631363639303b6f7061636974793a3830253b7d2363327b66696c6c3a234631363639303b6f7061636974793a313030253b7d2363337b66696c6c3a234546433341333b6f7061636974793a3830253b7d2363347b66696c6c3a234546433341333b6f7061636974793a313030253b7d2363357b66696c6c3a234646434630313b6f7061636974793a3830253b7d2363367b66696c6c3a234646434630313b6f7061636974793a313030253b7d2363317b66696c6c3a234235384237463b6f7061636974793a313030253b7d2363327b66696c6c3a233141313831383b6f7061636974793a313030253b7d2363337b66696c6c3a233030413441353b6f7061636974793a313030253b7d2363347b66696c6c3a233030413441353b6f7061636974793a313030253b7d2363357b66696c6c3a233141313831383b6f7061636974793a313030253b7d2363367b66696c6c3a234235384237463b6f7061636974793a313030253b7d2363317b66696c6c3a233233314632303b6f7061636974793a3135253b7d2363327b66696c6c3a233233314632303b6f7061636974793a3330253b7d2363337b66696c6c3a233233314632303b6f7061636974793a3435253b7d2363347b66696c6c3a233233314632303b6f7061636974793a3630253b7d2363357b66696c6c3a233233314632303b6f7061636974793a3735253b7d2363367b66696c6c3a233233314632303b6f7061636974793a3930253b7d2363317b66696c6c3a234546343237373b6f7061636974793a3830253b7d2363327b66696c6c3a234546343237373b6f7061636974793a313030253b7d2363337b66696c6c3a233236413139313b6f7061636974793a3830253b7d2363347b66696c6c3a233236413139313b6f7061636974793a313030253b7d2363357b66696c6c3a234646434630313b6f7061636974793a3830253b7d2363367b66696c6c3a234646434630313b6f7061636974793a313030253b7d2363317b66696c6c3a234641384132343b6f7061636974793a313030253b7d2363327b66696c6c3a233141313831383b6f7061636974793a313030253b7d2363337b66696c6c3a234536303032303b6f7061636974793a313030253b7d2363347b66696c6c3a234536303032303b6f7061636974793a313030253b7d2363357b66696c6c3a233141313831383b6f7061636974793a313030253b7d2363367b66696c6c3a234641384132343b6f7061636974793a313030253b7d2363317b66696c6c3a234631354432413b6f7061636974793a3830253b7d2363327b66696c6c3a234631354432413b6f7061636974793a313030253b7d2363337b66696c6c3a234232413941373b6f7061636974793a3830253b7d2363347b66696c6c3a234232413941373b6f7061636974793a313030253b7d2363357b66696c6c3a233244364342353b6f7061636974793a3830253b7d2363367b66696c6c3a233244364342353b6f7061636974793a313030253b7d2363317b66696c6c3a233536383836313b6f7061636974793a313030253b7d2363327b66696c6c3a233435374235333b6f7061636974793a313030253b7d2363337b66696c6c3a233332364534333b6f7061636974793a313030253b7d2363347b66696c6c3a233230363133343b6f7061636974793a313030253b7d2363357b66696c6c3a233030353532363b6f7061636974793a313030253b7d2363367b66696c6c3a233030344231433b6f7061636974793a313030253b7d2363317b66696c6c3a234646434630313b6f7061636974793a3135253b7d2363327b66696c6c3a234646434630313b6f7061636974793a3330253b7d2363337b66696c6c3a234646434630313b6f7061636974793a3435253b7d2363347b66696c6c3a234646434630313b6f7061636974793a3630253b7d2363357b66696c6c3a234646434630313b6f7061636974793a3735253b7d2363367b66696c6c3a234646434630313b6f7061636974793a3930253b7d2363317b66696c6c3a233030323835303b6f7061636974793a3135253b7d2363327b66696c6c3a233030323835303b6f7061636974793a3330253b7d2363337b66696c6c3a233030323835303b6f7061636974793a3435253b7d2363347b66696c6c3a233030323835303b6f7061636974793a3630253b7d2363357b66696c6c3a233030323835303b6f7061636974793a3735253b7d2363367b66696c6c3a233030323835303b6f7061636974793a3930253b7d
Deployed Bytecode
0x60806040526004361061020e575f3560e01c80638462151c11610117578063bd296d231161009f578063d89135cd1161006e578063d89135cd1461078a578063d9e54e24146107b4578063dac2e804146107de578063e985e9c514610808578063f2fde38b146108445761020e565b8063bd296d23146106c2578063c23dc68f146106ea578063c87b56dd14610726578063d1f7a9c5146107625761020e565b806399a2557a116100e657806399a2557a146105ea578063a0712d6814610626578063a22cb46514610642578063b0dc78fa1461066a578063b88d4fde146106a65761020e565b80638462151c146105305780638a0275fe1461056c5780638da5cb5b1461059657806395d89b41146105c05761020e565b806342842e0e1161019a5780635bbb2177116101695780635bbb21771461042a5780636352211e146104665780636511cc77146104a257806370a08231146104de578063715018a61461051a5761020e565b806342842e0e1461036a57806342966c68146103865780634f64b2be146103ae5780635a72bfa9146103ee5761020e565b806318160ddd116101e157806318160ddd146102d057806323b872dd146102fa578063379607f5146103165780633ccfd60b1461033e5780634063151d146103545761020e565b806301ffc9a71461021257806306fdde031461024e578063081812fc14610278578063095ea7b3146102b4575b5f80fd5b34801561021d575f80fd5b5061023860048036038101906102339190613732565b61086c565b6040516102459190613777565b60405180910390f35b348015610259575f80fd5b506102626108fd565b60405161026f919061381a565b60405180910390f35b348015610283575f80fd5b5061029e6004803603810190610299919061386d565b61098d565b6040516102ab91906138d7565b60405180910390f35b6102ce60048036038101906102c9919061391a565b610a07565b005b3480156102db575f80fd5b506102e4610b46565b6040516102f19190613967565b60405180910390f35b610314600480360381019061030f9190613980565b610b5b565b005b348015610321575f80fd5b5061033c6004803603810190610337919061386d565b610e69565b005b348015610349575f80fd5b50610352610fe1565b005b34801561035f575f80fd5b506103686110ea565b005b610384600480360381019061037f9190613980565b611261565b005b348015610391575f80fd5b506103ac60048036038101906103a7919061386d565b611280565b005b3480156103b9575f80fd5b506103d460048036038101906103cf919061386d565b61128e565b6040516103e5959493929190613acf565b60405180910390f35b3480156103f9575f80fd5b50610414600480360381019061040f919061386d565b611457565b6040516104219190613777565b60405180910390f35b348015610435575f80fd5b50610450600480360381019061044b9190613b8f565b611474565b60405161045d9190613d23565b60405180910390f35b348015610471575f80fd5b5061048c6004803603810190610487919061386d565b611534565b60405161049991906138d7565b60405180910390f35b3480156104ad575f80fd5b506104c860048036038101906104c39190613d43565b611545565b6040516104d59190613777565b60405180910390f35b3480156104e9575f80fd5b5061050460048036038101906104ff9190613d43565b611562565b6040516105119190613967565b60405180910390f35b348015610525575f80fd5b5061052e611617565b005b34801561053b575f80fd5b5061055660048036038101906105519190613d43565b61162a565b6040516105639190613e16565b60405180910390f35b348015610577575f80fd5b50610580611766565b60405161058d9190613967565b60405180910390f35b3480156105a1575f80fd5b506105aa61176c565b6040516105b791906138d7565b60405180910390f35b3480156105cb575f80fd5b506105d4611794565b6040516105e1919061381a565b60405180910390f35b3480156105f5575f80fd5b50610610600480360381019061060b9190613e36565b611824565b60405161061d9190613e16565b60405180910390f35b610640600480360381019061063b919061386d565b611a23565b005b34801561064d575f80fd5b5061066860048036038101906106639190613eb0565b611bf8565b005b348015610675575f80fd5b50610690600480360381019061068b919061386d565b611cfe565b60405161069d919061381a565b60405180910390f35b6106c060048036038101906106bb9190614016565b611db3565b005b3480156106cd575f80fd5b506106e860048036038101906106e39190614209565b611e25565b005b3480156106f5575f80fd5b50610710600480360381019061070b919061386d565b6122d9565b60405161071d9190614305565b60405180910390f35b348015610731575f80fd5b5061074c6004803603810190610747919061386d565b612343565b604051610759919061381a565b60405180910390f35b34801561076d575f80fd5b5061078860048036038101906107839190614373565b6123fa565b005b348015610795575f80fd5b5061079e6124de565b6040516107ab9190613967565b60405180910390f35b3480156107bf575f80fd5b506107c86124ec565b6040516107d59190613967565b60405180910390f35b3480156107e9575f80fd5b506107f2612560565b6040516107ff9190613967565b60405180910390f35b348015610813575f80fd5b5061082e600480360381019061082991906143be565b612566565b60405161083b9190613777565b60405180910390f35b34801561084f575f80fd5b5061086a60048036038101906108659190613d43565b6125f4565b005b5f6301ffc9a760e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806108c657506380ac58cd60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806108f65750635b5e139f60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b60606002805461090c90614429565b80601f016020809104026020016040519081016040528092919081815260200182805461093890614429565b80156109835780601f1061095a57610100808354040283529160200191610983565b820191905f5260205f20905b81548152906001019060200180831161096657829003601f168201915b5050505050905090565b5f61099782612678565b6109cd576040517fcf4700e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60065f8381526020019081526020015f205f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b5f610a1182611534565b90508073ffffffffffffffffffffffffffffffffffffffff16610a326126d2565b73ffffffffffffffffffffffffffffffffffffffff1614610a9557610a5e81610a596126d2565b612566565b610a94576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b8260065f8481526020019081526020015f205f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b5f610b4f6126d9565b6001545f540303905090565b5f610b65826126e1565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610bcc576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80610bd7846127a4565b91509150610bed8187610be86126d2565b6127c7565b610c3957610c0286610bfd6126d2565b612566565b610c38576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603610c9e576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cab868686600161280a565b8015610cb5575f82555b60055f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8154600190039190508190555060055f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f815460010191905081905550610d7d85610d5988888761286a565b7c020000000000000000000000000000000000000000000000000000000017612891565b60045f8681526020019081526020015f20819055505f7c0200000000000000000000000000000000000000000000000000000000841603610df9575f6001850190505f60045f8381526020019081526020015f205403610df7575f548114610df6578360045f8381526020019081526020015f20819055505b5b505b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4610e6186868660016128bb565b505050505050565b3373ffffffffffffffffffffffffffffffffffffffff1673f9f27aa7718dccf4b4306e0321c2ce85215fa90273ffffffffffffffffffffffffffffffffffffffff16636352211e836040518263ffffffff1660e01b8152600401610ecd9190613967565b602060405180830381865afa158015610ee8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f0c919061446d565b73ffffffffffffffffffffffffffffffffffffffff16148015610f4a5750600d5f8281526020019081526020015f205f9054906101000a900460ff16155b8015610f5c57505f610f5a6124ec565b115b8015610f6f57506004610f6d6124ec565b105b15610fac57610f7e60016128c1565b6001600d5f8381526020019081526020015f205f6101000a81548160ff021916908315150217905550610fde565b6040517f3c21f90f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b610fe96129c2565b5f4790505f8103611026576040517f669567ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60028261103491906144f2565b90505f81836110439190614522565b9050739390333197446ec19b1b4dc7dc7fc5ae4957ebba73ffffffffffffffffffffffffffffffffffffffff166108fc8390811502906040515f60405180830381858888f1935050505080156110dd57507338f0dbba41258639ba0b2cddc753e46157ce14d073ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050505b6110e5575f80fd5b505050565b6110f26129c2565b60046110fc6124ec565b10806111145750600960149054906101000a900460ff165b1561114b576040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b60c8811015611201575f6111846107e8836111689190614555565b60016103e86111779190614555565b61117f612a49565b612a5a565b9050600c5f8281526020019081526020015f205f015f9054906101000a900460ff161580156111c857506007600c5f8381526020019081526020015f206002015414155b156111f3576111da826008600a612a5a565b600c5f8381526020019081526020015f20600201819055505b50808060010191505061114d565b506001600960146101000a81548160ff0219169083151502179055507f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c6103e8611249612a49565b604051611257929190614588565b60405180910390a1565b61127b83838360405180602001604052805f815250611db3565b505050565b61128b816001612abf565b50565b600c602052805f5260405f205f91509050805f015f9054906101000a900460ff16908060010154908060020154908060030180546112cb90614429565b80601f01602080910402602001604051908101604052809291908181526020018280546112f790614429565b80156113425780601f1061131957610100808354040283529160200191611342565b820191905f5260205f20905b81548152906001019060200180831161132557829003601f168201915b505050505090806004016040518060e00160405290815f82015f9054906101000a900460ff161515151581526020015f820160019054906101000a900460ff161515151581526020015f820160029054906101000a900460ff161515151581526020016001820180546113b490614429565b80601f01602080910402602001604051908101604052809291908181526020018280546113e090614429565b801561142b5780601f106114025761010080835404028352916020019161142b565b820191905f5260205f20905b81548152906001019060200180831161140e57829003601f168201915b505050505081526020016002820154815260200160038201548152602001600482015481525050905085565b600d602052805f5260405f205f915054906101000a900460ff1681565b60605f8383905090505f8167ffffffffffffffff81111561149857611497613ef2565b5b6040519080825280602002602001820160405280156114d157816020015b6114be613681565b8152602001906001900390816114b65790505b5090505f5b828114611528576114ff8686838181106114f3576114f26145af565b5b905060200201356122d9565b828281518110611512576115116145af565b5b60200260200101819052508060010190506114d6565b50809250505092915050565b5f61153e826126e1565b9050919050565b600e602052805f5260405f205f915054906101000a900460ff1681565b5f8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036115c8576040517f8f4eb60400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff60055f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054169050919050565b61161f6129c2565b6116285f612cfb565b565b60605f805f61163885611562565b90505f8167ffffffffffffffff81111561165557611654613ef2565b5b6040519080825280602002602001820160405280156116835781602001602082028036833780820191505090505b50905061168e613681565b5f6116976126d9565b90505b838614611758576116aa81612dbe565b9150816040015161174d575f73ffffffffffffffffffffffffffffffffffffffff16825f015173ffffffffffffffffffffffffffffffffffffffff16146116f257815f015194505b8773ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff160361174c578083878060010198508151811061173f5761173e6145af565b5b6020026020010181815250505b5b80600101905061169a565b508195505050505050919050565b600b5481565b5f60085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600380546117a390614429565b80601f01602080910402602001604051908101604052809291908181526020018280546117cf90614429565b801561181a5780601f106117f15761010080835404028352916020019161181a565b820191905f5260205f20905b8154815290600101906020018083116117fd57829003601f168201915b5050505050905090565b606081831061185f576040517f32c1995a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80611869612de7565b90506118736126d9565b851015611885576118826126d9565b94505b80841115611891578093505b5f61189b87611562565b9050848610156118bd575f8686039050818110156118b7578091505b506118c1565b5f90505b5f8167ffffffffffffffff8111156118dc576118db613ef2565b5b60405190808252806020026020018201604052801561190a5781602001602082028036833780820191505090505b5090505f82036119205780945050505050611a1c565b5f61192a886122d9565b90505f816040015161193d57815f015190505b5f8990505b8881141580156119525750848714155b15611a0e5761196081612dbe565b92508260400151611a03575f73ffffffffffffffffffffffffffffffffffffffff16835f015173ffffffffffffffffffffffffffffffffffffffff16146119a857825f015191505b8a73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611a0257808488806001019950815181106119f5576119f46145af565b5b6020026020010181815250505b5b806001019050611942565b508583528296505050505050505b9392505050565b5f611a2c6124ec565b90506611c37937e0800082611a4191906145dc565b341015611a7a576040517ff14a42b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028103611bab57600e5f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1680611b6157505f73f9f27aa7718dccf4b4306e0321c2ce85215fa90273ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401611b2091906138d7565b602060405180830381865afa158015611b3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b5f9190614631565b115b15611b7457611b6f826128c1565b611ba6565b6040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bf4565b60038103611bc157611bbc826128c1565b611bf3565b6040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5050565b8060075f611c046126d2565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16611cad6126d2565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611cf29190613777565b60405180910390a35050565b606060095f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663489320a4600c5f8581526020019081526020015f206040518263ffffffff1660e01b8152600401611d6a91906148f4565b5f60405180830381865afa158015611d84573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190611dac9190614982565b9050919050565b611dbe848484610b5b565b5f8373ffffffffffffffffffffffffffffffffffffffff163b14611e1f57611de884848484612def565b611e1e576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b50505050565b5f84845f818110611e3957611e386145af565b5b905060200201359050611e4d858533612f3a565b611e83576040517f97792c3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185859050036120c1576007600c5f8381526020019081526020015f2060020154118015611ec65750600b600c5f8381526020019081526020015f2060020154105b1561201657611edd83606001518460c00151612fc2565b611f13576040517f7a2a029c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6064611f1e8361306b565b1115611f56576040517f69ed1ea500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260600151600c5f8381526020019081526020015f206004016001019081611f7e9190614b54565b508260c00151600c5f8381526020019081526020015f206004016004018190555081600c5f8381526020019081526020015f206003019081611fc09190614b54565b50600c5f8281526020019081526020015f205f015f9054906101000a900460ff16612011576001600c5f8381526020019081526020015f205f015f6101000a81548160ff0219169083151502179055505b612085565b6103e88111806120425750600c5f8281526020019081526020015f205f015f9054906101000a900460ff165b15612079576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120848184846130d5565b5b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7816040516120b49190613967565b60405180910390a16122bb565b6005858590500361228857600c5f8281526020019081526020015f205f015f9054906101000a900460ff1615612123576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b600581101561228257612164600c5f888885818110612147576121466145af565b5b9050602002013581526020019081526020015f2060020154613369565b1561219b576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818686838181106121af576121ae6145af565b5b9050602002013503612202576121c68285856130d5565b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7826040516121f59190613967565b60405180910390a1612275565b612224868683818110612218576122176145af565b5b90506020020135611280565b7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7868683818110612258576122576145af565b5b9050602002013560405161226c9190613967565b60405180910390a15b8080600101915050612125565b506122ba565b6040517f524f409b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b600b5f8154809291906122cd90614c23565b91905055505050505050565b6122e1613681565b6122e9613681565b6122f16126d9565b8310806123055750612301612de7565b8310155b15612313578091505061233e565b61231c83612dbe565b9050806040015115612331578091505061233e565b61233a836133a1565b9150505b919050565b606060095f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a3915e6583600c5f8681526020019081526020015f206040518363ffffffff1660e01b81526004016123b1929190614c6a565b5f60405180830381865afa1580156123cb573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906123f39190614982565b9050919050565b6124026129c2565b5f61240b6124ec565b1115612443576040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b828290508110156124d9576001600e5f858585818110612468576124676145af565b5b905060200201602081019061247d9190613d43565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508080600101915050612445565b505050565b5f6124e76133c1565b905090565b5f6365ae74e0421015612501575f905061255d565b612a306365ae74e06125139190614555565b421015612523576001905061255d565b6162706365ae74e06125359190614555565b421015612545576002905061255d565b600a54421015612558576003905061255d565b600490505b90565b600a5481565b5f60075f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b6125fc6129c2565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361266c575f6040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161266391906138d7565b60405180910390fd5b61267581612cfb565b50565b5f816126826126d9565b1115801561269057505f5482105b80156126cb57505f7c010000000000000000000000000000000000000000000000000000000060045f8581526020019081526020015f205416145b9050919050565b5f33905090565b5f6001905090565b5f80829050806126ef6126d9565b1161276d575f5481101561276c575f60045f8381526020019081526020015f205490505f7c010000000000000000000000000000000000000000000000000000000082160361276a575b5f81036127605760045f836001900393508381526020019081526020015f20549050612739565b809250505061279f565b505b5b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b5f805f60065f8581526020019081526020015f2090508092508254915050915091565b5f73ffffffffffffffffffffffffffffffffffffffff8316925073ffffffffffffffffffffffffffffffffffffffff821691508382148383141790509392505050565b612816848484846133ca565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603612864576007600c5f8481526020019081526020015f20600201819055505b50505050565b5f8060e883901c905060e86128808686846133d0565b62ffffff16901b9150509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff83169250814260a01b178317905092915050565b50505050565b5f5b8181101561294c575f816128d5612de7565b6128df9190614555565b90506103e88111156129145761290f8161290742846128fe9190614555565b60016006612a5a565b60ff166133d8565b61293e565b6103e88103612933576203f4804261292c9190614555565b600a819055505b61293d815f6133d8565b5b5080806001019150506128c3565b50600a81116129645761295f338261340e565b6129bf565b5f5b600a8261297391906144f2565b8110156129925761298533600a61340e565b8080600101915050612966565b505f600a826129a19190614c98565b11156129be576129bd33600a836129b89190614c98565b61340e565b5b5b50565b6129ca6135b7565b73ffffffffffffffffffffffffffffffffffffffff166129e861176c565b73ffffffffffffffffffffffffffffffffffffffff1614612a4757612a0b6135b7565b6040517f118cdaa7000000000000000000000000000000000000000000000000000000008152600401612a3e91906138d7565b60405180910390fd5b565b5f612a526126d9565b5f5403905090565b5f8060018484612a6a9190614522565b612a749190614555565b90508085604051602001612a889190614ce8565b604051602081830303815290604052805190602001205f1c612aaa9190614c98565b84612ab59190614555565b9150509392505050565b5f612ac9836126e1565b90505f8190505f80612ada866127a4565b915091508415612b4357612af68184612af16126d2565b6127c7565b612b4257612b0b83612b066126d2565b612566565b612b41576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5b612b50835f88600161280a565b8015612b5a575f82555b600160806001901b0360055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282540192505081905550612bfe83612bbb855f8861286a565b7c02000000000000000000000000000000000000000000000000000000007c01000000000000000000000000000000000000000000000000000000001717612891565b60045f8881526020019081526020015f20819055505f7c0200000000000000000000000000000000000000000000000000000000851603612c7a575f6001870190505f60045f8381526020019081526020015f205403612c78575f548114612c77578460045f8381526020019081526020015f20819055505b5b505b855f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4612ce2835f8860016128bb565b60015f8154809291906001019190505550505050505050565b5f60085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b612dc6613681565b612de060045f8481526020019081526020015f20546135be565b9050919050565b5f8054905090565b5f8373ffffffffffffffffffffffffffffffffffffffff1663150b7a02612e146126d2565b8786866040518563ffffffff1660e01b8152600401612e369493929190614d54565b6020604051808303815f875af1925050508015612e7157506040513d601f19601f82011682018060405250810190612e6e9190614db2565b60015b612ee7573d805f8114612e9f576040519150601f19603f3d011682016040523d82523d5f602084013e612ea4565b606091505b505f815103612edf576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050949350505050565b5f805f90505b84849050811015612fb5578273ffffffffffffffffffffffffffffffffffffffff16612f84868684818110612f7857612f776145af565b5b90506020020135611534565b73ffffffffffffffffffffffffffffffffffffffff1614612fa8575f915050612fbb565b8080600101915050612f40565b50600190505b9392505050565b5f6006821080612fd25750601d82115b15612fdf575f9050613065565b5f612fe98461306b565b90505f60078211612ffd57601d905061305c565b6010821161300e57600b905061305b565b6016821161301f576009905061305a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161305190614e27565b60405180910390fd5b5b5b80841115925050505b92915050565b5f805f90505f5b83518110156130cb57608060c0858381518110613092576130916145af565b5b602001015160f81c60f81b60f81c1660ff16146130b85781806130b490614c23565b9250505b80806130c390614c23565b915050613072565b8192505050919050565b600f82608001511115613114576040517f02e14b0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60078260a001511115613153576040517f3d36cb8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61316582606001518360c00151612fc2565b61319b576040517f7a2a029c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60646131a68261306b565b11156131de576040517f69ed1ea500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600c5f8581526020019081526020015f2060030190816131ff9190614b54565b506040518060e00160405280835f01511515815260200183602001511515815260200183604001511515815260200183606001518152602001836080015181526020018360a0015181526020018360c00151815250600c5f8581526020019081526020015f206004015f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff0219169083151502179055506040820151815f0160026101000a81548160ff02191690831515021790555060608201518160010190816132d89190614b54565b506080820151816002015560a0820151816003015560c08201518160040155905050600c5f8481526020019081526020015f205f015f9054906101000a900460ff16613364576001600c5f8581526020019081526020015f205f015f6101000a81548160ff021916908315150217905550600b600c5f8581526020019081526020015f20600201819055505b505050565b5f80820361337a576001905061339c565b60078211801561338a5750600b82105b15613398576001905061339c565b5f90505b919050565b6133a9613681565b6133ba6133b5836126e1565b6135be565b9050919050565b5f600154905090565b50505050565b5f9392505050565b80600c5f8481526020019081526020015f206001018190555080600c5f8481526020019081526020015f20600201819055505050565b5f805490505f820361344c576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134585f84838561280a565b600160406001901b17820260055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055506134ca836134bb5f865f61286a565b6134c485613672565b17612891565b60045f8381526020019081526020015f20819055505f80838301905073ffffffffffffffffffffffffffffffffffffffff8516915082825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4600183015b8181146135645780835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a460018101905061352b565b505f820361359e576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f8190555050506135b25f8483856128bb565b505050565b5f33905090565b6135c6613681565b81815f019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060a082901c816020019067ffffffffffffffff16908167ffffffffffffffff16815250505f7c01000000000000000000000000000000000000000000000000000000008316141581604001901515908115158152505060e882901c816060019062ffffff16908162ffffff1681525050919050565b5f6001821460e11b9050919050565b60405180608001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f67ffffffffffffffff1681526020015f151581526020015f62ffffff1681525090565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b613711816136dd565b811461371b575f80fd5b50565b5f8135905061372c81613708565b92915050565b5f60208284031215613747576137466136d5565b5b5f6137548482850161371e565b91505092915050565b5f8115159050919050565b6137718161375d565b82525050565b5f60208201905061378a5f830184613768565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156137c75780820151818401526020810190506137ac565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6137ec82613790565b6137f6818561379a565b93506138068185602086016137aa565b61380f816137d2565b840191505092915050565b5f6020820190508181035f83015261383281846137e2565b905092915050565b5f819050919050565b61384c8161383a565b8114613856575f80fd5b50565b5f8135905061386781613843565b92915050565b5f60208284031215613882576138816136d5565b5b5f61388f84828501613859565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6138c182613898565b9050919050565b6138d1816138b7565b82525050565b5f6020820190506138ea5f8301846138c8565b92915050565b6138f9816138b7565b8114613903575f80fd5b50565b5f81359050613914816138f0565b92915050565b5f80604083850312156139305761392f6136d5565b5b5f61393d85828601613906565b925050602061394e85828601613859565b9150509250929050565b6139618161383a565b82525050565b5f60208201905061397a5f830184613958565b92915050565b5f805f60608486031215613997576139966136d5565b5b5f6139a486828701613906565b93505060206139b586828701613906565b92505060406139c686828701613859565b9150509250925092565b6139d98161375d565b82525050565b5f82825260208201905092915050565b5f6139f982613790565b613a0381856139df565b9350613a138185602086016137aa565b613a1c816137d2565b840191505092915050565b613a308161383a565b82525050565b5f60e083015f830151613a4b5f8601826139d0565b506020830151613a5e60208601826139d0565b506040830151613a7160408601826139d0565b5060608301518482036060860152613a8982826139ef565b9150506080830151613a9e6080860182613a27565b5060a0830151613ab160a0860182613a27565b5060c0830151613ac460c0860182613a27565b508091505092915050565b5f60a082019050613ae25f830188613768565b613aef6020830187613958565b613afc6040830186613958565b8181036060830152613b0e81856137e2565b90508181036080830152613b228184613a36565b90509695505050505050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112613b4f57613b4e613b2e565b5b8235905067ffffffffffffffff811115613b6c57613b6b613b32565b5b602083019150836020820283011115613b8857613b87613b36565b5b9250929050565b5f8060208385031215613ba557613ba46136d5565b5b5f83013567ffffffffffffffff811115613bc257613bc16136d9565b5b613bce85828601613b3a565b92509250509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b613c0c816138b7565b82525050565b5f67ffffffffffffffff82169050919050565b613c2e81613c12565b82525050565b5f62ffffff82169050919050565b613c4b81613c34565b82525050565b608082015f820151613c655f850182613c03565b506020820151613c786020850182613c25565b506040820151613c8b60408501826139d0565b506060820151613c9e6060850182613c42565b50505050565b5f613caf8383613c51565b60808301905092915050565b5f602082019050919050565b5f613cd182613bda565b613cdb8185613be4565b9350613ce683613bf4565b805f5b83811015613d16578151613cfd8882613ca4565b9750613d0883613cbb565b925050600181019050613ce9565b5085935050505092915050565b5f6020820190508181035f830152613d3b8184613cc7565b905092915050565b5f60208284031215613d5857613d576136d5565b5b5f613d6584828501613906565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f613da28383613a27565b60208301905092915050565b5f602082019050919050565b5f613dc482613d6e565b613dce8185613d78565b9350613dd983613d88565b805f5b83811015613e09578151613df08882613d97565b9750613dfb83613dae565b925050600181019050613ddc565b5085935050505092915050565b5f6020820190508181035f830152613e2e8184613dba565b905092915050565b5f805f60608486031215613e4d57613e4c6136d5565b5b5f613e5a86828701613906565b9350506020613e6b86828701613859565b9250506040613e7c86828701613859565b9150509250925092565b613e8f8161375d565b8114613e99575f80fd5b50565b5f81359050613eaa81613e86565b92915050565b5f8060408385031215613ec657613ec56136d5565b5b5f613ed385828601613906565b9250506020613ee485828601613e9c565b9150509250929050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b613f28826137d2565b810181811067ffffffffffffffff82111715613f4757613f46613ef2565b5b80604052505050565b5f613f596136cc565b9050613f658282613f1f565b919050565b5f67ffffffffffffffff821115613f8457613f83613ef2565b5b613f8d826137d2565b9050602081019050919050565b828183375f83830152505050565b5f613fba613fb584613f6a565b613f50565b905082815260208101848484011115613fd657613fd5613eee565b5b613fe1848285613f9a565b509392505050565b5f82601f830112613ffd57613ffc613b2e565b5b813561400d848260208601613fa8565b91505092915050565b5f805f806080858703121561402e5761402d6136d5565b5b5f61403b87828801613906565b945050602061404c87828801613906565b935050604061405d87828801613859565b925050606085013567ffffffffffffffff81111561407e5761407d6136d9565b5b61408a87828801613fe9565b91505092959194509250565b5f80fd5b5f80fd5b5f67ffffffffffffffff8211156140b8576140b7613ef2565b5b6140c1826137d2565b9050602081019050919050565b5f6140e06140db8461409e565b613f50565b9050828152602081018484840111156140fc576140fb613eee565b5b614107848285613f9a565b509392505050565b5f82601f83011261412357614122613b2e565b5b81356141338482602086016140ce565b91505092915050565b5f60e0828403121561415157614150614096565b5b61415b60e0613f50565b90505f61416a84828501613e9c565b5f83015250602061417d84828501613e9c565b602083015250604061419184828501613e9c565b604083015250606082013567ffffffffffffffff8111156141b5576141b461409a565b5b6141c18482850161410f565b60608301525060806141d584828501613859565b60808301525060a06141e984828501613859565b60a08301525060c06141fd84828501613859565b60c08301525092915050565b5f805f8060608587031215614221576142206136d5565b5b5f85013567ffffffffffffffff81111561423e5761423d6136d9565b5b61424a87828801613b3a565b9450945050602085013567ffffffffffffffff81111561426d5761426c6136d9565b5b6142798782880161413c565b925050604085013567ffffffffffffffff81111561429a576142996136d9565b5b6142a68782880161410f565b91505092959194509250565b608082015f8201516142c65f850182613c03565b5060208201516142d96020850182613c25565b5060408201516142ec60408501826139d0565b5060608201516142ff6060850182613c42565b50505050565b5f6080820190506143185f8301846142b2565b92915050565b5f8083601f84011261433357614332613b2e565b5b8235905067ffffffffffffffff8111156143505761434f613b32565b5b60208301915083602082028301111561436c5761436b613b36565b5b9250929050565b5f8060208385031215614389576143886136d5565b5b5f83013567ffffffffffffffff8111156143a6576143a56136d9565b5b6143b28582860161431e565b92509250509250929050565b5f80604083850312156143d4576143d36136d5565b5b5f6143e185828601613906565b92505060206143f285828601613906565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061444057607f821691505b602082108103614453576144526143fc565b5b50919050565b5f81519050614467816138f0565b92915050565b5f60208284031215614482576144816136d5565b5b5f61448f84828501614459565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6144fc8261383a565b91506145078361383a565b92508261451757614516614498565b5b828204905092915050565b5f61452c8261383a565b91506145378361383a565b925082820390508181111561454f5761454e6144c5565b5b92915050565b5f61455f8261383a565b915061456a8361383a565b9250828201905080821115614582576145816144c5565b5b92915050565b5f60408201905061459b5f830185613958565b6145a86020830184613958565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6145e68261383a565b91506145f18361383a565b92508282026145ff8161383a565b91508282048414831517614616576146156144c5565b5b5092915050565b5f8151905061462b81613843565b92915050565b5f60208284031215614646576146456136d5565b5b5f6146538482850161461d565b91505092915050565b5f815f1c9050919050565b5f60ff82169050919050565b5f6146856146808361465c565b614667565b9050919050565b5f819050919050565b5f6146a76146a28361465c565b61468c565b9050919050565b5f819050815f5260205f209050919050565b5f81546146cc81614429565b6146d681866139df565b9450600182165f81146146f0576001811461470657614738565b60ff198316865281151560200286019350614738565b61470f856146ae565b5f5b8381101561473057815481890152600182019150602081019050614711565b808801955050505b50505092915050565b5f8160081c9050919050565b5f61475f61475a83614741565b614667565b9050919050565b5f8160101c9050919050565b5f61478461477f83614766565b614667565b9050919050565b5f60e083015f8084015490506147a081614673565b6147ac5f8701826139d0565b506147b68161474d565b6147c360208701826139d0565b506147cd81614772565b6147da60408701826139d0565b506001840185830360608701526147f183826146c0565b9250506002840154905061480481614695565b6148116080870182613a27565b506003840154905061482281614695565b61482f60a0870182613a27565b506004840154905061484081614695565b61484d60c0870182613a27565b50819250505092915050565b5f60a083015f80840154905061486e81614673565b61487a5f8701826139d0565b506001840154905061488b81614695565b6148986020870182613a27565b50600284015490506148a981614695565b6148b66040870182613a27565b506003840185830360608701526148cd83826146c0565b9250506004840185830360808701526148e6838261478b565b925050819250505092915050565b5f6020820190508181035f83015261490c8184614859565b905092915050565b5f6149266149218461409e565b613f50565b90508281526020810184848401111561494257614941613eee565b5b61494d8482856137aa565b509392505050565b5f82601f83011261496957614968613b2e565b5b8151614979848260208601614914565b91505092915050565b5f60208284031215614997576149966136d5565b5b5f82015167ffffffffffffffff8111156149b4576149b36136d9565b5b6149c084828501614955565b91505092915050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302614a137fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826149d8565b614a1d86836149d8565b95508019841693508086168417925050509392505050565b5f819050919050565b5f614a58614a53614a4e8461383a565b614a35565b61383a565b9050919050565b5f819050919050565b614a7183614a3e565b614a85614a7d82614a5f565b8484546149e4565b825550505050565b5f90565b614a99614a8d565b614aa4818484614a68565b505050565b5b81811015614ac757614abc5f82614a91565b600181019050614aaa565b5050565b601f821115614b0c57614add816146ae565b614ae6846149c9565b81016020851015614af5578190505b614b09614b01856149c9565b830182614aa9565b50505b505050565b5f82821c905092915050565b5f614b2c5f1984600802614b11565b1980831691505092915050565b5f614b448383614b1d565b9150826002028217905092915050565b614b5d82613790565b67ffffffffffffffff811115614b7657614b75613ef2565b5b614b808254614429565b614b8b828285614acb565b5f60209050601f831160018114614bbc575f8415614baa578287015190505b614bb48582614b39565b865550614c1b565b601f198416614bca866146ae565b5f5b82811015614bf157848901518255600182019150602085019450602081019050614bcc565b86831015614c0e5784890151614c0a601f891682614b1d565b8355505b6001600288020188555050505b505050505050565b5f614c2d8261383a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614c5f57614c5e6144c5565b5b600182019050919050565b5f604082019050614c7d5f830185613958565b8181036020830152614c8f8184614859565b90509392505050565b5f614ca28261383a565b9150614cad8361383a565b925082614cbd57614cbc614498565b5b828206905092915050565b5f819050919050565b614ce2614cdd8261383a565b614cc8565b82525050565b5f614cf38284614cd1565b60208201915081905092915050565b5f81519050919050565b5f82825260208201905092915050565b5f614d2682614d02565b614d308185614d0c565b9350614d408185602086016137aa565b614d49816137d2565b840191505092915050565b5f608082019050614d675f8301876138c8565b614d7460208301866138c8565b614d816040830185613958565b8181036060830152614d938184614d1c565b905095945050505050565b5f81519050614dac81613708565b92915050565b5f60208284031215614dc757614dc66136d5565b5b5f614dd484828501614d9e565b91505092915050565b7f696e76616c6964206c656e6774680000000000000000000000000000000000005f82015250565b5f614e11600e8361379a565b9150614e1c82614ddd565b602082019050919050565b5f6020820190508181035f830152614e3e81614e05565b905091905056fea2646970667358221220372f96efb9bf8fd2c063fd42d3ae70e20116b2a331f1377a5970c58b7675af5064736f6c63430008170033
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.