Spend less on fees, more on crypto. Buy crypto easily with MoonPay Balance. 20M+ users trust MoonPay worldwide.
Don’t invest unless you’re prepared to lose all the money you invest.
3000+ Slots, 20+ Cryptos, 75K Raffle, Sports Promos - World's largest Crypto Casino & Sportsbook - Provably Fair!
Play in crypto to make deposits and withdrawals easy! Register and get a free daily shot at a 100 000 $ jackpot.
Monthly Wagering Contest - $500,000+ rewards. Provably Fair, Low House Edge and best VIP Program!
Daily free Spin 50000 Matic ,760% Deposit Bonus, 20%Rakeback, And Get 1000000 Matic free bonus on BC.Game
Deposit BONUS 300% and Cashbacks. without verification!
Holidays are coming soon! Start betting on 1xBit and get a secret gift from Santa!
Overview
POL Balance
POL Value
$0.00Token Holdings
Could not find any matches!
- ERC-20 Tokens (33)43,767 AAVE [ACCESS AAVE.ASIA]ERC-20: ! AAVE.as... (AAVE [...)9,650 Access Airdrop Link [zksyon.one]ERC-20: ! Airdrop... (Access...)152,000 Fyde Points - www.fyde.pwERC-20: ! FYDE (Fyde P...)1 https://t.ly/ethersERC-20: deBridge ... (https:...)1 deBridge Airdrop https://t.ly/ethersERC-20: deBridge ... (deBrid...)1 https://t.ly/ethersERC-20: deBridge ... (https:...)14,000 $ YOUR LIDO ON:lid-ethen.com .ERC-20: LIDO ($ YOUR...)724,900 TokenERC-20 TOKEN*[Suspicious]745,900 TokenERC-20 TOKEN*[Suspicious]9,860 TokenERC-20 TOKEN*[Suspicious]9,543 TokenERC-20 TOKEN*[Suspicious]8,750 TokenERC-20 TOKEN*[Suspicious]7,800 TokenERC-20 TOKEN*[Suspicious]7,800 TokenERC-20 TOKEN*[Suspicious]900,000,000 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]3,750 TokenERC-20 TOKEN*[Spam]27,500 TokenERC-20 TOKEN*[Spam]550,000 TokenERC-20 TOKEN*[Spam]8,726 TokenERC-20 TOKEN*[Spam]8,400 TokenERC-20 TOKEN*[Spam]22,400 TokenERC-20 TOKEN*[Spam]8 TokenERC-20 TOKEN*[Spam]8,273 TokenERC-20 TOKEN*[Spam]8,273 TokenERC-20 TOKEN*[Spam]4,376 TokenERC-20 TOKEN*[Spam]85,600 TokenERC-20 TOKEN*[Spam]956 TokenERC-20 TOKEN*[Spam]9,600 TokenERC-20 TOKEN*[Spam]1 TokenERC-20 TOKEN*[Spam]1 TokenERC-20 TOKEN*[Spam]1 TokenERC-20 TOKEN*[Spam]25,053 TokenERC-20 TOKEN*[Spam]NFT Tokens (12)drop-usdt.xyz2000$ RewardERC-1155deBridge Airdrop https://t.ly/ethersdeBridge AirdropERC-721https://t.ly/ethersdeBridge Airdropx2ERC-721https://t.ly/ethersdeBridge AirdropERC-721
More Info
Private Name Tags
ContractCreator
TokenTracker
- Transactions
- Internal Transactions
- Token Transfers (ERC-20)
- NFT Transfers
- Contract
- Events
- Multichain Portfolio
- Info
Advanced Filter- Filter by Tx Type:
- Tx
- Internal Tx
- ERC-20
- NFTs
Latest 25 from a total of 112 transactions
Transaction Hash MethodBlockFromToMint Top Hat 62368945 2024-09-28 6:14:38 70 days ago 1727504078 IN 0 POL$0.00 0.00993636 35.00000003 Mint Top Hat 62367323 2024-09-28 5:17:04 70 days ago 1727500624 IN 0 POL$0.00 0.00851688 30.00000002 Mint Top Hat 62366289 2024-09-28 4:40:28 70 days ago 1727498428 IN 0 POL$0.00 0.00993636 35.00000002 Mint Top Hat 62366116 2024-09-28 4:34:20 70 days ago 1727498060 IN 0 POL$0.00 0.00851688 30.00000002 Mint Top Hat 62361826 2024-09-28 2:02:21 71 days ago 1727488941 IN 0 POL$0.00 0.00586308 30.00000003 Mint Top Hat 62358625 2024-09-28 0:08:29 71 days ago 1727482109 IN 0 POL$0.00 0.00851688 30.00000002 Multicall 62358341 2024-09-27 23:58:25 71 days ago 1727481505 IN 0 POL$0.00 0.01035702 30.00000002 Mint Top Hat 62358085 2024-09-27 23:49:20 71 days ago 1727480960 IN 0 POL$0.00 0.00585924 30.00000002 Mint Top Hat 61535576 2024-09-07 13:42:18 91 days ago 1725716538 IN 0 POL$0.00 0.00993636 35.00000003 Mint Top Hat 61534174 2024-09-07 12:52:37 91 days ago 1725713557 IN 0 POL$0.00 0.00851304 30.00000002 Multicall 59757444 2024-07-24 16:18:51 136 days ago 1721837931 IN 0 POL$0.00 0.00185316 30.00000005 Mint Top Hat 59757220 2024-07-24 16:10:55 136 days ago 1721837455 IN 0 POL$0.00 0.00851304 30.00000006 Mint Top Hat 58604022 2024-06-25 21:25:07 165 days ago 1719350707 IN 0 POL$0.00 0.00585924 30.00000002 Mint Top Hat 57063150 2024-05-17 7:47:12 204 days ago 1715932032 IN 0 POL$0.00 0.01293547 45.58469318 Multicall 56720325 2024-05-08 11:05:55 213 days ago 1715166355 IN 0 POL$0.00 0.00556062 30.00000006 Mint Top Hat 56720292 2024-05-08 11:04:33 213 days ago 1715166273 IN 0 POL$0.00 0.00585924 30.00000007 Mint Top Hat 56620895 2024-05-05 21:29:11 216 days ago 1714944551 IN 0 POL$0.00 0.00903632 31.84539102 Mint Top Hat 55740826 2024-04-12 14:42:58 239 days ago 1712932978 IN 0 POL$0.00 0.05535205 283.40905988 Mint Hat 55371161 2024-04-02 19:35:39 249 days ago 1712086539 IN 0 POL$0.00 0.00644364 88.70663292 Mint Top Hat 54562126 2024-03-12 7:59:58 270 days ago 1710230398 IN 0 POL$0.00 0.02364553 121.06790606 Mint Top Hat 54250616 2024-03-04 9:21:37 278 days ago 1709544097 IN 0 POL$0.00 0.06918663 243.82438177 Multicall 52813750 2024-01-27 15:09:33 315 days ago 1706368173 IN 0 POL$0.00 0.0245364 161.74615786 Multicall 52784803 2024-01-26 20:49:34 316 days ago 1706302174 IN 0 POL$0.00 0.0040982 32.07989827 Multicall 52784724 2024-01-26 20:46:42 316 days ago 1706302002 IN 0 POL$0.00 0.01200961 32.0651379 Mint Top Hat 52784646 2024-01-26 20:43:38 316 days ago 1706301818 IN 0 POL$0.00 0.0063071 32.29310568 Latest 1 internal transaction
Parent Transaction Hash Block From To 44952020 2023-07-11 13:43:58 515 days ago 1689083038 Contract Creation 0 POL$0.00 Loading...LoadingThis contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.Contract Name:Hats
Compiler Versionv0.8.17+commit.8df45f5f
Optimization Enabled:Yes with 10000 runs
Other Settings:default evmVersionContract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; import { ERC1155 } from "lib/ERC1155/ERC1155.sol"; // import { console2 } from "forge-std/Test.sol"; //remove after testing import "./Interfaces/IHats.sol"; import "./HatsIdUtilities.sol"; import "./Interfaces/IHatsToggle.sol"; import "./Interfaces/IHatsEligibility.sol"; import "solbase/utils/Base64.sol"; import "solbase/utils/LibString.sol"; import "solady/utils/Multicallable.sol"; /// @title Hats Protocol v1 /// @notice Hats are DAO-native, revocable, and programmable roles that are represented as non-transferable ERC-1155-similar tokens for composability /// @dev This is a multi-tenant contract that can manage all hats for a given chain. While it fully implements the ERC1155 interface, it does not fully comply with the ERC1155 standard. /// @author Haberdasher Labs contract Hats is IHats, ERC1155, Multicallable, HatsIdUtilities { /// @notice This contract's version is labeled v1. Previous versions labeled similarly as v1 and v1.0 are deprecated, /// and should be treated as beta deployments. /*////////////////////////////////////////////////////////////// HATS DATA MODELS //////////////////////////////////////////////////////////////*/ /// @notice A Hat object containing the hat's properties /// @dev The members are packed to minimize storage costs /// @custom:member eligibility Module that rules on wearer eligibiliy and standing /// @custom:member maxSupply The max number of hats with this id that can exist /// @custom:member supply The number of this hat that currently exist /// @custom:member lastHatId Indexes how many different child hats an admin has /// @custom:member toggle Module that sets the hat's status /** * @custom:member config Holds status and other settings, with this bitwise schema: * * 0th bit | `active` status; can be altered by toggle * 1 | `mutable` setting * 2 - 95 | unassigned */ /// @custom:member details Holds arbitrary metadata about the hat /// @custom:member imageURI A uri pointing to an image for the hat struct Hat { // 1st storage slot address eligibility; // ─┐ 20 uint32 maxSupply; // │ 4 uint32 supply; // │ 4 uint16 lastHatId; // ─┘ 2 // 2nd slot address toggle; // ─┐ 20 uint96 config; // ─┘ 12 // 3rd+ slot (optional) string details; string imageURI; } /*////////////////////////////////////////////////////////////// HATS STORAGE //////////////////////////////////////////////////////////////*/ /// @notice The name of the contract, typically including the version string public name; /// @notice The first 4 bytes of the id of the last tophat created. uint32 public lastTopHatId; // first tophat id starts at 1 /// @notice The fallback image URI for hat tokens with no `imageURI` specified in their branch string public baseImageURI; /// @dev Internal mapping of hats to hat ids. See HatsIdUtilities.sol for more info on how hat ids work mapping(uint256 => Hat) internal _hats; // key: hatId => value: Hat struct /// @notice Mapping of wearers in bad standing for certain hats /// @dev Used by external contracts to trigger penalties for wearers in bad standing /// hatId => wearer => !standing mapping(uint256 => mapping(address => bool)) public badStandings; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ /// @notice All arguments are immutable; they can only be set once during construction /// @param _name The name of this contract, typically including the version /// @param _baseImageURI The fallback image URI constructor(string memory _name, string memory _baseImageURI) { name = _name; baseImageURI = _baseImageURI; } /*////////////////////////////////////////////////////////////// HATS LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Creates and mints a Hat that is its own admin, i.e. a "topHat" /// @dev A topHat has no eligibility and no toggle /// @param _target The address to which the newly created topHat is minted /// @param _details A description of the Hat [optional]. Should not be larger than 7000 bytes /// (enforced in changeHatDetails) /// @param _imageURI The image uri for this top hat and the fallback for its /// downstream hats [optional]. Should not be large than 7000 bytes /// (enforced in changeHatImageURI) /// @return topHatId The id of the newly created topHat function mintTopHat(address _target, string calldata _details, string calldata _imageURI) public returns (uint256 topHatId) { // create hat topHatId = uint256(++lastTopHatId) << 224; _createHat( topHatId, _details, // details 1, // maxSupply = 1 address(0), // there is no eligibility address(0), // it has no toggle false, // its immutable _imageURI ); _mintHat(_target, topHatId); } /// @notice Creates a new hat. The msg.sender must wear the `_admin` hat. /// @dev Initializes a new Hat struct, but does not mint any tokens. /// @param _details A description of the Hat. Should not be larger than 7000 bytes (enforced in changeHatDetails) /// @param _maxSupply The total instances of the Hat that can be worn at once /// @param _admin The id of the Hat that will control who wears the newly created hat /// @param _eligibility The address that can report on the Hat wearer's status /// @param _toggle The address that can deactivate the Hat /// @param _mutable Whether the hat's properties are changeable after creation /// @param _imageURI The image uri for this hat and the fallback for its /// downstream hats [optional]. Should not be larger than 7000 bytes (enforced in changeHatImageURI) /// @return newHatId The id of the newly created Hat function createHat( uint256 _admin, string calldata _details, uint32 _maxSupply, address _eligibility, address _toggle, bool _mutable, string calldata _imageURI ) public returns (uint256 newHatId) { if (uint16(_admin) > 0) { revert MaxLevelsReached(); } if (_eligibility == address(0)) revert ZeroAddress(); if (_toggle == address(0)) revert ZeroAddress(); // check that the admin id is valid, ie does not contain empty levels between filled levels if (!isValidHatId(_admin)) revert InvalidHatId(); // construct the next hat id newHatId = getNextId(_admin); // to create a hat, you must be wearing one of its admin hats _checkAdmin(newHatId); // create the new hat _createHat(newHatId, _details, _maxSupply, _eligibility, _toggle, _mutable, _imageURI); // increment _admin.lastHatId // use the overflow check to constrain to correct number of hats per level ++_hats[_admin].lastHatId; } /// @notice Creates new hats in batch. The msg.sender must be an admin of each hat. /// @dev This is a convenience function that loops through the arrays and calls `createHat`. /// @param _admins Array of ids of admins for each hat to create /// @param _details Array of details for each hat to create /// @param _maxSupplies Array of supply caps for each hat to create /// @param _eligibilityModules Array of eligibility module addresses for each hat to /// create /// @param _toggleModules Array of toggle module addresses for each hat to create /// @param _mutables Array of mutable flags for each hat to create /// @param _imageURIs Array of imageURIs for each hat to create /// @return success True if all createHat calls succeeded function batchCreateHats( uint256[] calldata _admins, string[] calldata _details, uint32[] calldata _maxSupplies, address[] memory _eligibilityModules, address[] memory _toggleModules, bool[] calldata _mutables, string[] calldata _imageURIs ) public returns (bool success) { // check if array lengths are the same uint256 length = _admins.length; // save an MLOAD { bool sameLengths = ( length == _details.length // details && length == _maxSupplies.length // supplies && length == _eligibilityModules.length // eligibility && length == _toggleModules.length // toggle && length == _mutables.length // mutable && length == _imageURIs.length ); // imageURI if (!sameLengths) revert BatchArrayLengthMismatch(); } // loop through and create each hat for (uint256 i = 0; i < length;) { createHat( _admins[i], _details[i], _maxSupplies[i], _eligibilityModules[i], _toggleModules[i], _mutables[i], _imageURIs[i] ); unchecked { ++i; } } success = true; } /// @notice Gets the id of the next child hat of the hat `_admin` /// @dev Does not incrememnt lastHatId /// @param _admin The id of the hat to serve as the admin for the next child hat /// @return nextId The new hat id function getNextId(uint256 _admin) public view returns (uint256 nextId) { uint16 nextHatId = _hats[_admin].lastHatId + 1; nextId = buildHatId(_admin, nextHatId); } /// @notice Mints an ERC1155-similar token of the Hat to an eligible recipient, who then "wears" the hat /// @dev The msg.sender must wear an admin Hat of `_hatId`, and the recipient must be eligible to wear `_hatId` /// @param _hatId The id of the Hat to mint /// @param _wearer The address to which the Hat is minted /// @return success Whether the mint succeeded function mintHat(uint256 _hatId, address _wearer) public returns (bool success) { Hat storage hat = _hats[_hatId]; if (hat.maxSupply == 0) revert HatDoesNotExist(_hatId); // only eligible wearers can receive minted hats if (!isEligible(_wearer, _hatId)) revert NotEligible(); // only active hats can be minted if (!_isActive(hat, _hatId)) revert HatNotActive(); // only the wearer of one of a hat's admins can mint it _checkAdmin(_hatId); // hat supply cannot exceed maxSupply if (hat.supply >= hat.maxSupply) revert AllHatsWorn(_hatId); // wearers cannot wear the same hat more than once if (_staticBalanceOf(_wearer, _hatId) > 0) revert AlreadyWearingHat(_wearer, _hatId); // if we've made it through all the checks, mint the hat _mintHat(_wearer, _hatId); success = true; } /// @notice Mints new hats in batch. The msg.sender must be an admin of each hat. /// @dev This is a convenience function that loops through the arrays and calls `mintHat`. /// @param _hatIds Array of ids of hats to mint /// @param _wearers Array of addresses to which the hats will be minted /// @return success True if all mintHat calls succeeded function batchMintHats(uint256[] calldata _hatIds, address[] calldata _wearers) public returns (bool success) { uint256 length = _hatIds.length; if (length != _wearers.length) revert BatchArrayLengthMismatch(); for (uint256 i = 0; i < length;) { mintHat(_hatIds[i], _wearers[i]); unchecked { ++i; } } success = true; } /// @notice Toggles a Hat's status from active to deactive, or vice versa /// @dev The msg.sender must be set as the hat's toggle /// @param _hatId The id of the Hat for which to adjust status /// @param _newStatus The new status to set /// @return toggled Whether the status was toggled function setHatStatus(uint256 _hatId, bool _newStatus) external returns (bool toggled) { Hat storage hat = _hats[_hatId]; if (msg.sender != hat.toggle) { revert NotHatsToggle(); } toggled = _processHatStatus(_hatId, _newStatus); } /// @notice Checks a hat's toggle module and processes the returned status /// @dev May change the hat's status in storage /// @param _hatId The id of the Hat whose toggle we are checking /// @return toggled Whether there was a new status function checkHatStatus(uint256 _hatId) public returns (bool toggled) { Hat storage hat = _hats[_hatId]; // attempt to retrieve the hat's status from the toggle module (bool success, bool newStatus) = _pullHatStatus(hat, _hatId); // if unsuccessful (ie toggle was humanistic), process the new status if (!success) revert NotHatsToggle(); // if successful (ie toggle was mechanistic), process the new status toggled = _processHatStatus(_hatId, newStatus); } function _pullHatStatus(Hat storage _hat, uint256 _hatId) internal view returns (bool success, bool newStatus) { bytes memory data = abi.encodeWithSignature("getHatStatus(uint256)", _hatId); bytes memory returndata; (success, returndata) = _hat.toggle.staticcall(data); /* * if function call succeeds with data of length == 32, then we know the contract exists * and has the getHatStatus function. * But — since function selectors don't include return types — we still can't assume that the return data is a boolean, * so we treat it as a uint so it will always safely decode without throwing. */ if (success && returndata.length == 32) { // check the returndata manually uint256 uintReturndata = abi.decode(returndata, (uint256)); // false condition if (uintReturndata == 0) { newStatus = false; // true condition } else if (uintReturndata == 1) { newStatus = true; } // invalid condition else { success = false; } } else { success = false; } } /// @notice Report from a hat's eligibility on the status of one of its wearers and, if `false`, revoke their hat /// @dev Burns the wearer's hat, if revoked /// @param _hatId The id of the hat /// @param _wearer The address of the hat wearer whose status is being reported /// @param _eligible Whether the wearer is eligible for the hat (will be revoked if /// false) /// @param _standing False if the wearer is no longer in good standing (and potentially should be penalized) /// @return updated Whether the report succeeded function setHatWearerStatus(uint256 _hatId, address _wearer, bool _eligible, bool _standing) external returns (bool updated) { Hat storage hat = _hats[_hatId]; if (msg.sender != hat.eligibility) { revert NotHatsEligibility(); } updated = _processHatWearerStatus(_hatId, _wearer, _eligible, _standing); } /// @notice Check a hat's eligibility for a report on the status of one of the hat's wearers and, if `false`, revoke their hat /// @dev Burns the wearer's hat, if revoked /// @param _hatId The id of the hat /// @param _wearer The address of the Hat wearer whose status report is being requested /// @return updated Whether the wearer's status was altered function checkHatWearerStatus(uint256 _hatId, address _wearer) public returns (bool updated) { bool eligible; bool standing; (bool success, bytes memory returndata) = _hats[_hatId].eligibility.staticcall( abi.encodeWithSignature("getWearerStatus(address,uint256)", _wearer, _hatId) ); /* * if function call succeeds with data of length == 64, then we know the contract exists * and has the getWearerStatus function (which returns two words). * But — since function selectors don't include return types — we still can't assume that the return data is two booleans, * so we treat it as a uint so it will always safely decode without throwing. */ if (success && returndata.length == 64) { // check the returndata manually (uint256 firstWord, uint256 secondWord) = abi.decode(returndata, (uint256, uint256)); // returndata is valid if (firstWord < 2 && secondWord < 2) { standing = (secondWord == 1) ? true : false; // never eligible if in bad standing eligible = (standing && firstWord == 1) ? true : false; } // returndata is invalid else { revert NotHatsEligibility(); } } else { revert NotHatsEligibility(); } updated = _processHatWearerStatus(_hatId, _wearer, eligible, standing); } /// @notice Stop wearing a hat, aka "renounce" it /// @dev Burns the msg.sender's hat /// @param _hatId The id of the Hat being renounced function renounceHat(uint256 _hatId) external { if (_staticBalanceOf(msg.sender, _hatId) < 1) { revert NotHatWearer(); } // remove the hat _burnHat(msg.sender, _hatId); } /*////////////////////////////////////////////////////////////// HATS INTERNAL LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Internal call for creating a new hat /// @dev Initializes a new Hat in storage, but does not mint any tokens /// @param _id ID of the hat to be stored /// @param _details A description of the hat /// @param _maxSupply The total instances of the Hat that can be worn at once /// @param _eligibility The address that can report on the Hat wearer's status /// @param _toggle The address that can deactivate the hat [optional] /// @param _mutable Whether the hat's properties are changeable after creation /// @param _imageURI The image uri for this top hat and the fallback for its /// downstream hats [optional] function _createHat( uint256 _id, string calldata _details, uint32 _maxSupply, address _eligibility, address _toggle, bool _mutable, string calldata _imageURI ) internal { /* We write directly to storage instead of first building the Hat struct in memory. This allows us to cheaply use the existing lastHatId value in case it was incremented by creating a hat while skipping admin levels. (Resetting it to 0 would be bad since this hat's child hat(s) would overwrite the previously created hat(s) at that level.) */ Hat storage hat = _hats[_id]; hat.details = _details; hat.maxSupply = _maxSupply; hat.eligibility = _eligibility; hat.toggle = _toggle; hat.imageURI = _imageURI; // config is a concatenation of the status and mutability properties hat.config = _mutable ? uint96(3 << 94) : uint96(1 << 95); emit HatCreated(_id, _details, _maxSupply, _eligibility, _toggle, _mutable, _imageURI); } /// @notice Internal function to process hat status /// @dev Updates a hat's status if different from current /// @param _hatId The id of the Hat in quest /// @param _newStatus The status to potentially change to /// @return updated - Whether the status was updated function _processHatStatus(uint256 _hatId, bool _newStatus) internal returns (bool updated) { // optimize later Hat storage hat = _hats[_hatId]; if (_newStatus != _getHatStatus(hat)) { _setHatStatus(hat, _newStatus); emit HatStatusChanged(_hatId, _newStatus); updated = true; } } /// @notice Internal call to process wearer status from the eligibility module /// @dev Burns the wearer's Hat token if _eligible is false, and updates badStandings /// state if necessary /// @param _hatId The id of the Hat to revoke /// @param _wearer The address of the wearer in question /// @param _eligible Whether _wearer is eligible for the Hat (if false, this function /// will revoke their Hat) /// @param _standing Whether _wearer is in good standing (to be recorded in storage) /// @return updated Whether the wearer standing was updated function _processHatWearerStatus(uint256 _hatId, address _wearer, bool _eligible, bool _standing) internal returns (bool updated) { // revoke/burn the hat if _wearer has a positive balance if (_staticBalanceOf(_wearer, _hatId) > 0) { // always ineligible if in bad standing if (!_eligible || !_standing) { _burnHat(_wearer, _hatId); } } // record standing for use by other contracts // note: here, standing and badStandings are opposite // i.e. if standing (true = good standing) // then badStandings[_hatId][wearer] will be false // if they are different, then something has changed, and we need to update // badStandings marker if (_standing == badStandings[_hatId][_wearer]) { badStandings[_hatId][_wearer] = !_standing; updated = true; emit WearerStandingChanged(_hatId, _wearer, _standing); } } /// @notice Internal function to set a hat's status in storage /// @dev Flips the 0th bit of _hat.config via bitwise operation /// @param _hat The hat object /// @param _status The status to set for the hat function _setHatStatus(Hat storage _hat, bool _status) internal { if (_status) { _hat.config |= uint96(1 << 95); } else { _hat.config &= ~uint96(1 << 95); } } /** * @notice Internal function to retrieve an account's internal "static" balance directly from internal storage, * @dev This function bypasses the dynamic `_isActive` and `_isEligible` checks * @param _account The account to check * @param _hatId The hat to check * @return staticBalance The account's static of the hat, from internal storage */ function _staticBalanceOf(address _account, uint256 _hatId) internal view returns (uint256 staticBalance) { staticBalance = _balanceOf[_account][_hatId]; } /*////////////////////////////////////////////////////////////// HATS ADMIN FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @notice Checks whether msg.sender is an admin of a hat, and reverts if not function _checkAdmin(uint256 _hatId) internal view { if (!isAdminOfHat(msg.sender, _hatId)) { revert NotAdmin(msg.sender, _hatId); } } /// @notice checks whether the msg.sender is either an admin or wearer or a hat, and reverts the appropriate error if not function _checkAdminOrWearer(uint256 _hatId) internal view { if (!isAdminOfHat(msg.sender, _hatId) && !isWearerOfHat(msg.sender, _hatId)) { revert NotAdminOrWearer(); } } /// @notice Transfers a hat from one wearer to another eligible wearer /// @dev The hat must be mutable, and the transfer must be initiated by an admin /// @param _hatId The hat in question /// @param _from The current wearer /// @param _to The new wearer function transferHat(uint256 _hatId, address _from, address _to) public { _checkAdmin(_hatId); // cannot transfer immutable hats, except for tophats, which can always transfer themselves if (!isTopHat(_hatId)) { if (!_isMutable(_hats[_hatId])) revert Immutable(); } // Checks storage instead of `isWearerOfHat` since admins may want to transfer revoked Hats to new wearers if (_staticBalanceOf(_from, _hatId) < 1) revert NotHatWearer(); // Check if recipient is already wearing hat; also checks storage to maintain balance == 1 invariant if (_staticBalanceOf(_to, _hatId) > 0) revert AlreadyWearingHat(_to, _hatId); // only eligible wearers can receive transferred hats if (!isEligible(_to, _hatId)) revert NotEligible(); // only active hats can be transferred if (!_isActive(_hats[_hatId], _hatId)) revert HatNotActive(); // we've made it passed all the checks, so adjust balances to execute the transfer _balanceOf[_from][_hatId] = 0; _balanceOf[_to][_hatId] = 1; // emit the ERC1155 standard transfer event emit TransferSingle(msg.sender, _from, _to, _hatId, 1); } /// @notice Set a mutable hat to immutable /// @dev Sets the second bit of hat.config to 0 /// @param _hatId The id of the Hat to make immutable function makeHatImmutable(uint256 _hatId) external { _checkAdmin(_hatId); Hat storage hat = _hats[_hatId]; if (!_isMutable(hat)) { revert Immutable(); } hat.config &= ~uint96(1 << 94); emit HatMutabilityChanged(_hatId); } /// @notice Change a hat's details /// @dev Hat must be mutable, except for tophats. /// @param _hatId The id of the Hat to change /// @param _newDetails The new details. Must not be larger than 7000 bytes. function changeHatDetails(uint256 _hatId, string calldata _newDetails) external { if (bytes(_newDetails).length > 7000) revert StringTooLong(); _checkAdmin(_hatId); Hat storage hat = _hats[_hatId]; // a tophat can change its own details, but otherwise only mutable hat details can be changed if (!isTopHat(_hatId)) { if (!_isMutable(hat)) revert Immutable(); } hat.details = _newDetails; emit HatDetailsChanged(_hatId, _newDetails); } /// @notice Change a hat's details /// @dev Hat must be mutable /// @param _hatId The id of the Hat to change /// @param _newEligibility The new eligibility module function changeHatEligibility(uint256 _hatId, address _newEligibility) external { if (_newEligibility == address(0)) revert ZeroAddress(); _checkAdmin(_hatId); Hat storage hat = _hats[_hatId]; if (!_isMutable(hat)) { revert Immutable(); } hat.eligibility = _newEligibility; emit HatEligibilityChanged(_hatId, _newEligibility); } /// @notice Change a hat's details /// @dev Hat must be mutable /// @param _hatId The id of the Hat to change /// @param _newToggle The new toggle module function changeHatToggle(uint256 _hatId, address _newToggle) external { if (_newToggle == address(0)) revert ZeroAddress(); _checkAdmin(_hatId); Hat storage hat = _hats[_hatId]; if (!_isMutable(hat)) { revert Immutable(); } // record hat status from old toggle before changing; ensures smooth transition to new toggle, // especially in case of switching from mechanistic to humanistic toggle // a) attempt to retrieve hat status from old toggle (bool success, bool newStatus) = _pullHatStatus(hat, _hatId); // b) if succeeded, (ie if old toggle was mechanistic), store the retrieved status if (success) _processHatStatus(_hatId, newStatus); // set the new toggle hat.toggle = _newToggle; emit HatToggleChanged(_hatId, _newToggle); } /// @notice Change a hat's details /// @dev Hat must be mutable, except for tophats /// @param _hatId The id of the Hat to change /// @param _newImageURI The new imageURI. Must not be larger than 7000 bytes. function changeHatImageURI(uint256 _hatId, string calldata _newImageURI) external { if (bytes(_newImageURI).length > 7000) revert StringTooLong(); _checkAdmin(_hatId); Hat storage hat = _hats[_hatId]; // a tophat can change its own imageURI, but otherwise only mutable hat imageURIs can be changed if (!isTopHat(_hatId)) { if (!_isMutable(hat)) revert Immutable(); } hat.imageURI = _newImageURI; emit HatImageURIChanged(_hatId, _newImageURI); } /// @notice Change a hat's details /// @dev Hat must be mutable; new max supply cannot be less than current supply /// @param _hatId The id of the Hat to change /// @param _newMaxSupply The new max supply function changeHatMaxSupply(uint256 _hatId, uint32 _newMaxSupply) external { _checkAdmin(_hatId); Hat storage hat = _hats[_hatId]; if (!_isMutable(hat)) { revert Immutable(); } if (_newMaxSupply < hat.supply) { revert NewMaxSupplyTooLow(); } if (_newMaxSupply != hat.maxSupply) { hat.maxSupply = _newMaxSupply; emit HatMaxSupplyChanged(_hatId, _newMaxSupply); } } /// @notice Submits a request to link a Hat Tree under a parent tree. Requests can be /// submitted by either... /// a) the wearer of a topHat, previous to any linkage, or /// b) the admin(s) of an already-linked topHat (aka tree root), where such a /// request is to move the tree root to another admin within the same parent /// tree /// @dev A topHat can have at most 1 request at a time. Submitting a new request will /// replace the existing request. /// @param _topHatDomain The domain of the topHat to link /// @param _requestedAdminHat The hat that will administer the linked tree function requestLinkTopHatToTree(uint32 _topHatDomain, uint256 _requestedAdminHat) external { uint256 fullTopHatId = uint256(_topHatDomain) << 224; // (256 - TOPHAT_ADDRESS_SPACE); // The wearer of an unlinked tophat is also the admin of same; once a tophat is linked, its wearer is no longer its admin _checkAdmin(fullTopHatId); linkedTreeRequests[_topHatDomain] = _requestedAdminHat; emit TopHatLinkRequested(_topHatDomain, _requestedAdminHat); } /// @notice Approve a request to link a Tree under a parent tree, with options to add eligibility or toggle modules and change its metadata /// @dev Requests can only be approved by wearer or an admin of the `_newAdminHat`, and there /// can only be one link per tree root at a given time. /// @param _topHatDomain The 32 bit domain of the topHat to link /// @param _newAdminHat The hat that will administer the linked tree /// @param _eligibility Optional new eligibility module for the linked topHat /// @param _toggle Optional new toggle module for the linked topHat /// @param _details Optional new details for the linked topHat /// @param _imageURI Optional new imageURI for the linked topHat function approveLinkTopHatToTree( uint32 _topHatDomain, uint256 _newAdminHat, address _eligibility, address _toggle, string calldata _details, string calldata _imageURI ) external { // for everything but the last hat level, check the admin of `_newAdminHat`'s theoretical child hat, since either wearer or admin of `_newAdminHat` can approve if (getHatLevel(_newAdminHat) < MAX_LEVELS) { _checkAdmin(buildHatId(_newAdminHat, 1)); } else { // the above buildHatId trick doesn't work for the last hat level, so we need to explicitly check both admin and wearer in this case _checkAdminOrWearer(_newAdminHat); } // Linkages must be initiated by a request if (_newAdminHat != linkedTreeRequests[_topHatDomain]) revert LinkageNotRequested(); // remove the request -- ensures all linkages are initialized by unique requests, // except for relinks (see `relinkTopHatWithinTree`) delete linkedTreeRequests[_topHatDomain]; // execute the link. Replaces existing link, if any. _linkTopHatToTree(_topHatDomain, _newAdminHat, _eligibility, _toggle, _details, _imageURI); } /** * @notice Unlink a Tree from the parent tree * @dev This can only be called by an admin of the tree root. Fails if the topHat to unlink has no non-zero wearer, which can occur if... * - It's wearer is in badStanding * - It has been revoked from its wearer (and possibly burned)˘ * - It is not active (ie toggled off) * @param _topHatDomain The 32 bit domain of the topHat to unlink * @param _wearer The current wearer of the topHat to unlink */ function unlinkTopHatFromTree(uint32 _topHatDomain, address _wearer) external { uint256 fullTopHatId = uint256(_topHatDomain) << 224; // (256 - TOPHAT_ADDRESS_SPACE); _checkAdmin(fullTopHatId); // prevent unlinking if the topHat has no non-zero wearer // since we cannot search the entire address space for a wearer, we require the caller to provide the wearer if (_wearer == address(0) || !isWearerOfHat(_wearer, fullTopHatId)) revert HatsErrors.InvalidUnlink(); // execute the unlink delete linkedTreeAdmins[_topHatDomain]; // remove the request — ensures all linkages are initialized by unique requests delete linkedTreeRequests[_topHatDomain]; // reset eligibility and storage to defaults for unlinked top hats Hat storage hat = _hats[fullTopHatId]; delete hat.eligibility; delete hat.toggle; emit TopHatLinked(_topHatDomain, 0); } /// @notice Move a tree root to a different position within the same parent tree, /// without a request. Valid destinations include within the same local tree as the origin, /// or to the local tree of the tippyTopHat. TippyTopHat wearers can bypass this restriction /// to relink to anywhere in its full tree. /// @dev Caller must be both an admin tree root and admin or wearer of `_newAdminHat`. /// @param _topHatDomain The 32 bit domain of the topHat to relink /// @param _newAdminHat The new admin for the linked tree /// @param _eligibility Optional new eligibility module for the linked topHat /// @param _toggle Optional new toggle module for the linked topHat /// @param _details Optional new details for the linked topHat /// @param _imageURI Optional new imageURI for the linked topHat function relinkTopHatWithinTree( uint32 _topHatDomain, uint256 _newAdminHat, address _eligibility, address _toggle, string calldata _details, string calldata _imageURI ) external { uint256 fullTopHatId = uint256(_topHatDomain) << 224; // (256 - TOPHAT_ADDRESS_SPACE); // msg.sender being capable of both requesting and approving allows us to skip the request step _checkAdmin(fullTopHatId); // "requester" must be admin // "approver" can be wearer or admin if (getHatLevel(_newAdminHat) < MAX_LEVELS) { _checkAdmin(buildHatId(_newAdminHat, 1)); } else { // the above buildHatId trick doesn't work for the last hat level, so we need to explicitly check both admin and wearer in this case _checkAdminOrWearer(_newAdminHat); } // execute the new link, replacing the old link _linkTopHatToTree(_topHatDomain, _newAdminHat, _eligibility, _toggle, _details, _imageURI); } /// @notice Internal function to link a Tree under a parent Tree, with protection against circular linkages and relinking to a separate Tree, /// with options to add eligibility or toggle modules and change its metadata /// @dev Linking `_topHatDomain` replaces any existing links /// @param _topHatDomain The 32 bit domain of the topHat to link /// @param _newAdminHat The new admin for the linked tree /// @param _eligibility Optional new eligibility module for the linked topHat /// @param _toggle Optional new toggle module for the linked topHat /// @param _details Optional new details for the linked topHat /// @param _imageURI Optional new imageURI for the linked topHat function _linkTopHatToTree( uint32 _topHatDomain, uint256 _newAdminHat, address _eligibility, address _toggle, string calldata _details, string calldata _imageURI ) internal { if (!noCircularLinkage(_topHatDomain, _newAdminHat)) revert CircularLinkage(); { uint256 linkedAdmin = linkedTreeAdmins[_topHatDomain]; // disallow relinking to separate tree if (linkedAdmin > 0) { uint256 tippyTopHat = uint256(getTippyTopHatDomain(_topHatDomain)) << 224; if (!isWearerOfHat(msg.sender, tippyTopHat)) { uint256 destLocalTopHat = uint256(_newAdminHat >> 224 << 224); // (256 - TOPHAT_ADDRESS_SPACE); // for non-tippyTopHat wearers: destination local tophat must be either... // a) the same as origin local tophat, or // b) within the tippy top hat's local tree uint256 originLocalTopHat = linkedAdmin >> 224 << 224; // (256 - TOPHAT_ADDRESS_SPACE); if (destLocalTopHat != originLocalTopHat && destLocalTopHat != tippyTopHat) { revert CrossTreeLinkage(); } // for tippyTopHat weerers: destination must be within the same super tree } else if (!sameTippyTopHatDomain(_topHatDomain, _newAdminHat)) { revert CrossTreeLinkage(); } } } // update and log the linked topHat's modules and metadata, if any changes uint256 topHatId = uint256(_topHatDomain) << 224; Hat storage hat = _hats[topHatId]; if (_eligibility != address(0)) { hat.eligibility = _eligibility; emit HatEligibilityChanged(topHatId, _eligibility); } if (_toggle != address(0)) { hat.toggle = _toggle; emit HatToggleChanged(topHatId, _toggle); } uint256 length = bytes(_details).length; if (length > 0) { if (length > 7000) revert StringTooLong(); hat.details = _details; emit HatDetailsChanged(topHatId, _details); } length = bytes(_imageURI).length; if (length > 0) { if (length > 7000) revert StringTooLong(); hat.imageURI = _imageURI; emit HatImageURIChanged(topHatId, _imageURI); } // store the new linked admin linkedTreeAdmins[_topHatDomain] = _newAdminHat; emit TopHatLinked(_topHatDomain, _newAdminHat); } /*////////////////////////////////////////////////////////////// HATS VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @notice View the properties of a given Hat /// @param _hatId The id of the Hat /// @return details The details of the Hat /// @return maxSupply The max supply of tokens for this Hat /// @return supply The number of current wearers of this Hat /// @return eligibility The eligibility address for this Hat /// @return toggle The toggle address for this Hat /// @return imageURI The image URI used for this Hat /// @return lastHatId The most recently created Hat with this Hat as admin; also the count of Hats with this Hat as admin /// @return mutable_ Whether this hat's properties can be changed /// @return active Whether the Hat is current active, as read from `_isActive` function viewHat(uint256 _hatId) public view returns ( string memory details, uint32 maxSupply, uint32 supply, address eligibility, address toggle, string memory imageURI, uint16 lastHatId, bool mutable_, bool active ) { Hat storage hat = _hats[_hatId]; details = hat.details; maxSupply = hat.maxSupply; supply = hat.supply; eligibility = hat.eligibility; toggle = hat.toggle; imageURI = getImageURIForHat(_hatId); lastHatId = hat.lastHatId; mutable_ = _isMutable(hat); active = _isActive(hat, _hatId); } /// @notice Checks whether a given address wears a given Hat /// @dev Convenience function that wraps `balanceOf` /// @param _user The address in question /// @param _hatId The id of the Hat that the `_user` might wear /// @return isWearer Whether the `_user` wears the Hat. function isWearerOfHat(address _user, uint256 _hatId) public view returns (bool isWearer) { isWearer = (balanceOf(_user, _hatId) > 0); } /// @notice Checks whether a given address serves as the admin of a given Hat /// @dev Recursively checks if `_user` wears the admin Hat of the Hat in question. This is recursive since there may be a string of Hats as admins of Hats. /// @param _user The address in question /// @param _hatId The id of the Hat for which the `_user` might be the admin /// @return isAdmin Whether the `_user` has admin rights for the Hat function isAdminOfHat(address _user, uint256 _hatId) public view returns (bool isAdmin) { uint256 linkedTreeAdmin; uint32 adminLocalHatLevel; if (isLocalTopHat(_hatId)) { linkedTreeAdmin = linkedTreeAdmins[getTopHatDomain(_hatId)]; if (linkedTreeAdmin == 0) { // tree is not linked return isAdmin = isWearerOfHat(_user, _hatId); } else { // tree is linked if (isWearerOfHat(_user, linkedTreeAdmin)) { return isAdmin = true; } // user wears the treeAdmin else { adminLocalHatLevel = getLocalHatLevel(linkedTreeAdmin); _hatId = linkedTreeAdmin; } } } else { // if we get here, _hatId is not a tophat of any kind // get the local tree level of _hatId's admin adminLocalHatLevel = getLocalHatLevel(_hatId) - 1; } // search up _hatId's local address space for an admin hat that the _user wears while (adminLocalHatLevel > 0) { if (isWearerOfHat(_user, getAdminAtLocalLevel(_hatId, adminLocalHatLevel))) { return isAdmin = true; } // should not underflow given stopping condition > 0 unchecked { --adminLocalHatLevel; } } // if we get here, we've reached the top of _hatId's local tree, ie the local tophat // check if the user wears the local tophat if (isWearerOfHat(_user, getAdminAtLocalLevel(_hatId, 0))) return isAdmin = true; // if not, we check if it's linked to another tree linkedTreeAdmin = linkedTreeAdmins[getTopHatDomain(_hatId)]; if (linkedTreeAdmin == 0) { // tree is not linked // we've already learned that user doesn't wear the local tophat, so there's nothing else to check; we return false return isAdmin = false; } else { // tree is linked // check if user is wearer of linkedTreeAdmin if (isWearerOfHat(_user, linkedTreeAdmin)) return true; // if not, recurse to traverse the parent tree for a hat that the user wears isAdmin = isAdminOfHat(_user, linkedTreeAdmin); } } /// @notice Checks the active status of a hat /// @dev For internal use instead of `isActive` when passing Hat as param is preferable /// @param _hat The Hat struct /// @param _hatId The id of the hat /// @return active The active status of the hat function _isActive(Hat storage _hat, uint256 _hatId) internal view returns (bool active) { (bool success, bytes memory returndata) = _hat.toggle.staticcall(abi.encodeWithSignature("getHatStatus(uint256)", _hatId)); /* * if function call succeeds with data of length == 32, then we know the contract exists * and has the getHatStatus function. * But — since function selectors don't include return types — we still can't assume that the return data is a boolean, * so we treat it as a uint so it will always safely decode without throwing. */ if (success && returndata.length == 32) { // check the returndata manually uint256 uintReturndata = uint256(bytes32(returndata)); // false condition if (uintReturndata == 0) { active = false; // true condition } else if (uintReturndata == 1) { active = true; } // invalid condition else { active = _getHatStatus(_hat); } } else { active = _getHatStatus(_hat); } } /// @notice Checks the active status of a hat /// @param _hatId The id of the hat /// @return active Whether the hat is active function isActive(uint256 _hatId) external view returns (bool active) { active = _isActive(_hats[_hatId], _hatId); } /// @notice Internal function to retrieve a hat's status from storage /// @dev reads the 0th bit of the hat's config /// @param _hat The hat object /// @return status Whether the hat is active function _getHatStatus(Hat storage _hat) internal view returns (bool status) { status = (_hat.config >> 95 != 0); } /// @notice Internal function to retrieve a hat's mutability setting /// @dev reads the 1st bit of the hat's config /// @param _hat The hat object /// @return _mutable Whether the hat is mutable function _isMutable(Hat storage _hat) internal view returns (bool _mutable) { _mutable = (_hat.config & uint96(1 << 94) != 0); } /// @notice Checks whether a wearer of a Hat is in good standing /// @param _wearer The address of the Hat wearer /// @param _hatId The id of the Hat /// @return standing Whether the wearer is in good standing function isInGoodStanding(address _wearer, uint256 _hatId) public view returns (bool standing) { (bool success, bytes memory returndata) = _hats[_hatId].eligibility.staticcall( abi.encodeWithSignature("getWearerStatus(address,uint256)", _wearer, _hatId) ); /* * if function call succeeds with data of length == 64, then we know the contract exists * and has the getWearerStatus function (which returns two words). * But — since function selectors don't include return types — we still can't assume that the return data is two booleans, * so we treat it as a uint so it will always safely decode without throwing. */ if (success && returndata.length == 64) { // check the returndata manually (uint256 firstWord, uint256 secondWord) = abi.decode(returndata, (uint256, uint256)); // returndata is valid if (firstWord < 2 && secondWord < 2) { standing = (secondWord == 1) ? true : false; // returndata is invalid } else { standing = !badStandings[_hatId][_wearer]; } } else { standing = !badStandings[_hatId][_wearer]; } } /// @notice Internal call to check whether an address is eligible for a given Hat /// @dev Tries an external call to the Hat's eligibility module, defaulting to existing badStandings state if the call fails (ie if the eligibility module address does not conform to the IHatsEligibility interface) /// @param _wearer The address of the Hat wearer /// @param _hat The Hat object /// @param _hatId The id of the Hat /// @return eligible Whether the wearer is eligible for the Hat function _isEligible(address _wearer, Hat storage _hat, uint256 _hatId) internal view returns (bool eligible) { (bool success, bytes memory returndata) = _hat.eligibility.staticcall(abi.encodeWithSignature("getWearerStatus(address,uint256)", _wearer, _hatId)); /* * if function call succeeds with data of length == 64, then we know the contract exists * and has the getWearerStatus function (which returns two words). * But — since function selectors don't include return types — we still can't assume that the return data is two booleans, * so we treat it as a uint so it will always safely decode without throwing. */ if (success && returndata.length == 64) { bool standing; // check the returndata manually (uint256 firstWord, uint256 secondWord) = abi.decode(returndata, (uint256, uint256)); // returndata is valid if (firstWord < 2 && secondWord < 2) { standing = (secondWord == 1) ? true : false; // never eligible if in bad standing eligible = (standing && firstWord == 1) ? true : false; } // returndata is invalid else { eligible = !badStandings[_hatId][_wearer]; } } else { eligible = !badStandings[_hatId][_wearer]; } } /// @notice Checks whether an address is eligible for a given Hat /// @dev Public function for use when passing a Hat object is not possible or preferable /// @param _hatId The id of the Hat /// @param _wearer The address to check /// @return eligible Whether the wearer is eligible for the Hat function isEligible(address _wearer, uint256 _hatId) public view returns (bool eligible) { eligible = _isEligible(_wearer, _hats[_hatId], _hatId); } /// @notice Gets the current supply of a Hat /// @dev Only tracks explicit burns and mints, not dynamic revocations /// @param _hatId The id of the Hat /// @return supply The current supply of the Hat function hatSupply(uint256 _hatId) external view returns (uint32 supply) { supply = _hats[_hatId].supply; } /// @notice Gets the eligibility module for a hat /// @param _hatId The hat whose eligibility module we're looking for /// @return eligibility The eligibility module for this hat function getHatEligibilityModule(uint256 _hatId) external view returns (address eligibility) { eligibility = _hats[_hatId].eligibility; } /// @notice Gets the toggle module for a hat /// @param _hatId The hat whose toggle module we're looking for /// @return toggle The toggle module for this hat function getHatToggleModule(uint256 _hatId) external view returns (address toggle) { toggle = _hats[_hatId].toggle; } /// @notice Gets the max supply for a hat /// @param _hatId The hat whose max supply we're looking for /// @return maxSupply The maximum possible quantity of this hat that could be minted function getHatMaxSupply(uint256 _hatId) external view returns (uint32 maxSupply) { maxSupply = _hats[_hatId].maxSupply; } /// @notice Gets the imageURI for a given hat /// @dev If this hat does not have an imageURI set, recursively get the imageURI from /// its admin /// @param _hatId The hat whose imageURI we're looking for /// @return _uri The imageURI of this hat or, if empty, its admin function getImageURIForHat(uint256 _hatId) public view returns (string memory _uri) { // check _hatId first to potentially avoid the `getHatLevel` call Hat storage hat = _hats[_hatId]; string memory imageURI = hat.imageURI; // save 1 SLOAD // if _hatId has an imageURI, we return it if (bytes(imageURI).length > 0) { return imageURI; } // otherwise, we check its branch of admins uint256 level = getHatLevel(_hatId); // but first we check if _hatId is a tophat, in which case we fall back to the global image uri if (level == 0) return baseImageURI; // otherwise, we check each of its admins for a valid imageURI uint256 id; // already checked at `level` above, so we start the loop at `level - 1` for (uint256 i = level - 1; i > 0;) { id = getAdminAtLevel(_hatId, uint32(i)); hat = _hats[id]; imageURI = hat.imageURI; if (bytes(imageURI).length > 0) { return imageURI; } // should not underflow given stopping condition is > 0 unchecked { --i; } } id = getAdminAtLevel(_hatId, 0); hat = _hats[id]; imageURI = hat.imageURI; if (bytes(imageURI).length > 0) { return imageURI; } // if none of _hatId's admins has an imageURI of its own, we again fall back to the global image uri _uri = baseImageURI; } /// @notice Constructs the URI for a Hat, using data from the Hat struct /// @param _hatId The id of the Hat /// @return _uri An ERC1155-compatible JSON string function _constructURI(uint256 _hatId) internal view returns (string memory _uri) { Hat storage hat = _hats[_hatId]; uint256 hatAdmin; if (isTopHat(_hatId)) { hatAdmin = _hatId; } else { hatAdmin = getAdminAtLevel(_hatId, getHatLevel(_hatId) - 1); } // split into two objects to avoid stack too deep error string memory idProperties = string.concat( '"domain": "', LibString.toString(getTopHatDomain(_hatId)), '", "id": "', LibString.toString(_hatId), '", "pretty id": "', LibString.toHexString(_hatId, 32), '",' ); string memory otherProperties = string.concat( '"status": "', (_isActive(hat, _hatId) ? "active" : "inactive"), '", "current supply": "', LibString.toString(hat.supply), '", "supply cap": "', LibString.toString(hat.maxSupply), '", "admin (id)": "', LibString.toString(hatAdmin), '", "admin (pretty id)": "', LibString.toHexString(hatAdmin, 32), '", "eligibility module": "', LibString.toHexString(hat.eligibility), '", "toggle module": "', LibString.toHexString(hat.toggle), '", "mutable": "', _isMutable(hat) ? "true" : "false", '"' ); _uri = string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( string.concat( '{"name": "', "Hat", '", "description": "', hat.details, '", "image": "', getImageURIForHat(_hatId), '",', '"properties": ', "{", idProperties, otherProperties, "}", "}" ) ) ) ) ); } /*////////////////////////////////////////////////////////////// ERC1155 OVERRIDES //////////////////////////////////////////////////////////////*/ /// @notice Gets the Hat token balance of a user for a given Hat /// @dev Balance is dynamic based on the hat's status and wearer's eligibility, so off-chain balance data indexed from events may not be in sync /// @param _wearer The address whose balance is being checked /// @param _hatId The id of the Hat /// @return balance The `wearer`'s balance of the Hat tokens. Can never be > 1. function balanceOf(address _wearer, uint256 _hatId) public view override(ERC1155, IHats) returns (uint256 balance) { Hat storage hat = _hats[_hatId]; balance = 0; if (_isActive(hat, _hatId) && _isEligible(_wearer, hat, _hatId)) { balance = super.balanceOf(_wearer, _hatId); } } /// @notice Internal call to mint a Hat token to a wearer /// @dev Unsafe if called when `_wearer` has a non-zero balance of `_hatId` /// @param _wearer The wearer of the Hat and the recipient of the newly minted token /// @param _hatId The id of the Hat to mint function _mintHat(address _wearer, uint256 _hatId) internal { unchecked { // should not overflow since `mintHat` enforces max balance of 1 _balanceOf[_wearer][_hatId] = 1; // increment Hat supply counter // should not overflow given AllHatsWorn check in `mintHat` ++_hats[_hatId].supply; } emit TransferSingle(msg.sender, address(0), _wearer, _hatId, 1); } /// @notice Internal call to burn a wearer's Hat token /// @dev Unsafe if called when `_wearer` doesn't have a zero balance of `_hatId` /// @param _wearer The wearer from which to burn the Hat token /// @param _hatId The id of the Hat to burn function _burnHat(address _wearer, uint256 _hatId) internal { // neither should underflow since `_burnHat` is never called on non-positive balance unchecked { _balanceOf[_wearer][_hatId] = 0; // decrement Hat supply counter --_hats[_hatId].supply; } emit TransferSingle(msg.sender, _wearer, address(0), _hatId, 1); } /// @notice Approvals are not necessary for Hats since transfers are not handled by the wearer /// @dev Admins should use `transferHat()` to transfer function setApprovalForAll(address, bool) public pure override { revert(); } /// @notice Safe transfers are not necessary for Hats since transfers are not handled by the wearer /// @dev Admins should use `transferHat()` to transfer function safeTransferFrom(address, address, uint256, uint256, bytes calldata) public pure override { revert(); } /// @notice Safe transfers are not necessary for Hats since transfers are not handled by the wearer function safeBatchTransferFrom(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) public pure override { revert(); } /** * @notice ERC165 interface detection * @dev While Hats Protocol conforms to the ERC1155 *interface*, it does not fully conform to the ERC1155 *specification* * since it does not implement the ERC1155Receiver functionality. * For this reason, this function overrides the ERC1155 implementation to return false for ERC1155. * @param interfaceId The interface identifier, as specified in ERC-165 * @return bool True if the contract implements `interfaceId` and false otherwise */ function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165 // interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 || interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI } /// @notice Batch retrieval for wearer balances /// @dev Given the higher gas overhead of Hats balanceOf checks, large batches may be high cost or run into gas limits /// @param _wearers Array of addresses to check balances for /// @param _hatIds Array of Hat ids to check, using the same index as _wearers function balanceOfBatch(address[] calldata _wearers, uint256[] calldata _hatIds) public view override(ERC1155, IHats) returns (uint256[] memory balances) { if (_wearers.length != _hatIds.length) revert BatchArrayLengthMismatch(); balances = new uint256[](_wearers.length); // Unchecked because the only math done is incrementing // the array index counter which cannot possibly overflow. unchecked { for (uint256 i; i < _wearers.length; ++i) { balances[i] = balanceOf(_wearers[i], _hatIds[i]); } } } /// @notice View the uri for a Hat /// @param id The id of the Hat /// @return _uri An 1155-compatible JSON object function uri(uint256 id) public view override(ERC1155, IHats) returns (string memory _uri) { _uri = _constructURI(id); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Minimalist and gas efficient standard ERC1155 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol) abstract contract ERC1155 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 amount ); event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] amounts ); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); event URI(string value, uint256 indexed id); /*////////////////////////////////////////////////////////////// ERC1155 STORAGE //////////////////////////////////////////////////////////////*/ mapping(address => mapping(uint256 => uint256)) internal _balanceOf; mapping(address => mapping(address => bool)) public isApprovedForAll; /*////////////////////////////////////////////////////////////// METADATA LOGIC //////////////////////////////////////////////////////////////*/ function uri(uint256 id) public view virtual returns (string memory); /*////////////////////////////////////////////////////////////// ERC1155 LOGIC //////////////////////////////////////////////////////////////*/ function setApprovalForAll(address operator, bool approved) public virtual { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) public virtual { require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); _balanceOf[from][id] -= amount; _balanceOf[to][id] += amount; emit TransferSingle(msg.sender, from, to, id, amount); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == ERC1155TokenReceiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) public virtual { require(ids.length == amounts.length, "LENGTH_MISMATCH"); require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); // Storing these outside the loop saves ~15 gas per iteration. uint256 id; uint256 amount; for (uint256 i = 0; i < ids.length; ) { id = ids[i]; amount = amounts[i]; _balanceOf[from][id] -= amount; _balanceOf[to][id] += amount; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, from, to, ids, amounts); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == ERC1155TokenReceiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function balanceOf(address owner, uint256 id) public view virtual returns (uint256 balance) { balance = _balanceOf[owner][id]; } function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) public view virtual returns (uint256[] memory balances) { require(owners.length == ids.length, "LENGTH_MISMATCH"); balances = new uint256[](owners.length); // Unchecked because the only math done is incrementing // the array index counter which cannot possibly overflow. unchecked { for (uint256 i = 0; i < owners.length; ++i) { balances[i] = _balanceOf[owners[i]][ids[i]]; } } } /*////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint( address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { _balanceOf[to][id] += amount; emit TransferSingle(msg.sender, address(0), to, id, amount); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) == ERC1155TokenReceiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function _batchMint( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < idsLength; ) { _balanceOf[to][ids[i]] += amounts[i]; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, address(0), to, ids, amounts); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == ERC1155TokenReceiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function _batchBurn( address from, uint256[] memory ids, uint256[] memory amounts ) internal virtual { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < idsLength; ) { _balanceOf[from][ids[i]] -= amounts[i]; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, from, address(0), ids, amounts); } function _burn( address from, uint256 id, uint256 amount ) internal virtual { _balanceOf[from][id] -= amount; emit TransferSingle(msg.sender, from, address(0), id, amount); } } /// @notice A generic interface for a contract which properly accepts ERC1155 tokens. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol) abstract contract ERC1155TokenReceiver { function onERC1155Received( address, address, uint256, uint256, bytes calldata ) external virtual returns (bytes4) { return ERC1155TokenReceiver.onERC1155Received.selector; } function onERC1155BatchReceived( address, address, uint256[] calldata, uint256[] calldata, bytes calldata ) external virtual returns (bytes4) { return ERC1155TokenReceiver.onERC1155BatchReceived.selector; } }
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; import "./IHatsIdUtilities.sol"; import "./HatsErrors.sol"; import "./HatsEvents.sol"; interface IHats is IHatsIdUtilities, HatsErrors, HatsEvents { function mintTopHat(address _target, string memory _details, string memory _imageURI) external returns (uint256 topHatId); function createHat( uint256 _admin, string calldata _details, uint32 _maxSupply, address _eligibility, address _toggle, bool _mutable, string calldata _imageURI ) external returns (uint256 newHatId); function batchCreateHats( uint256[] calldata _admins, string[] calldata _details, uint32[] calldata _maxSupplies, address[] memory _eligibilityModules, address[] memory _toggleModules, bool[] calldata _mutables, string[] calldata _imageURIs ) external returns (bool success); function getNextId(uint256 _admin) external view returns (uint256 nextId); function mintHat(uint256 _hatId, address _wearer) external returns (bool success); function batchMintHats(uint256[] calldata _hatIds, address[] calldata _wearers) external returns (bool success); function setHatStatus(uint256 _hatId, bool _newStatus) external returns (bool toggled); function checkHatStatus(uint256 _hatId) external returns (bool toggled); function setHatWearerStatus(uint256 _hatId, address _wearer, bool _eligible, bool _standing) external returns (bool updated); function checkHatWearerStatus(uint256 _hatId, address _wearer) external returns (bool updated); function renounceHat(uint256 _hatId) external; function transferHat(uint256 _hatId, address _from, address _to) external; /*////////////////////////////////////////////////////////////// HATS ADMIN FUNCTIONS //////////////////////////////////////////////////////////////*/ function makeHatImmutable(uint256 _hatId) external; function changeHatDetails(uint256 _hatId, string memory _newDetails) external; function changeHatEligibility(uint256 _hatId, address _newEligibility) external; function changeHatToggle(uint256 _hatId, address _newToggle) external; function changeHatImageURI(uint256 _hatId, string memory _newImageURI) external; function changeHatMaxSupply(uint256 _hatId, uint32 _newMaxSupply) external; function requestLinkTopHatToTree(uint32 _topHatId, uint256 _newAdminHat) external; function approveLinkTopHatToTree( uint32 _topHatId, uint256 _newAdminHat, address _eligibility, address _toggle, string calldata _details, string calldata _imageURI ) external; function unlinkTopHatFromTree(uint32 _topHatId, address _wearer) external; function relinkTopHatWithinTree( uint32 _topHatDomain, uint256 _newAdminHat, address _eligibility, address _toggle, string calldata _details, string calldata _imageURI ) external; /*////////////////////////////////////////////////////////////// VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ function viewHat(uint256 _hatId) external view returns ( string memory details, uint32 maxSupply, uint32 supply, address eligibility, address toggle, string memory imageURI, uint16 lastHatId, bool mutable_, bool active ); function isWearerOfHat(address _user, uint256 _hatId) external view returns (bool isWearer); function isAdminOfHat(address _user, uint256 _hatId) external view returns (bool isAdmin); function isInGoodStanding(address _wearer, uint256 _hatId) external view returns (bool standing); function isEligible(address _wearer, uint256 _hatId) external view returns (bool eligible); function getHatEligibilityModule(uint256 _hatId) external view returns (address eligibility); function getHatToggleModule(uint256 _hatId) external view returns (address toggle); function getHatMaxSupply(uint256 _hatId) external view returns (uint32 maxSupply); function hatSupply(uint256 _hatId) external view returns (uint32 supply); function getImageURIForHat(uint256 _hatId) external view returns (string memory _uri); function balanceOf(address wearer, uint256 hatId) external view returns (uint256 balance); function balanceOfBatch(address[] calldata _wearers, uint256[] calldata _hatIds) external view returns (uint256[] memory); function uri(uint256 id) external view returns (string memory _uri); }
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; import "./Interfaces/IHatsIdUtilities.sol"; // import { console2 } from "forge-std/Test.sol"; //remove after testing /// @notice see HatsErrors.sol for description error MaxLevelsReached(); /// @title Hats Id Utilities /// @dev Functions for working with Hat Ids from Hats Protocol. Factored out of Hats.sol /// for easier use by other contracts. /// @author Haberdasher Labs contract HatsIdUtilities is IHatsIdUtilities { /// @notice Mapping of tophats requesting to link to admin hats in other trees /// @dev Linkage only occurs if request is approved by the new admin mapping(uint32 => uint256) public linkedTreeRequests; // topHatDomain => requested new admin /// @notice Mapping of approved & linked tophats to admin hats in other trees, used for grafting one hats tree onto another /// @dev Trees can only be linked to another tree via their tophat mapping(uint32 => uint256) public linkedTreeAdmins; // topHatDomain => hatId /** * Hat Ids serve as addresses. A given Hat's Id represents its location in its * hat tree: its level, its admin, its admin's admin (etc, all the way up to the * tophat). * * The top level consists of 4 bytes and references all tophats. * * Each level below consists of 16 bits, and contains up to 65,536 child hats. * * A uint256 contains 4 bytes of space for tophat addresses, giving room for ((256 - * 32) / 16) = 14 levels of delegation, with the admin at each level having space for * 65,536 different child hats. * * A hat tree consists of a single tophat and has a max depth of 14 levels. */ /// @dev Number of bits of address space for tophat ids, ie the tophat domain uint256 internal constant TOPHAT_ADDRESS_SPACE = 32; /// @dev Number of bits of address space for each level below the tophat uint256 internal constant LOWER_LEVEL_ADDRESS_SPACE = 16; /// @dev Maximum number of levels below the tophat, ie max tree depth /// (256 - TOPHAT_ADDRESS_SPACE) / LOWER_LEVEL_ADDRESS_SPACE; uint256 internal constant MAX_LEVELS = 14; /// @notice Constructs a valid hat id for a new hat underneath a given admin /// @dev Reverts if the admin has already reached `MAX_LEVELS` /// @param _admin the id of the admin for the new hat /// @param _newHat the uint16 id of the new hat /// @return id The constructed hat id function buildHatId(uint256 _admin, uint16 _newHat) public pure returns (uint256 id) { uint256 mask; for (uint256 i = 0; i < MAX_LEVELS;) { unchecked { mask = uint256( type(uint256).max // should not overflow given known constants >> (TOPHAT_ADDRESS_SPACE + (LOWER_LEVEL_ADDRESS_SPACE * i)) ); } if (_admin & mask == 0) { unchecked { id = _admin | ( uint256(_newHat) // should not overflow given known constants << (LOWER_LEVEL_ADDRESS_SPACE * (MAX_LEVELS - 1 - i)) ); } return id; } // should not overflow based on < MAX_LEVELS stopping condition unchecked { ++i; } } // if _admin is already at MAX_LEVELS, child hats are not possible, so we revert revert MaxLevelsReached(); } /// @notice Identifies the level a given hat in its hat tree /// @param _hatId the id of the hat in question /// @return level (0 to type(uint32).max) function getHatLevel(uint256 _hatId) public view returns (uint32 level) { // uint256 mask; // uint256 i; level = getLocalHatLevel(_hatId); uint256 treeAdmin = linkedTreeAdmins[getTopHatDomain(_hatId)]; if (treeAdmin != 0) { level = 1 + level + getHatLevel(treeAdmin); } } /// @notice Identifies the level a given hat in its local hat tree /// @dev Similar to getHatLevel, but does not account for linked trees /// @param _hatId the id of the hat in question /// @return level The local level, from 0 to 14 function getLocalHatLevel(uint256 _hatId) public pure returns (uint32 level) { if (_hatId & uint256(type(uint224).max) == 0) return 0; if (_hatId & uint256(type(uint208).max) == 0) return 1; if (_hatId & uint256(type(uint192).max) == 0) return 2; if (_hatId & uint256(type(uint176).max) == 0) return 3; if (_hatId & uint256(type(uint160).max) == 0) return 4; if (_hatId & uint256(type(uint144).max) == 0) return 5; if (_hatId & uint256(type(uint128).max) == 0) return 6; if (_hatId & uint256(type(uint112).max) == 0) return 7; if (_hatId & uint256(type(uint96).max) == 0) return 8; if (_hatId & uint256(type(uint80).max) == 0) return 9; if (_hatId & uint256(type(uint64).max) == 0) return 10; if (_hatId & uint256(type(uint48).max) == 0) return 11; if (_hatId & uint256(type(uint32).max) == 0) return 12; if (_hatId & uint256(type(uint16).max) == 0) return 13; return 14; } /// @notice Checks whether a hat is a topHat /// @param _hatId The hat in question /// @return _isTopHat Whether the hat is a topHat function isTopHat(uint256 _hatId) public view returns (bool _isTopHat) { _isTopHat = isLocalTopHat(_hatId) && linkedTreeAdmins[getTopHatDomain(_hatId)] == 0; } /// @notice Checks whether a hat is a topHat in its local hat tree /// @dev Similar to isTopHat, but does not account for linked trees /// @param _hatId The hat in question /// @return _isLocalTopHat Whether the hat is a topHat for its local tree function isLocalTopHat(uint256 _hatId) public pure returns (bool _isLocalTopHat) { _isLocalTopHat = _hatId > 0 && uint224(_hatId) == 0; } function isValidHatId(uint256 _hatId) public pure returns (bool validHatId) { // valid top hats are valid hats if (isLocalTopHat(_hatId)) return true; uint32 level = getLocalHatLevel(_hatId); uint256 admin; // for each subsequent level up the tree, check if the level is 0 and return false if so for (uint256 i = level - 1; i > 0;) { // truncate to find the (truncated) admin at this level // we don't need to check _hatId's own level since getLocalHatLevel already ensures that its non-empty admin = _hatId >> (LOWER_LEVEL_ADDRESS_SPACE * (MAX_LEVELS - i)); // if the lowest level of the truncated admin is empty, the hat id is invalid if (uint16(admin) == 0) return false; unchecked { --i; } } // if there are no empty levels, return true return true; } /// @notice Gets the hat id of the admin at a given level of a given hat /// @dev This function traverses trees by following the linkedTreeAdmin /// pointer to a hat located in a different tree /// @param _hatId the id of the hat in question /// @param _level the admin level of interest /// @return admin The hat id of the resulting admin function getAdminAtLevel(uint256 _hatId, uint32 _level) public view returns (uint256 admin) { uint256 linkedTreeAdmin = linkedTreeAdmins[getTopHatDomain(_hatId)]; if (linkedTreeAdmin == 0) return admin = getAdminAtLocalLevel(_hatId, _level); uint32 localTopHatLevel = getHatLevel(getAdminAtLocalLevel(_hatId, 0)); if (localTopHatLevel <= _level) return admin = getAdminAtLocalLevel(_hatId, _level - localTopHatLevel); return admin = getAdminAtLevel(linkedTreeAdmin, _level); } /// @notice Gets the hat id of the admin at a given level of a given hat /// local to the tree containing the hat. /// @param _hatId the id of the hat in question /// @param _level the admin level of interest /// @return admin The hat id of the resulting admin function getAdminAtLocalLevel(uint256 _hatId, uint32 _level) public pure returns (uint256 admin) { uint256 mask = type(uint256).max << (LOWER_LEVEL_ADDRESS_SPACE * (MAX_LEVELS - _level)); admin = _hatId & mask; } /// @notice Gets the tophat domain of a given hat /// @dev A domain is the identifier for a given hat tree, stored in the first 4 bytes of a hat's id /// @param _hatId the id of the hat in question /// @return domain The domain of the hat's tophat function getTopHatDomain(uint256 _hatId) public pure returns (uint32 domain) { domain = uint32(_hatId >> (LOWER_LEVEL_ADDRESS_SPACE * MAX_LEVELS)); } /// @notice Gets the domain of the highest parent tophat — the "tippy tophat" /// @param _topHatDomain the 32 bit domain of a (likely linked) tophat /// @return domain The tippy tophat domain function getTippyTopHatDomain(uint32 _topHatDomain) public view returns (uint32 domain) { uint256 linkedAdmin = linkedTreeAdmins[_topHatDomain]; if (linkedAdmin == 0) return domain = _topHatDomain; return domain = getTippyTopHatDomain(getTopHatDomain(linkedAdmin)); } /// @notice Checks For any circular linkage of trees /// @param _topHatDomain the 32 bit domain of the tree to be linked /// @param _linkedAdmin the hatId of the potential tree admin /// @return notCircular circular link has not been found function noCircularLinkage(uint32 _topHatDomain, uint256 _linkedAdmin) public view returns (bool notCircular) { if (_linkedAdmin == 0) return true; uint32 adminDomain = getTopHatDomain(_linkedAdmin); if (_topHatDomain == adminDomain) return false; uint256 parentAdmin = linkedTreeAdmins[adminDomain]; return noCircularLinkage(_topHatDomain, parentAdmin); } /// @notice Checks that a tophat domain and its potential linked admin are from the same tree, ie have the same tippy tophat domain /// @param _topHatDomain The 32 bit domain of the tophat to be linked /// @param _newAdminHat The new admin for the linked tree /// @return sameDomain Whether the _topHatDomain and the domain of its potential linked _newAdminHat domains are the same function sameTippyTopHatDomain(uint32 _topHatDomain, uint256 _newAdminHat) public view returns (bool sameDomain) { // get highest parent domains for current and new tree root admins uint32 currentTippyTophatDomain = getTippyTopHatDomain(_topHatDomain); uint32 newAdminDomain = getTopHatDomain(_newAdminHat); uint32 newHTippyTophatDomain = getTippyTopHatDomain(newAdminDomain); // check that both domains are equal sameDomain = (currentTippyTophatDomain == newHTippyTophatDomain); } }
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; interface IHatsToggle { function getHatStatus(uint256 _hatId) external view returns (bool); }
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; interface IHatsEligibility { /// @notice Returns the status of a wearer for a given hat /// @dev If standing is false, eligibility MUST also be false /// @param _wearer The address of the current or prospective Hat wearer /// @param _hatId The id of the hat in question /// @return eligible Whether the _wearer is eligible to wear the hat /// @return standing Whether the _wearer is in goog standing function getWearerStatus(address _wearer, uint256 _hatId) external view returns (bool eligible, bool standing); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library to encode and decode strings in Base64. /// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/utils/Base64.sol) /// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) library Base64 { /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// See: https://datatracker.ietf.org/doc/html/rfc4648 /// @param fileSafe Whether to replace '+' with '-' and '/' with '_'. /// @param noPadding Whether to strip away the padding. function encode(bytes memory data, bool fileSafe, bool noPadding) internal pure returns (string memory result) { assembly { let dataLength := mload(data) if dataLength { // Multiply by 4/3 rounded up. // The `shl(2, ...)` is equivalent to multiplying by 4. let encodedLength := shl(2, div(add(dataLength, 2), 3)) // Set `result` to point to the start of the free memory. result := mload(0x40) // Store the table into the scratch space. // Offsetted by -1 byte so that the `mload` will load the character. // We will rewrite the free memory pointer at `0x40` later with // the allocated size. // The magic constant 0x0230 will translate "-_" + "+/". mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230))) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, encodedLength) // Run over the input, 3 bytes at a time. // prettier-ignore for {} 1 {} { data := add(data, 3) // Advance 3 bytes. let input := mload(data) // Write 4 bytes. Optimized for fewer stack operations. mstore8( ptr , mload(and(shr(18, input), 0x3F))) mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F))) mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F))) mstore8(add(ptr, 3), mload(and( input , 0x3F))) ptr := add(ptr, 4) // Advance 4 bytes. // prettier-ignore if iszero(lt(ptr, end)) { break } } let r := mod(dataLength, 3) switch noPadding case 0 { // Offset `ptr` and pad with '='. We can simply write over the end. mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`. mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`. // Write the length of the string. mstore(result, encodedLength) } default { // Write the length of the string. mstore(result, sub(encodedLength, add(iszero(iszero(r)), eq(r, 1)))) } // Allocate the memory for the string. // Add 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(end, 31), not(31))) } } } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, false, false)`. function encode(bytes memory data) internal pure returns (string memory result) { result = encode(data, false, false); } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, fileSafe, false)`. function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) { result = encode(data, fileSafe, false); } /// @dev Decodes base64 encoded `data`. /// /// Supports: /// - RFC 4648 (both standard and file-safe mode). /// - RFC 3501 (63: ','). /// /// Does not support: /// - Line breaks. /// /// Note: For performance reasons, /// this function will NOT revert on invalid `data` inputs. /// Outputs for invalid inputs will simply be undefined behaviour. /// It is the user's responsibility to ensure that the `data` /// is a valid base64 encoded string. function decode(string memory data) internal pure returns (bytes memory result) { assembly { let dataLength := mload(data) if dataLength { let end := add(data, dataLength) let decodedLength := mul(shr(2, dataLength), 3) switch and(dataLength, 3) case 0 { // If padded. decodedLength := sub( decodedLength, add(eq(and(mload(end), 0xFF), 0x3d), eq(and(mload(end), 0xFFFF), 0x3d3d)) ) } default { // If non-padded. decodedLength := add(decodedLength, sub(and(dataLength, 3), 1)) } result := mload(0x40) // Write the length of the string. mstore(result, decodedLength) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) // Load the table into the scratch space. // Constants are optimized for smaller bytecode with zero gas overhead. // `m` also doubles as the mask of the upper 6 bits. let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc mstore(0x5b, m) mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064) mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4) // prettier-ignore for {} 1 {} { // Read 4 bytes. data := add(data, 4) let input := mload(data) // Write 3 bytes. mstore(ptr, or( and(m, mload(byte(28, input))), shr(6, or( and(m, mload(byte(29, input))), shr(6, or( and(m, mload(byte(30, input))), shr(6, mload(byte(31, input))) )) )) )) ptr := add(ptr, 3) // prettier-ignore if iszero(lt(data, end)) { break } } // Allocate the memory for the string. // Add 32 + 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(add(result, decodedLength), 63), not(31))) // Restore the zero slot. mstore(0x60, 0) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/utils/LibString.sol) /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) library LibString { /// ----------------------------------------------------------------------- /// Custom Errors /// ----------------------------------------------------------------------- /// @dev The `length` of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /// ----------------------------------------------------------------------- /// Constants /// ----------------------------------------------------------------------- /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = uint256(int256(-1)); /// ----------------------------------------------------------------------- /// Decimal Operations /// ----------------------------------------------------------------------- /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure 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) } } /// ----------------------------------------------------------------------- /// Hexadecimal Operations /// ----------------------------------------------------------------------- /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) { assembly { let start := mload(0x40) // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. // We add 0x20 to the total and round down to a multiple of 0x20. // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. let m := add(start, and(add(shl(1, length), 0x62), not(0x1f))) // Allocate the memory. 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 to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let temp := value // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. // prettier-ignore for {} 1 {} { str := sub(str, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) length := sub(length, 1) // prettier-ignore if iszero(length) { break } } if temp { // Store the function selector of `HexLengthInsufficient()`. mstore(0x00, 0x2194895a) // Revert with (offset, size). revert(0x1c, 0x04) } // Compute the string's length. let strLength := add(sub(end, str), 2) // Move the pointer and write the "0x" prefix. str := sub(str, 0x20) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2 + 2` bytes. function toHexString(uint256 value) internal pure returns (string memory str) { assembly { let start := mload(0x40) // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x40 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. let m := add(start, 0xa0) // Allocate the memory. 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 to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) // 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, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) // prettier-ignore if iszero(temp) { break } } // Compute the string's length. let strLength := add(sub(end, str), 2) // Move the pointer and write the "0x" prefix. str := sub(str, 0x20) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory str) { assembly { let start := mload(0x40) // We need 0x20 bytes for the length, 0x02 bytes for the prefix, // and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x02 + 0x28) is 0x60. str := add(start, 0x60) // Allocate the memory. mstore(0x40, str) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let length := 20 // 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, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) length := sub(length, 1) // prettier-ignore if iszero(length) { break } } // Move the pointer and write the "0x" prefix. str := sub(str, 32) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, 42) } } /// ----------------------------------------------------------------------- /// Other String Operations /// ----------------------------------------------------------------------- // For performance and bytecode compactness, all indices of the following operations // are byte (ASCII) offsets, not UTF character offsets. /// @dev Returns `subject` all occurances of `search` replaced with `replacement`. function replace( string memory subject, string memory search, string memory replacement ) internal pure returns (string memory result) { assembly { let subjectLength := mload(subject) let searchLength := mload(search) let replacementLength := mload(replacement) subject := add(subject, 0x20) search := add(search, 0x20) replacement := add(replacement, 0x20) result := add(mload(0x40), 0x20) let subjectEnd := add(subject, subjectLength) if iszero(gt(searchLength, subjectLength)) { let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) let h := 0 if iszero(lt(searchLength, 32)) { h := keccak256(search, searchLength) } let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(search) // prettier-ignore for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { mstore(result, t) result := add(result, 1) subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. // prettier-ignore for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) // prettier-ignore if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } } let resultRemainder := result result := add(mload(0x40), 0x20) let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) // Copy the rest of the string one word at a time. // prettier-ignore for {} lt(subject, subjectEnd) {} { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) // Zeroize the slot after the string. let last := add(add(result, 0x20), k) mstore(last, 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) // Store the length of the result. mstore(result, k) } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { assembly { // prettier-ignore for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { // `result = min(from, subjectLength)`. result := xor(from, mul(xor(from, subjectLength), lt(subjectLength, from))) break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let subjectSearchEnd := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(add(search, 0x20)) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } if iszero(lt(searchLength, 32)) { // prettier-ignore for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, searchLength), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } break } // prettier-ignore for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = indexOf(subject, search, 0); } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf( string memory subject, string memory search, uint256 from ) internal pure returns (uint256 result) { assembly { // prettier-ignore for {} 1 {} { let searchLength := mload(search) let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } if iszero(mload(search)) { result := from break } result := not(0) // Initialize to `NOT_FOUND`. let subjectSearchEnd := sub(add(subject, 0x20), 1) subject := add(add(subject, 0x20), from) // prettier-ignore if iszero(gt(subject, subjectSearchEnd)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. // prettier-ignore for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(subjectSearchEnd, 1)) break } subject := sub(subject, 1) // prettier-ignore if iszero(gt(subject, subjectSearchEnd)) { break } } break } } } /// @dev Returns the index of the first location of `search` in `subject`, /// searching from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = lastIndexOf(subject, search, uint256(int256(-1))); } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. result := and( iszero(gt(searchLength, mload(subject))), eq(keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength)) ) } } /// @dev Returns whether `subject` ends with `search`. function endsWith(string memory subject, string memory search) internal pure returns (bool result) { assembly { let searchLength := mload(search) let subjectLength := mload(subject) // Whether `search` is not longer than `subject`. let withinRange := iszero(gt(searchLength, subjectLength)) // Just using keccak256 directly is actually cheaper. result := and( withinRange, eq( keccak256( // `subject + 0x20 + max(subjectLength - searchLength, 0)`. add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), searchLength ), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) // prettier-ignore for {} 1 {} { // Copy the `subject` one word at a time. // prettier-ignore for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) // prettier-ignore if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) // prettier-ignore if iszero(times) { break } } // Zeroize the slot after the string. mstore(output, 0) // Store the length. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 63), not(31)))) } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory result) { assembly { let subjectLength := mload(subject) if iszero(gt(subjectLength, end)) { end := subjectLength } if iszero(gt(subjectLength, start)) { start := subjectLength } if lt(start, end) { result := mload(0x40) let resultLength := sub(end, start) mstore(result, resultLength) subject := add(subject, start) // Copy the `subject` one word at a time, backwards. // prettier-ignore for { let o := and(add(resultLength, 31), not(31)) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(result, 0x20), resultLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 63), not(31)))) } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. /// `start` is a byte offset. function slice(string memory subject, uint256 start) internal pure returns (string memory result) { result = slice(subject, start, uint256(int256(-1))); } /// @dev Returns all the indices of `search` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) { assembly { let subjectLength := mload(subject) let searchLength := mload(search) if iszero(gt(searchLength, subjectLength)) { subject := add(subject, 0x20) search := add(search, 0x20) result := add(mload(0x40), 0x20) let subjectStart := subject let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) let h := 0 if iszero(lt(searchLength, 32)) { h := keccak256(search, searchLength) } let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(search) // prettier-ignore for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Append to `result`. mstore(result, sub(subject, subjectStart)) result := add(result, 0x20) // Advance `subject` by `searchLength`. subject := add(subject, searchLength) if searchLength { // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } let resultEnd := result // Assign `result` to the free memory pointer. result := mload(0x40) // Store the length of `result`. mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(resultEnd, 0x20)) } } } /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { uint256[] memory indices = indicesOf(subject, delimiter); assembly { if mload(indices) { let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(sub(indicesEnd, 0x20), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 // prettier-ignore for {} 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let elementLength := sub(index, prevIndex) mstore(element, elementLength) // Copy the `subject` one word at a time, backwards. // prettier-ignore for { let o := and(add(elementLength, 31), not(31)) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(element, 0x20), elementLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(element, and(add(elementLength, 63), not(31)))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) // prettier-ignore if iszero(lt(indexPtr, indicesEnd)) { break } } result := indices if iszero(mload(delimiter)) { result := add(indices, 0x20) mstore(result, sub(mload(indices), 2)) } } } } /// @dev Returns a concatenated string of `a` and `b`. /// Cheaper than `string.concat()` and does not de-align the free memory pointer. function concat(string memory a, string memory b) internal pure returns (string memory result) { assembly { result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. // prettier-ignore for { let o := and(add(mload(a), 32), not(31)) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } let bLength := mload(b) let output := add(result, mload(a)) // Copy `b` one word at a time, backwards. // prettier-ignore for { let o := and(add(bLength, 32), not(31)) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } let totalLength := add(aLength, bLength) let last := add(add(result, 0x20), totalLength) // Zeroize the slot after the string. mstore(last, 0) // Stores the length. mstore(result, totalLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) } } /// @dev Packs a single string with its length into a single word. /// Returns `bytes32(0)` if the length is zero or greater than 31. function packOne(string memory a) internal pure returns (bytes32 result) { assembly { // We don't need to zero right pad the string, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes. mload(add(a, 0x1f)), // `length != 0 && length < 32`. Abuses underflow. // Assumes that the length is valid and within the block gas limit. lt(sub(mload(a), 1), 0x1f) ) } } /// @dev Unpacks a string packed using {packOne}. /// Returns the empty string if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packOne}, the output behaviour is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { assembly { // Grab the free memory pointer. result := mload(0x40) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(0x40, add(result, 0x40)) // Zeroize the length slot. mstore(result, 0) // Store the length and bytes. mstore(add(result, 0x1f), packed) // Right pad with zeroes. mstore(add(add(result, 0x20), mload(result)), 0) } } /// @dev Packs two strings with their lengths into a single word. /// Returns `bytes32(0)` if combined length is zero or greater than 30. function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { assembly { let aLength := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes of `a` and `b`. or(shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength))), // `totalLength != 0 && totalLength < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLength, mload(b)), 1), 0x1e) ) } } /// @dev Unpacks strings packed using {packTwo}. /// Returns the empty strings if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packTwo}, the output behaviour is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { assembly { // Grab the free memory pointer. resultA := mload(0x40) resultB := add(resultA, 0x40) // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. mstore(0x40, add(resultB, 0x40)) // Zeroize the length slots. mstore(resultA, 0) mstore(resultB, 0) // Store the lengths and bytes. mstore(add(resultA, 0x1f), packed) mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) // Right pad with zeroes. mstore(add(add(resultA, 0x20), mload(resultA)), 0) mstore(add(add(resultB, 0x20), mload(resultB)), 0) } } /// @dev Directly returns `a` without copying. function directReturn(string memory a) internal pure { assembly { // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(add(a, 0x20), mload(a)), 0) // Store the return offset. // Assumes that the string does not start from the scratch space. mstore(sub(a, 0x20), 0x20) // End the transaction, returning the string. return(sub(a, 0x20), add(mload(a), 0x40)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Contract that enables a single call to call multiple methods on itself. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol) abstract contract Multicallable { /// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`, /// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`. /// If any of the `DELEGATECALL`s reverts, the entire context is reverted, /// and the error is bubbled up. /// /// This function is deliberately made non-payable to guard against double-spending. /// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong) /// /// For efficiency, this function will directly return the results, terminating the context. /// If called internally, it must be called at the end of a function /// that returns `(bytes[] memory)`. function multicall(bytes[] calldata data) public virtual returns (bytes[] memory) { assembly { mstore(0x00, 0x20) mstore(0x20, data.length) // Store `data.length` into `results`. // Early return if no data. if iszero(data.length) { return(0x00, 0x40) } let results := 0x40 // `shl` 5 is equivalent to multiplying by 0x20. let end := shl(5, data.length) // Copy the offsets from calldata into memory. calldatacopy(0x40, data.offset, end) // Offset into `results`. let resultsOffset := end // Pointer to the end of `results`. end := add(results, end) for {} 1 {} { // The offset of the current bytes in the calldata. let o := add(data.offset, mload(results)) let memPtr := add(resultsOffset, 0x40) // Copy the current bytes from calldata to the memory. calldatacopy( memPtr, add(o, 0x20), // The offset of the current bytes' bytes. calldataload(o) // The length of the current bytes. ) if iszero(delegatecall(gas(), address(), memPtr, calldataload(o), 0x00, 0x00)) { // Bubble up the revert if the delegatecall reverts. returndatacopy(0x00, 0x00, returndatasize()) revert(0x00, returndatasize()) } // Append the current `resultsOffset` into `results`. mstore(results, resultsOffset) results := add(results, 0x20) // Append the `returndatasize()`, and the return data. mstore(memPtr, returndatasize()) returndatacopy(add(memPtr, 0x20), 0x00, returndatasize()) // Advance the `resultsOffset` by `returndatasize() + 0x20`, // rounded up to the next multiple of 32. resultsOffset := and(add(add(resultsOffset, returndatasize()), 0x3f), 0xffffffffffffffe0) if iszero(lt(results, end)) { break } } return(0x00, add(resultsOffset, 0x40)) } } }
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; interface IHatsIdUtilities { function buildHatId(uint256 _admin, uint16 _newHat) external pure returns (uint256 id); function getHatLevel(uint256 _hatId) external view returns (uint32 level); function getLocalHatLevel(uint256 _hatId) external pure returns (uint32 level); function isTopHat(uint256 _hatId) external view returns (bool _topHat); function isLocalTopHat(uint256 _hatId) external pure returns (bool _localTopHat); function isValidHatId(uint256 _hatId) external view returns (bool validHatId); function getAdminAtLevel(uint256 _hatId, uint32 _level) external view returns (uint256 admin); function getAdminAtLocalLevel(uint256 _hatId, uint32 _level) external pure returns (uint256 admin); function getTopHatDomain(uint256 _hatId) external view returns (uint32 domain); function getTippyTopHatDomain(uint32 _topHatDomain) external view returns (uint32 domain); function noCircularLinkage(uint32 _topHatDomain, uint256 _linkedAdmin) external view returns (bool notCircular); function sameTippyTopHatDomain(uint32 _topHatDomain, uint256 _newAdminHat) external view returns (bool sameDomain); }
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; interface HatsErrors { /// @notice Emitted when `user` is attempting to perform an action on `hatId` but is not wearing one of `hatId`'s admin hats /// @dev Can be equivalent to `NotHatWearer(buildHatId(hatId))`, such as when emitted by `approveLinkTopHatToTree` or `relinkTopHatToTree` error NotAdmin(address user, uint256 hatId); /// @notice Emitted when attempting to perform an action as or for an account that is not a wearer of a given hat error NotHatWearer(); /// @notice Emitted when attempting to perform an action that requires being either an admin or wearer of a given hat error NotAdminOrWearer(); /// @notice Emitted when attempting to mint `hatId` but `hatId`'s maxSupply has been reached error AllHatsWorn(uint256 hatId); /// @notice Emitted when attempting to create a hat with a level 14 hat as its admin error MaxLevelsReached(); /// @notice Emitted when an attempted hat id has empty intermediate level(s) error InvalidHatId(); /// @notice Emitted when attempting to mint `hatId` to a `wearer` who is already wearing the hat error AlreadyWearingHat(address wearer, uint256 hatId); /// @notice Emitted when attempting to mint a non-existant hat error HatDoesNotExist(uint256 hatId); /// @notice Emmitted when attempting to mint or transfer a hat that is not active error HatNotActive(); /// @notice Emitted when attempting to mint or transfer a hat to an ineligible wearer error NotEligible(); /// @notice Emitted when attempting to check or set a hat's status from an account that is not that hat's toggle module error NotHatsToggle(); /// @notice Emitted when attempting to check or set a hat wearer's status from an account that is not that hat's eligibility module error NotHatsEligibility(); /// @notice Emitted when array arguments to a batch function have mismatching lengths error BatchArrayLengthMismatch(); /// @notice Emitted when attempting to mutate or transfer an immutable hat error Immutable(); /// @notice Emitted when attempting to change a hat's maxSupply to a value lower than its current supply error NewMaxSupplyTooLow(); /// @notice Emitted when attempting to link a tophat to a new admin for which the tophat serves as an admin error CircularLinkage(); /// @notice Emitted when attempting to link or relink a tophat to a separate tree error CrossTreeLinkage(); /// @notice Emitted when attempting to link a tophat without a request error LinkageNotRequested(); /// @notice Emitted when attempting to unlink a tophat that does not have a wearer /// @dev This ensures that unlinking never results in a bricked tophat error InvalidUnlink(); /// @notice Emmited when attempting to change a hat's eligibility or toggle module to the zero address error ZeroAddress(); /// @notice Emmitted when attempting to change a hat's details or imageURI to a string with over 7000 bytes (~characters) /// @dev This protects against a DOS attack where an admin iteratively extend's a hat's details or imageURI /// to be so long that reading it exceeds the block gas limit, breaking `uri()` and `viewHat()` error StringTooLong(); }
// SPDX-License-Identifier: AGPL-3.0 // Copyright (C) 2023 Haberdasher Labs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.13; interface HatsEvents { /// @notice Emitted when a new hat is created /// @param id The id for the new hat /// @param details A description of the Hat /// @param maxSupply The total instances of the Hat that can be worn at once /// @param eligibility The address that can report on the Hat wearer's status /// @param toggle The address that can deactivate the Hat /// @param mutable_ Whether the hat's properties are changeable after creation /// @param imageURI The image uri for this hat and the fallback for its event HatCreated( uint256 id, string details, uint32 maxSupply, address eligibility, address toggle, bool mutable_, string imageURI ); /// @notice Emitted when a hat wearer's standing is updated /// @dev Eligibility is excluded since the source of truth for eligibility is the eligibility module and may change without a transaction /// @param hatId The id of the wearer's hat /// @param wearer The wearer's address /// @param wearerStanding Whether the wearer is in good standing for the hat event WearerStandingChanged(uint256 hatId, address wearer, bool wearerStanding); /// @notice Emitted when a hat's status is updated /// @param hatId The id of the hat /// @param newStatus Whether the hat is active event HatStatusChanged(uint256 hatId, bool newStatus); /// @notice Emitted when a hat's details are updated /// @param hatId The id of the hat /// @param newDetails The updated details event HatDetailsChanged(uint256 hatId, string newDetails); /// @notice Emitted when a hat's eligibility module is updated /// @param hatId The id of the hat /// @param newEligibility The updated eligibiliy module event HatEligibilityChanged(uint256 hatId, address newEligibility); /// @notice Emitted when a hat's toggle module is updated /// @param hatId The id of the hat /// @param newToggle The updated toggle module event HatToggleChanged(uint256 hatId, address newToggle); /// @notice Emitted when a hat's mutability is updated /// @param hatId The id of the hat event HatMutabilityChanged(uint256 hatId); /// @notice Emitted when a hat's maximum supply is updated /// @param hatId The id of the hat /// @param newMaxSupply The updated max supply event HatMaxSupplyChanged(uint256 hatId, uint32 newMaxSupply); /// @notice Emitted when a hat's image URI is updated /// @param hatId The id of the hat /// @param newImageURI The updated image URI event HatImageURIChanged(uint256 hatId, string newImageURI); /// @notice Emitted when a tophat linkage is requested by its admin /// @param domain The domain of the tree tophat to link /// @param newAdmin The tophat's would-be admin in the parent tree event TopHatLinkRequested(uint32 domain, uint256 newAdmin); /// @notice Emitted when a tophat is linked to a another tree /// @param domain The domain of the newly-linked tophat /// @param newAdmin The tophat's new admin in the parent tree event TopHatLinked(uint32 domain, uint256 newAdmin); }
{ "remappings": [ "ERC1155/=lib/ERC1155/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "solady/=lib/solady/src/", "solbase/=lib/solbase/src/", "utils/=lib/utils/" ], "optimizer": { "enabled": true, "runs": 10000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_baseImageURI","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"hatId","type":"uint256"}],"name":"AllHatsWorn","type":"error"},{"inputs":[{"internalType":"address","name":"wearer","type":"address"},{"internalType":"uint256","name":"hatId","type":"uint256"}],"name":"AlreadyWearingHat","type":"error"},{"inputs":[],"name":"BatchArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CircularLinkage","type":"error"},{"inputs":[],"name":"CrossTreeLinkage","type":"error"},{"inputs":[{"internalType":"uint256","name":"hatId","type":"uint256"}],"name":"HatDoesNotExist","type":"error"},{"inputs":[],"name":"HatNotActive","type":"error"},{"inputs":[],"name":"Immutable","type":"error"},{"inputs":[],"name":"InvalidHatId","type":"error"},{"inputs":[],"name":"InvalidUnlink","type":"error"},{"inputs":[],"name":"LinkageNotRequested","type":"error"},{"inputs":[],"name":"MaxLevelsReached","type":"error"},{"inputs":[],"name":"MaxLevelsReached","type":"error"},{"inputs":[],"name":"NewMaxSupplyTooLow","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"hatId","type":"uint256"}],"name":"NotAdmin","type":"error"},{"inputs":[],"name":"NotAdminOrWearer","type":"error"},{"inputs":[],"name":"NotEligible","type":"error"},{"inputs":[],"name":"NotHatWearer","type":"error"},{"inputs":[],"name":"NotHatsEligibility","type":"error"},{"inputs":[],"name":"NotHatsToggle","type":"error"},{"inputs":[],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"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":"id","type":"uint256"},{"indexed":false,"internalType":"string","name":"details","type":"string"},{"indexed":false,"internalType":"uint32","name":"maxSupply","type":"uint32"},{"indexed":false,"internalType":"address","name":"eligibility","type":"address"},{"indexed":false,"internalType":"address","name":"toggle","type":"address"},{"indexed":false,"internalType":"bool","name":"mutable_","type":"bool"},{"indexed":false,"internalType":"string","name":"imageURI","type":"string"}],"name":"HatCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"},{"indexed":false,"internalType":"string","name":"newDetails","type":"string"}],"name":"HatDetailsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"},{"indexed":false,"internalType":"address","name":"newEligibility","type":"address"}],"name":"HatEligibilityChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"},{"indexed":false,"internalType":"string","name":"newImageURI","type":"string"}],"name":"HatImageURIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"newMaxSupply","type":"uint32"}],"name":"HatMaxSupplyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"}],"name":"HatMutabilityChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"newStatus","type":"bool"}],"name":"HatStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"},{"indexed":false,"internalType":"address","name":"newToggle","type":"address"}],"name":"HatToggleChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"domain","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"newAdmin","type":"uint256"}],"name":"TopHatLinkRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"domain","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"newAdmin","type":"uint256"}],"name":"TopHatLinked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hatId","type":"uint256"},{"indexed":false,"internalType":"address","name":"wearer","type":"address"},{"indexed":false,"internalType":"bool","name":"wearerStanding","type":"bool"}],"name":"WearerStandingChanged","type":"event"},{"inputs":[{"internalType":"uint32","name":"_topHatDomain","type":"uint32"},{"internalType":"uint256","name":"_newAdminHat","type":"uint256"},{"internalType":"address","name":"_eligibility","type":"address"},{"internalType":"address","name":"_toggle","type":"address"},{"internalType":"string","name":"_details","type":"string"},{"internalType":"string","name":"_imageURI","type":"string"}],"name":"approveLinkTopHatToTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"badStandings","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wearer","type":"address"},{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_wearers","type":"address[]"},{"internalType":"uint256[]","name":"_hatIds","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseImageURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_admins","type":"uint256[]"},{"internalType":"string[]","name":"_details","type":"string[]"},{"internalType":"uint32[]","name":"_maxSupplies","type":"uint32[]"},{"internalType":"address[]","name":"_eligibilityModules","type":"address[]"},{"internalType":"address[]","name":"_toggleModules","type":"address[]"},{"internalType":"bool[]","name":"_mutables","type":"bool[]"},{"internalType":"string[]","name":"_imageURIs","type":"string[]"}],"name":"batchCreateHats","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_hatIds","type":"uint256[]"},{"internalType":"address[]","name":"_wearers","type":"address[]"}],"name":"batchMintHats","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_admin","type":"uint256"},{"internalType":"uint16","name":"_newHat","type":"uint16"}],"name":"buildHatId","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"string","name":"_newDetails","type":"string"}],"name":"changeHatDetails","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"address","name":"_newEligibility","type":"address"}],"name":"changeHatEligibility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"string","name":"_newImageURI","type":"string"}],"name":"changeHatImageURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"uint32","name":"_newMaxSupply","type":"uint32"}],"name":"changeHatMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"address","name":"_newToggle","type":"address"}],"name":"changeHatToggle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"checkHatStatus","outputs":[{"internalType":"bool","name":"toggled","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"address","name":"_wearer","type":"address"}],"name":"checkHatWearerStatus","outputs":[{"internalType":"bool","name":"updated","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_admin","type":"uint256"},{"internalType":"string","name":"_details","type":"string"},{"internalType":"uint32","name":"_maxSupply","type":"uint32"},{"internalType":"address","name":"_eligibility","type":"address"},{"internalType":"address","name":"_toggle","type":"address"},{"internalType":"bool","name":"_mutable","type":"bool"},{"internalType":"string","name":"_imageURI","type":"string"}],"name":"createHat","outputs":[{"internalType":"uint256","name":"newHatId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"uint32","name":"_level","type":"uint32"}],"name":"getAdminAtLevel","outputs":[{"internalType":"uint256","name":"admin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"uint32","name":"_level","type":"uint32"}],"name":"getAdminAtLocalLevel","outputs":[{"internalType":"uint256","name":"admin","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"getHatEligibilityModule","outputs":[{"internalType":"address","name":"eligibility","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"getHatLevel","outputs":[{"internalType":"uint32","name":"level","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"getHatMaxSupply","outputs":[{"internalType":"uint32","name":"maxSupply","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"getHatToggleModule","outputs":[{"internalType":"address","name":"toggle","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"getImageURIForHat","outputs":[{"internalType":"string","name":"_uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"getLocalHatLevel","outputs":[{"internalType":"uint32","name":"level","type":"uint32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_admin","type":"uint256"}],"name":"getNextId","outputs":[{"internalType":"uint256","name":"nextId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_topHatDomain","type":"uint32"}],"name":"getTippyTopHatDomain","outputs":[{"internalType":"uint32","name":"domain","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"getTopHatDomain","outputs":[{"internalType":"uint32","name":"domain","type":"uint32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"hatSupply","outputs":[{"internalType":"uint32","name":"supply","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isActive","outputs":[{"internalType":"bool","name":"active","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isAdminOfHat","outputs":[{"internalType":"bool","name":"isAdmin","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wearer","type":"address"},{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isEligible","outputs":[{"internalType":"bool","name":"eligible","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wearer","type":"address"},{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isInGoodStanding","outputs":[{"internalType":"bool","name":"standing","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isLocalTopHat","outputs":[{"internalType":"bool","name":"_isLocalTopHat","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isTopHat","outputs":[{"internalType":"bool","name":"_isTopHat","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isValidHatId","outputs":[{"internalType":"bool","name":"validHatId","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"isWearerOfHat","outputs":[{"internalType":"bool","name":"isWearer","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTopHatId","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"linkedTreeAdmins","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"linkedTreeRequests","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"makeHatImmutable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"address","name":"_wearer","type":"address"}],"name":"mintHat","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"string","name":"_details","type":"string"},{"internalType":"string","name":"_imageURI","type":"string"}],"name":"mintTopHat","outputs":[{"internalType":"uint256","name":"topHatId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_topHatDomain","type":"uint32"},{"internalType":"uint256","name":"_linkedAdmin","type":"uint256"}],"name":"noCircularLinkage","outputs":[{"internalType":"bool","name":"notCircular","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_topHatDomain","type":"uint32"},{"internalType":"uint256","name":"_newAdminHat","type":"uint256"},{"internalType":"address","name":"_eligibility","type":"address"},{"internalType":"address","name":"_toggle","type":"address"},{"internalType":"string","name":"_details","type":"string"},{"internalType":"string","name":"_imageURI","type":"string"}],"name":"relinkTopHatWithinTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"renounceHat","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_topHatDomain","type":"uint32"},{"internalType":"uint256","name":"_requestedAdminHat","type":"uint256"}],"name":"requestLinkTopHatToTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint32","name":"_topHatDomain","type":"uint32"},{"internalType":"uint256","name":"_newAdminHat","type":"uint256"}],"name":"sameTippyTopHatDomain","outputs":[{"internalType":"bool","name":"sameDomain","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"bool","name":"_newStatus","type":"bool"}],"name":"setHatStatus","outputs":[{"internalType":"bool","name":"toggled","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"address","name":"_wearer","type":"address"},{"internalType":"bool","name":"_eligible","type":"bool"},{"internalType":"bool","name":"_standing","type":"bool"}],"name":"setHatWearerStatus","outputs":[{"internalType":"bool","name":"updated","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"transferHat","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_topHatDomain","type":"uint32"},{"internalType":"address","name":"_wearer","type":"address"}],"name":"unlinkTopHatFromTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"_uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hatId","type":"uint256"}],"name":"viewHat","outputs":[{"internalType":"string","name":"details","type":"string"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"supply","type":"uint32"},{"internalType":"address","name":"eligibility","type":"address"},{"internalType":"address","name":"toggle","type":"address"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"uint16","name":"lastHatId","type":"uint16"},{"internalType":"bool","name":"mutable_","type":"bool"},{"internalType":"bool","name":"active","type":"bool"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b506040516200594a3803806200594a83398101604081905262000034916200011f565b600462000042838262000218565b50600662000051828262000218565b505050620002e4565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200008257600080fd5b81516001600160401b03808211156200009f576200009f6200005a565b604051601f8301601f19908116603f01168101908282118183101715620000ca57620000ca6200005a565b81604052838152602092508683858801011115620000e757600080fd5b600091505b838210156200010b5785820183015181830184015290820190620000ec565b600093810190920192909252949350505050565b600080604083850312156200013357600080fd5b82516001600160401b03808211156200014b57600080fd5b620001598683870162000070565b935060208501519150808211156200017057600080fd5b506200017f8582860162000070565b9150509250929050565b600181811c908216806200019e57607f821691505b602082108103620001bf57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200021357600081815260208120601f850160051c81016020861015620001ee5750805b601f850160051c820191505b818110156200020f57828155600101620001fa565b5050505b505050565b81516001600160401b038111156200023457620002346200005a565b6200024c8162000245845462000189565b84620001c5565b602080601f8311600181146200028457600084156200026b5750858301515b600019600386901b1c1916600185901b1785556200020f565b600085815260208120601f198616915b82811015620002b55788860151825594840194600190910190840162000294565b5085821015620002d45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61565680620002f46000396000f3fe608060405234801561001057600080fd5b50600436106103a35760003560e01c806382afd23b116101e9578063d0221ee01161010f578063e985e9c5116100ad578063fb2849171161007c578063fb284917146108e7578063fb2aaa4c14610914578063fc3e05a414610927578063fd8a71631461093757600080fd5b8063e985e9c514610885578063f242432a146108b3578063f737401e146108c1578063f8f353b6146108d457600080fd5b8063d80a8434116100e9578063d80a843414610839578063d9fd995b1461084c578063e05209ff1461085f578063e33dd5c91461087257600080fd5b8063d0221ee0146107eb578063d3272ba8146107fe578063d395acf81461081157600080fd5b8063a2be079b11610187578063b052925e11610156578063b052925e14610792578063b56f7562146107a5578063c43dc177146107b8578063cead6304146107cb57600080fd5b8063a2be079b1461071e578063a4a1f06c1461074c578063ac9650d81461075f578063afaae55a1461077f57600080fd5b80639d6ccb9f116101c35780639d6ccb9f146106d75780639fe3b510146106ea578063a22cb465146106fd578063a29e8fa61461070b57600080fd5b806382afd23b146106885780638c0760771461069b5780639aa22f6d146106c457600080fd5b8063499c05e8116102ce578063641f776e1161026c578063756329a41161023b578063756329a41461063c5780637903e56b1461064f5780637a9df89f1461066257806380198fad1461067557600080fd5b8063641f776e146105cc5780636743175a146105df5780637178fb51146105f257806374f82e301461063457600080fd5b806354a1826c116102a857806354a1826c1461054f57806357f607721461056257806359701e44146105a65780635d5eb5af146105b957600080fd5b8063499c05e8146104f45780634e1273f41461051c57806352a6b2651461053c57600080fd5b80631a64dfad11610346578063378a848911610315578063378a8489146104a85780633fa9d544146104bb5780634352409a146104ce57806345fffac8146104e157600080fd5b80631a64dfad1461046157806321dbb8cb146104745780632eb2c2d614610487578063376792b91461049557600080fd5b806306fdde031161038257806306fdde03146104065780630b328e261461041b5780630e89341c1461043b5780631183a8c01461044e57600080fd5b8062fdd58e146103a85780630109f854146103ce57806301ffc9a7146103e3575b600080fd5b6103bb6103b63660046141ef565b61094a565b6040519081526020015b60405180910390f35b6103e16103dc366004614219565b6109a6565b005b6103f66103f1366004614245565b610af1565b60405190151581526020016103c5565b61040e610b8a565b6040516103c591906142d7565b6103bb6104293660046142fe565b60036020526000908152604090205481565b61040e610449366004614319565b610c18565b6103bb61045c366004614319565b610c23565b6103bb61046f36600461437b565b610c76565b6103e16104823660046143fc565b610cdf565b6103e16103a3366004614464565b6103e16104a336600461451f565b610e41565b6103e16104b636600461451f565b610ec4565b6103f66104c9366004614219565b610f75565b6103f66104dc3660046141ef565b611117565b6103f66104ef366004614319565b61112c565b610507610502366004614319565b611169565b60405163ffffffff90911681526020016103c5565b61052f61052a3660046145a9565b611333565b6040516103c59190614615565b6103f661054a3660046145a9565b611434565b6103f661055d3660046141ef565b6114d4565b61058e610570366004614319565b6000908152600760205260409020600101546001600160a01b031690565b6040516001600160a01b0390911681526020016103c5565b6103e16105b4366004614219565b61166e565b6103bb6105c73660046143fc565b611787565b6103f66105da366004614219565b611814565b6103f66105ed366004614319565b6119e1565b610507610600366004614319565b6000908152600760205260409020547801000000000000000000000000000000000000000000000000900463ffffffff1690565b61040e611a69565b6103e161064a366004614319565b611a76565b6103f661065d36600461471f565b611b49565b6103e1610670366004614319565b611cd2565b6103e161068336600461488a565b611d32565b6103f6610696366004614319565b611e57565b61058e6106a9366004614319565b6000908152600760205260409020546001600160a01b031690565b6105076106d2366004614319565b611e6f565b6105076106e53660046142fe565b611e86565b6103f66106f83660046148b4565b611eb6565b6103e16103a33660046148e0565b6103f66107193660046148b4565b611ef3565b6103f661072c366004614219565b600860209081526000928352604080842090915290825290205460ff1681565b6103e161075a36600461490a565b611f4f565b61077261076d366004614946565b612194565b6040516103c59190614988565b6103f661078d366004614a08565b612215565b6103bb6107a0366004614a55565b61227c565b6103f66107b33660046141ef565b612405565b6103f66107c6366004614b16565b61254c565b6103bb6107d93660046142fe565b60026020526000908152604090205481565b6103e16107f93660046148b4565b6125aa565b6103f661080c366004614319565b612628565b61082461081f366004614319565b612687565b6040516103c599989796959493929190614b39565b6103f66108473660046141ef565b6127f5565b6103e161085a366004614bb5565b61280f565b6103bb61086d3660046143fc565b61290a565b61040e610880366004614319565b612939565b6103f6610893366004614c01565b600160209081526000928352604080842090915290825290205460ff1681565b6103e16103a3366004614c1d565b6103e16108cf366004614bb5565b612cd7565b6103bb6108e2366004614c95565b612dd2565b6105076108f5366004614319565b600090815260076020526040902054600160a01b900463ffffffff1690565b610507610922366004614319565b612e4f565b6005546105079063ffffffff1681565b6103f6610945366004614319565b612eae565b60008181526007602052604081206109628184612edd565b8015610974575061097484828561301f565b1561099f576001600160a01b0384166000908152602081815260408083208684529091529020545b91505b5092915050565b6001600160a01b0381166109e6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109ef826131d1565b6000828152600760205260409020610a1f8160010154600160a01b90046b40000000000000000000000016151590565b610a55576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610a62838661321a565b915091508115610a7857610a76858261335a565b505b6001830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0386169081179091556040805187815260208101929092527ff68bc34e5f23b18f8e3a63ff51c021c8fbc9266113a63a4c73a66b4ad9033638910160405180910390a15050505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610b8457507f0e89341c000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60048054610b9790614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc390614ccc565b8015610c105780601f10610be557610100808354040283529160200191610c10565b820191906000526020600020905b815481529060010190602001808311610bf357829003601f168201915b505050505081565b6060610b84826133d3565b6000818152600760205260408120548190610c63907c0100000000000000000000000000000000000000000000000000000000900461ffff166001614d48565b9050610c6f8382612dd2565b9392505050565b6005805460009160e0918390610c919063ffffffff16614d63565b91906101000a81548163ffffffff021916908363ffffffff160217905563ffffffff16901b9050610ccc818686600160008060008a8a61368e565b610cd686826137de565b95945050505050565b610ce8826131d1565b6000828152600760205260409020610d188160010154600160a01b90046b40000000000000000000000016151590565b610d4e576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805463ffffffff780100000000000000000000000000000000000000000000000090910481169083161015610daf576040517fd2955d3b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805463ffffffff838116600160a01b9092041614610e3c5780547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff16600160a01b63ffffffff84169081029190911782556040805185815260208101929092527fb1141d53812d55ffe5a4ca05e686728ebe52d8e4fc66057d6e54724c01a77f5691015b60405180910390a15b505050565b7fffffffff0000000000000000000000000000000000000000000000000000000060e089901b16610e71816131d1565b600e610e7c89612e4f565b63ffffffff161015610ea057610e9b610e96896001612dd2565b6131d1565b610ea9565b610ea98861389a565b610eb989898989898989896138ef565b505050505050505050565b600e610ecf88612e4f565b63ffffffff161015610eee57610ee9610e96886001612dd2565b610ef7565b610ef78761389a565b63ffffffff88166000908152600260205260409020548714610f45576040517fbcb31ea200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8816600090815260026020526040812055610f6b88888888888888886138ef565b5050505050505050565b6000828152600760205260408082205490516001600160a01b038481166024830152604482018690528392839283928392169060640160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd683872000000000000000000000000000000000000000000000000000000001790525161100e9190614da2565b600060405180830381855afa9150503d8060008114611049576040519150601f19603f3d011682016040523d82523d6000602084013e61104e565b606091505b5091509150818015611061575080516040145b156110cc576000808280602001905181019061107d9190614dbe565b915091506002821080156110915750600281105b156110cc57806001146110a55760006110a8565b60015b94508480156110b75750816001145b6110c25760006110c5565b60015b95506110fe565b6040517f8bd78b5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061110c87878686613cd9565b979650505050505050565b600080611124848461094a565b119392505050565b600061113782612eae565b8015610b8457506003600061114b84611e6f565b63ffffffff1681526020810191909152604001600020541592915050565b60007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216810361119857506000919050565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff82166000036111c457506001919050565b77ffffffffffffffffffffffffffffffffffffffffffffffff82166000036111ee57506002919050565b75ffffffffffffffffffffffffffffffffffffffffffff821660000361121657506003919050565b6001600160a01b03821660000361122f57506004919050565b71ffffffffffffffffffffffffffffffffffff821660000361125357506005919050565b6fffffffffffffffffffffffffffffffff821660000361127557506006919050565b6dffffffffffffffffffffffffffff821660000361129557506007919050565b6bffffffffffffffffffffffff82166000036112b357506008919050565b69ffffffffffffffffffff82166000036112cf57506009919050565b67ffffffffffffffff82166000036112e95750600a919050565b65ffffffffffff82166000036113015750600b919050565b63ffffffff82166000036113175750600c919050565b61ffff821660000361132b5750600d919050565b50600e919050565b606083821461136e576040517ffc5221bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8367ffffffffffffffff81111561138757611387614659565b6040519080825280602002602001820160405280156113b0578160200160208202803683370190505b50905060005b8481101561142b576114068686838181106113d3576113d3614de2565b90506020020160208101906113e89190614e11565b8585848181106113fa576113fa614de2565b9050602002013561094a565b82828151811061141857611418614de2565b60209081029190910101526001016113b6565b50949350505050565b600083828114611470576040517ffc5221bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156114c7576114be87878381811061149057611490614de2565b905060200201358686848181106114a9576114a9614de2565b90506020020160208101906105da9190614e11565b50600101611473565b5060019695505050505050565b6000818152600760205260408082205490516001600160a01b038581166024830152604482018590528392839291169060640160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd683872000000000000000000000000000000000000000000000000000000001790525161156a9190614da2565b600060405180830381855afa9150503d80600081146115a5576040519150601f19603f3d011682016040523d82523d6000602084013e6115aa565b606091505b50915091508180156115bd575080516040145b1561163c57600080828060200190518101906115d99190614dbe565b915091506002821080156115ed5750600281105b1561160b5780600114611601576000611604565b60015b9450611635565b60008681526008602090815260408083206001600160a01b038b16845290915290205460ff161594505b5050611666565b60008481526008602090815260408083206001600160a01b038916845290915290205460ff161592505b505092915050565b6001600160a01b0381166116ae576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116b7826131d1565b60008281526007602052604090206116e78160010154600160a01b90046b40000000000000000000000016151590565b61171d576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03831690811782556040805185815260208101929092527f5f59e4feecec6992840f6f7c313415b21889d4445ea82fefd7f620aeb194461d9101610e33565b6000806003600061179786611e6f565b63ffffffff1663ffffffff168152602001908152602001600020549050806000036117ce576117c6848461290a565b915050610b84565b60006117de61092286600061290a565b90508363ffffffff168163ffffffff161161180a576118018561086d8387614e2c565b92505050610b84565b610cd68285611787565b60008281526007602052604081208054600160a01b900463ffffffff168203611871576040517fae231814000000000000000000000000000000000000000000000000000000008152600481018590526024015b60405180910390fd5b61187b83856127f5565b6118b1576040517ff8eb54de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118bb8185612edd565b6118f1576040517fe629aba300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118fa846131d1565b805463ffffffff600160a01b8204811678010000000000000000000000000000000000000000000000009092041610611962576040517f0cd96ab800000000000000000000000000000000000000000000000000000000815260048101859052602401611868565b6001600160a01b038316600090815260208181526040808320878452909152902054156119cd576040517f643d20830000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101859052604401611868565b6119d783856137de565b5060019392505050565b60006119ec82612eae565b156119f957506001919050565b6000611a0483611169565b9050600080611a14600184614e2c565b63ffffffff1690505b8015611a5e57611a2e81600e614e49565b611a39906010614e5c565b85901c91508161ffff16600003611a5557506000949350505050565b60001901611a1d565b506001949350505050565b60068054610b9790614ccc565b611a7f816131d1565b6000818152600760205260409020611aaf8160010154600160a01b90046b40000000000000000000000016151590565b611ae5576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001810180546001600160a01b038116600160a01b918290046bbfffffffffffffffffffffff169091021790556040518281527f10bb2a0010ffb3a037a886128a37f6574be73acb0ee505c37a4f12e8d290ed8e9060200160405180910390a15050565b60008b818b82148015611b5b5750818a145b8015611b675750885182145b8015611b735750875182145b8015611b7e57508186145b8015611b8957508184145b905080611bc2576040517ffc5221bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060005b81811015611cbd57611cb48f8f83818110611be357611be3614de2565b905060200201358e8e84818110611bfc57611bfc614de2565b9050602002810190611c0e9190614e73565b8e8e86818110611c2057611c20614de2565b9050602002016020810190611c3591906142fe565b8d8681518110611c4757611c47614de2565b60200260200101518d8781518110611c6157611c61614de2565b60200260200101518d8d89818110611c7b57611c7b614de2565b9050602002016020810190611c909190614ed8565b8c8c8a818110611ca257611ca2614de2565b90506020028101906107a09190614e73565b50600101611bc6565b5060019e9d5050505050505050505050505050565b3360009081526020818152604080832084845290915290205460011115611d25576040517fae9f0ba500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d2f3382613de8565b50565b7fffffffff0000000000000000000000000000000000000000000000000000000060e083901b16611d62816131d1565b6001600160a01b0382161580611d7f5750611d7d8282611117565b155b15611db6576040517fa60d071800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff83166000818152600360209081526040808320839055600282528083208390558483526007825280832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811682556001820180549091169055815194855291840192909252917fd89812e6ebd66026da23b9c10923f8ec2d47c2f4bea260250e66d05ab76f6b8991015b60405180910390a150505050565b6000818152600760205260408120610b849083612edd565b6000611e7d600e6010614e5c565b9190911c919050565b63ffffffff8116600090815260036020526040812054808203611eaa575090919050565b610c6f6106e582611e6f565b600080611ec284611e86565b90506000611ecf84611e6f565b90506000611edc82611e86565b63ffffffff93841693169290921495945050505050565b600081600003611f0557506001610b84565b6000611f1083611e6f565b90508063ffffffff168463ffffffff1603611f2f576000915050610b84565b63ffffffff8116600090815260036020526040902054610cd68582611ef3565b611f58836131d1565b611f618361112c565b611fc257600083815260076020526040902060010154600160a01b90046b40000000000000000000000016611fc2576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0382166000908152602081815260408083208684529091529020546001111561201e576040517fae9f0ba500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03811660009081526020818152604080832086845290915290205415612089576040517f643d20830000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260248101849052604401611868565b61209381846127f5565b6120c9576040517ff8eb54de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602052604090206120e19084612edd565b612117576040517fe629aba300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038281166000818152602081815260408083208884528252808320839055938516808352828252848320888452825291849020600190819055845188815291820152909233917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a4505050565b6060602060005281602052816121aa5760406000f35b60408260051b8085604037818101905b82518601604082018135602083018237600080833583305af46121e1573d6000803e3d6000fd5b8285526020850194503d81523d6000602083013e50503d01603f0167ffffffffffffffe0168183106121ba57604081016000f35b600084815260076020526040812080546001600160a01b03163314612266576040517f8bd78b5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61227286868686613cd9565b9695505050505050565b600061ffff8a16156122ba576040517f7c9225c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0386166122fa576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851661233a576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123438a6119e1565b612379576040517fb43a065000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123828a610c23565b905061238d816131d1565b61239e818a8a8a8a8a8a8a8a61368e565b60008a81526007602052604090208054601c906123de907c0100000000000000000000000000000000000000000000000000000000900461ffff16614ef3565b91906101000a81548161ffff021916908361ffff1602179055509998505050505050505050565b600080600061241384612eae565b15612481576003600061242586611e6f565b63ffffffff1663ffffffff16815260200190815260200160002054915081600003612454576118018585611117565b61245e8583611117565b1561246e57600192505050610b84565b61247782611169565b9050819350612499565b600161248c85611169565b6124969190614e2c565b90505b63ffffffff8116156124cc576124b3856104dc868461290a565b156124c357600192505050610b84565b60001901612499565b6124db856104dc86600061290a565b156124eb57600192505050610b84565b600360006124f886611e6f565b63ffffffff1663ffffffff1681526020019081526020016000205491508160000361252857600092505050610b84565b6125328583611117565b1561254257600192505050610b84565b610cd68583612405565b600082815260076020526040812060018101546001600160a01b031633146125a0576040517feaca8b6500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61099c848461335a565b7fffffffff0000000000000000000000000000000000000000000000000000000060e083901b166125da816131d1565b63ffffffff8316600081815260026020908152604091829020859055815192835282018490527f81ae7626a9656578e59b92a7575fdbd9cfe59e467c1e88334e305e0b428e76669101610e33565b60008181526007602052604081208180612642838661321a565b915091508161267d576040517feaca8b6500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cd6858261335a565b60606000806000806060600080600080600760008c815260200190815260200160002090508060020180546126bb90614ccc565b80601f01602080910402602001604051908101604052809291908181526020018280546126e790614ccc565b80156127345780601f1061270957610100808354040283529160200191612734565b820191906000526020600020905b81548152906001019060200180831161271757829003601f168201915b505084546001860154949e5063ffffffff600160a01b820481169e5078010000000000000000000000000000000000000000000000008204169c506001600160a01b039081169b5093909316985061279192508d91506129399050565b815460018301549196507c0100000000000000000000000000000000000000000000000000000000900461ffff1694506b400000000000000000000000600160a01b90910416151592506127e5818c612edd565b9150509193959799909294969850565b6000818152600760205260408120610c6f9084908461301f565b611b5881111561284b576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612854836131d1565b600083815260076020526040902061286b8461112c565b6128c7576128918160010154600160a01b90046b40000000000000000000000016151590565b6128c7576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381016128d6838583614f58565b507f26bb402a5ddddf109a54486ab4d62a69ae00966cbc94c57075787f70bf9cba6c848484604051611e4993929190615044565b60008061291e63ffffffff8416600e614e49565b612929906010614e5c565b600019901b939093169392505050565b6000818152600760205260408120600381018054606093919061295b90614ccc565b80601f016020809104026020016040519081016040528092919081815260200182805461298790614ccc565b80156129d45780601f106129a9576101008083540402835291602001916129d4565b820191906000526020600020905b8154815290600101906020018083116129b757829003601f168201915b505050505090506000815111156129ec579392505050565b60006129f785612e4f565b63ffffffff16905080600003612a9c5760068054612a1490614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612a4090614ccc565b8015612a8d5780601f10612a6257610100808354040283529160200191612a8d565b820191906000526020600020905b815481529060010190602001808311612a7057829003601f168201915b50505050509350505050919050565b600080612aaa600184614e49565b90505b8015612b7e57612abd8782611787565b6000818152600760205260409020600381018054919750919350612ae090614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612b0c90614ccc565b8015612b595780601f10612b2e57610100808354040283529160200191612b59565b820191906000526020600020905b815481529060010190602001808311612b3c57829003601f168201915b50505050509350600084511115612b7557509195945050505050565b60001901612aad565b50612b8a866000611787565b6000818152600760205260409020600381018054919650919250612bad90614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612bd990614ccc565b8015612c265780601f10612bfb57610100808354040283529160200191612c26565b820191906000526020600020905b815481529060010190602001808311612c0957829003601f168201915b50505050509250600083511115612c41575090949350505050565b60068054612c4e90614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612c7a90614ccc565b8015612cc75780601f10612c9c57610100808354040283529160200191612cc7565b820191906000526020600020905b815481529060010190602001808311612caa57829003601f168201915b5050505050945050505050919050565b611b58811115612d13576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d1c836131d1565b6000838152600760205260409020612d338461112c565b612d8f57612d598160010154600160a01b90046b40000000000000000000000016151590565b612d8f576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101612d9e838583614f58565b507fb8350431af3841b22f977d0e8fd4be94669ef68e2db1d7f02a731d5d09ed44cc848484604051611e4993929190615044565b60008060005b600e811015612e1c57600019602060108302011c9150848216600003612e1457806001600e03036010028461ffff16901b851792505050610b84565b600101612dd8565b506040517f7c9225c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612e5a82611169565b9050600060036000612e6b85611e6f565b63ffffffff16815260208101919091526040016000205490508015612ea857612e9381612e4f565b612e9e83600161505e565b610c6f919061505e565b50919050565b60008082118015610b845750507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b600182015460405160248101839052600091829182916001600160a01b03169060440160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f96208f7a0000000000000000000000000000000000000000000000000000000017905251612f639190614da2565b600060405180830381855afa9150503d8060008114612f9e576040519150601f19603f3d011682016040523d82523d6000602084013e612fa3565b606091505b5091509150818015612fb6575080516020145b15613007576000612fc68261507b565b90506000819003612fda5760009350613001565b80600103612feb5760019350613001565b600180870154600160a01b9004605f1c16151593505b50611666565b600180860154600160a01b9004605f1c161515610cd6565b81546040516001600160a01b03858116602483015260448201849052600092839283929091169060640160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd68387200000000000000000000000000000000000000000000000000000000179052516130ac9190614da2565b600060405180830381855afa9150503d80600081146130e7576040519150601f19603f3d011682016040523d82523d6000602084013e6130ec565b606091505b50915091508180156130ff575080516040145b1561319e5760008060008380602001905181019061311d9190614dbe565b915091506002821080156131315750600281105b1561316c5780600114613145576000613148565b60015b92508280156131575750816001145b613162576000613165565b60015b9550613196565b60008781526008602090815260408083206001600160a01b038d16845290915290205460ff161595505b5050506131c8565b60008481526008602090815260408083206001600160a01b038a16845290915290205460ff161592505b50509392505050565b6131db3382612405565b611d2f576040517ff921ec0100000000000000000000000000000000000000000000000000000000815233600482015260248101829052604401611868565b60008060008360405160240161323291815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f96208f7a00000000000000000000000000000000000000000000000000000000179052600187015490519192506060916001600160a01b03909116906132ae908490614da2565b600060405180830381855afa9150503d80600081146132e9576040519150601f19603f3d011682016040523d82523d6000602084013e6132ee565b606091505b509094509050838015613302575080516020145b1561334c5760008180602001905181019061331d919061509f565b9050806000036133305760009350613346565b806001036133415760019350613346565b600094505b50613351565b600093505b50509250929050565b6000828152600760205260408120600180820154600160a01b9004605f1c16151515158315151461099f5761338f8184613ea4565b6040805185815284151560208201527fd07f9843f67faeb66cef933eec38bef71b53ef4086a2aa2a5b109b6ee531c751910160405180910390a15060019392505050565b60008181526007602052604081206060916133ed8461112c565b156133f9575082613415565b61341284600161340887612e4f565b6105c79190614e2c565b90505b600061342e61342386611e6f565b63ffffffff16613f16565b61343786613f16565b613442876020613f5a565b604051602001613454939291906150b8565b604051602081830303815290604052905060006134718487612edd565b6134b0576040518060400160405280600881526020017f696e6163746976650000000000000000000000000000000000000000000000008152506134e7565b6040518060400160405280600681526020017f61637469766500000000000000000000000000000000000000000000000000008152505b8454613514907801000000000000000000000000000000000000000000000000900463ffffffff16613f16565b855461352c90600160a01b900463ffffffff16613f16565b61353586613f16565b613540876020613f5a565b8854613554906001600160a01b0316614028565b60018a015461356b906001600160a01b0316614028565b61358d8b60010154600160a01b90046b40000000000000000000000016151590565b6135cc576040518060400160405280600581526020017f66616c7365000000000000000000000000000000000000000000000000000000815250613603565b6040518060400160405280600481526020017f74727565000000000000000000000000000000000000000000000000000000008152505b60405160200161361a9897969594939291906151a2565b60405160208183030381529060405290506136648460020161363b88612939565b84846040516020016136509493929190615380565b6040516020818303038152906040526140cb565b6040516020016136749190615573565b604051602081830303815290604052945050505050919050565b6000898152600760205260409020600281016136ab898b83614f58565b5080546001600160a01b038088167fffffffffffffffffffffffff000000000000000000000000000000000000000063ffffffff8b16600160a01b0281167fffffffffffffffff000000000000000000000000000000000000000000000000909416939093171783556001830180549188169190921617905560038101613733838583614f58565b508361374b576b800000000000000000000000613759565b6bc000000000000000000000005b8160010160146101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f4d38ad12136291322128027bb8c3af7db2261be395032c1d26dd0ad20cf294158a8a8a8a8a8a8a8a8a6040516137ca999897969594939291906155b8565b60405180910390a150505050505050505050565b6001600160a01b038216600081815260208181526040808320858452825280832060019081905560078352818420805463ffffffff780100000000000000000000000000000000000000000000000080830482168501909116027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617905581518681529283015233917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291015b60405180910390a45050565b6138a43382612405565b1580156138b857506138b63382611117565b155b15611d2f576040517fbc3a32ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138f98888611ef3565b61392f576040517f402128fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff88166000908152600360205260409020548015613a2757600060e06139588b611e86565b63ffffffff16901b905061396c3382611117565b6139e5577fffffffff00000000000000000000000000000000000000000000000000000000808a169083168082148015906139a75750828214155b156139de576040517f899c1ba600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050613a25565b6139ef8a8a611eb6565b613a25576040517f899c1ba600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b507fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1660008181526007602052604090206001600160a01b03881615613ada5780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03891690811782556040805184815260208101929092527f5f59e4feecec6992840f6f7c313415b21889d4445ea82fefd7f620aeb194461d910160405180910390a15b6001600160a01b03871615613b5c576001810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0389169081179091556040805184815260208101929092527ff68bc34e5f23b18f8e3a63ff51c021c8fbc9266113a63a4c73a66b4ad9033638910160405180910390a15b848015613beb57611b58811115613b9f576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201613bae878983614f58565b507fb8350431af3841b22f977d0e8fd4be94669ef68e2db1d7f02a731d5d09ed44cc838888604051613be293929190615044565b60405180910390a15b50828015613c7b57611b58811115613c2f576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038201613c3e858783614f58565b507f26bb402a5ddddf109a54486ab4d62a69ae00966cbc94c57075787f70bf9cba6c838686604051613c7293929190615044565b60405180910390a15b63ffffffff8b166000818152600360209081526040918290208d9055815192835282018c90527fd89812e6ebd66026da23b9c10923f8ec2d47c2f4bea260250e66d05ab76f6b89910160405180910390a15050505050505050505050565b6001600160a01b03831660009081526020818152604080832087845290915281205415613d1a57821580613d0b575081155b15613d1a57613d1a8486613de8565b60008581526008602090815260408083206001600160a01b038816845290915290205460ff16151582151503613de0575060008481526008602090815260408083206001600160a01b03871680855290835292819020805485157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116811790915581518881529283019390935291158183015290516001917ff95db9732f1ede51ad19afa6ee380168a1be58df547533ff009c18ceacd01ef5919081900360600190a15b949350505050565b6001600160a01b03821660008181526020818152604080832085845282528083208390556007825280832080547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff811678010000000000000000000000000000000000000000000000009182900463ffffffff9081166000190116909102179055805185815260019281019290925291929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910161388e565b8015613ee7575060010180546001600160a01b0381166b800000000000000000000000600160a01b928390046bffffffffffffffffffffffff1617909102179055565b5060010180546001600160a01b038116600160a01b918290046b7fffffffffffffffffffffff16909102179055565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a900480613f305750819003601f19909101908152919050565b60408051601f196062600185901b01811690910191829052600091019081526f30313233343536373839616263646566600f5280835b600f8116517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8401936000190153600f8160041c165183536000199093019260081c83613f90578015613feb57632194895a6000526004601cfd5b50613078601f198301528190036002017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffde90910190815292915050565b60408051606001908190526f30313233343536373839616263646566600f526014825b600f8116517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8401936000190153600f8160041c165183536000199091019060081c8161404b575050613078601f19820152602a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffde909101908152919050565b6060610b84826000806060835180156141cb576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526102308515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f03603f52602083018181015b6003880197508751603f8160121c16518353603f81600c1c16516001840153603f8160061c16516002840153603f81165160038401535060048201915080821061414457600384068680156141a4576001821482151501850387526141bc565b603d821515850353603d6001831460011b8503538487525b5050601f01601f191660405250505b509392505050565b80356001600160a01b03811681146141ea57600080fd5b919050565b6000806040838503121561420257600080fd5b61420b836141d3565b946020939093013593505050565b6000806040838503121561422c57600080fd5b8235915061423c602084016141d3565b90509250929050565b60006020828403121561425757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6f57600080fd5b60005b838110156142a257818101518382015260200161428a565b50506000910152565b600081518084526142c3816020860160208601614287565b601f01601f19169290920160200192915050565b602081526000610c6f60208301846142ab565b803563ffffffff811681146141ea57600080fd5b60006020828403121561431057600080fd5b610c6f826142ea565b60006020828403121561432b57600080fd5b5035919050565b60008083601f84011261434457600080fd5b50813567ffffffffffffffff81111561435c57600080fd5b60208301915083602082850101111561437457600080fd5b9250929050565b60008060008060006060868803121561439357600080fd5b61439c866141d3565b9450602086013567ffffffffffffffff808211156143b957600080fd5b6143c589838a01614332565b909650945060408801359150808211156143de57600080fd5b506143eb88828901614332565b969995985093965092949392505050565b6000806040838503121561440f57600080fd5b8235915061423c602084016142ea565b60008083601f84011261443157600080fd5b50813567ffffffffffffffff81111561444957600080fd5b6020830191508360208260051b850101111561437457600080fd5b60008060008060008060008060a0898b03121561448057600080fd5b614489896141d3565b975061449760208a016141d3565b9650604089013567ffffffffffffffff808211156144b457600080fd5b6144c08c838d0161441f565b909850965060608b01359150808211156144d957600080fd5b6144e58c838d0161441f565b909650945060808b01359150808211156144fe57600080fd5b5061450b8b828c01614332565b999c989b5096995094979396929594505050565b60008060008060008060008060c0898b03121561453b57600080fd5b614544896142ea565b97506020890135965061455960408a016141d3565b955061456760608a016141d3565b9450608089013567ffffffffffffffff8082111561458457600080fd5b6145908c838d01614332565b909650945060a08b01359150808211156144fe57600080fd5b600080600080604085870312156145bf57600080fd5b843567ffffffffffffffff808211156145d757600080fd5b6145e38883890161441f565b909650945060208701359150808211156145fc57600080fd5b506146098782880161441f565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b8181101561464d57835183529284019291840191600101614631565b50909695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261469957600080fd5b8135602067ffffffffffffffff808311156146b6576146b6614659565b8260051b604051601f19603f830116810181811084821117156146db576146db614659565b6040529384528581018301938381019250878511156146f957600080fd5b83870191505b8482101561110c57614710826141d3565b835291830191908301906146ff565b60008060008060008060008060008060008060e08d8f03121561474157600080fd5b67ffffffffffffffff8d35111561475757600080fd5b6147648e8e358f0161441f565b909c509a5067ffffffffffffffff60208e0135111561478257600080fd5b6147928e60208f01358f0161441f565b909a50985067ffffffffffffffff60408e013511156147b057600080fd5b6147c08e60408f01358f0161441f565b909850965067ffffffffffffffff60608e013511156147de57600080fd5b6147ee8e60608f01358f01614688565b955067ffffffffffffffff60808e0135111561480957600080fd5b6148198e60808f01358f01614688565b945067ffffffffffffffff60a08e0135111561483457600080fd5b6148448e60a08f01358f0161441f565b909450925067ffffffffffffffff60c08e0135111561486257600080fd5b6148728e60c08f01358f0161441f565b81935080925050509295989b509295989b509295989b565b6000806040838503121561489d57600080fd5b6148a6836142ea565b915061423c602084016141d3565b600080604083850312156148c757600080fd5b61420b836142ea565b803580151581146141ea57600080fd5b600080604083850312156148f357600080fd5b6148fc836141d3565b915061423c602084016148d0565b60008060006060848603121561491f57600080fd5b8335925061492f602085016141d3565b915061493d604085016141d3565b90509250925092565b6000806020838503121561495957600080fd5b823567ffffffffffffffff81111561497057600080fd5b61497c8582860161441f565b90969095509350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156149fb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526149e98583516142ab565b945092850192908501906001016149af565b5092979650505050505050565b60008060008060808587031215614a1e57600080fd5b84359350614a2e602086016141d3565b9250614a3c604086016148d0565b9150614a4a606086016148d0565b905092959194509250565b600080600080600080600080600060e08a8c031215614a7357600080fd5b8935985060208a013567ffffffffffffffff80821115614a9257600080fd5b614a9e8d838e01614332565b909a509850889150614ab260408d016142ea565b9750614ac060608d016141d3565b9650614ace60808d016141d3565b9550614adc60a08d016148d0565b945060c08c0135915080821115614af257600080fd5b50614aff8c828d01614332565b915080935050809150509295985092959850929598565b60008060408385031215614b2957600080fd5b8235915061423c602084016148d0565b6000610120808352614b4d8184018d6142ab565b63ffffffff8c811660208601528b1660408501526001600160a01b038a811660608601528916608085015283810360a08501529050614b8c81886142ab565b61ffff9690961660c0840152505091151560e08301521515610100909101529695505050505050565b600080600060408486031215614bca57600080fd5b83359250602084013567ffffffffffffffff811115614be857600080fd5b614bf486828701614332565b9497909650939450505050565b60008060408385031215614c1457600080fd5b6148a6836141d3565b60008060008060008060a08789031215614c3657600080fd5b614c3f876141d3565b9550614c4d602088016141d3565b94506040870135935060608701359250608087013567ffffffffffffffff811115614c7757600080fd5b614c8389828a01614332565b979a9699509497509295939492505050565b60008060408385031215614ca857600080fd5b82359150602083013561ffff81168114614cc157600080fd5b809150509250929050565b600181811c90821680614ce057607f821691505b602082108103612ea8577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61ffff81811683821601908082111561099f5761099f614d19565b600063ffffffff808316818103614d7c57614d7c614d19565b6001019392505050565b60008151614d98818560208601614287565b9290920192915050565b60008251614db4818460208701614287565b9190910192915050565b60008060408385031215614dd157600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614e2357600080fd5b610c6f826141d3565b63ffffffff82811682821603908082111561099f5761099f614d19565b81810381811115610b8457610b84614d19565b8082028115828204841417610b8457610b84614d19565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614ea857600080fd5b83018035915067ffffffffffffffff821115614ec357600080fd5b60200191503681900382131561437457600080fd5b600060208284031215614eea57600080fd5b610c6f826148d0565b600061ffff808316818103614d7c57614d7c614d19565b601f821115610e3c57600081815260208120601f850160051c81016020861015614f315750805b601f850160051c820191505b81811015614f5057828155600101614f3d565b505050505050565b67ffffffffffffffff831115614f7057614f70614659565b614f8483614f7e8354614ccc565b83614f0a565b6000601f841160018114614fb85760008515614fa05750838201355b600019600387901b1c1916600186901b178355615012565b600083815260209020601f19861690835b82811015614fe95786850135825560209485019460019092019101614fc9565b50868210156150065760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b838152604060208201526000610cd6604083018486615019565b63ffffffff81811683821601908082111561099f5761099f614d19565b80516020808301519190811015612ea85760001960209190910360031b1b16919050565b6000602082840312156150b157600080fd5b5051919050565b7f22646f6d61696e223a20220000000000000000000000000000000000000000008152600084516150f081600b850160208901614287565b7f222c20226964223a202200000000000000000000000000000000000000000000600b91840191820152845161512d816015840160208901614287565b7f222c2022707265747479206964223a202200000000000000000000000000000060159290910191820152835161516b816026840160208801614287565b7f222c0000000000000000000000000000000000000000000000000000000000006026929091019182015260280195945050505050565b7f22737461747573223a20220000000000000000000000000000000000000000008152600089516151da81600b850160208e01614287565b7f222c202263757272656e7420737570706c79223a202200000000000000000000600b918401918201528951615217816021840160208e01614287565b01615244602182017f222c2022737570706c7920636170223a202200000000000000000000000000009052565b615251603382018a614d86565b7f222c202261646d696e2028696429223a20220000000000000000000000000000815290506152836012820189614d86565b7f222c202261646d696e202870726574747920696429223a202200000000000000815290506152b56019820188614d86565b7f222c2022656c69676962696c697479206d6f64756c65223a2022000000000000815290506152e7601a820187614d86565b7f222c2022746f67676c65206d6f64756c65223a20220000000000000000000000815290506153196015820186614d86565b7f222c20226d757461626c65223a202200000000000000000000000000000000008152905061534b600f820185614d86565b7f220000000000000000000000000000000000000000000000000000000000000081526001019b9a5050505050505050505050565b7f7b226e616d65223a20220000000000000000000000000000000000000000000081527f4861740000000000000000000000000000000000000000000000000000000000600a8201527f222c20226465736372697074696f6e223a202200000000000000000000000000600d820152600060206000875461540081614ccc565b60018281168015615418576001811461544f5761547f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00841686890152858315158402890101945061547f565b8b6000528560002060005b848110156154755781548a820189015290830190870161545a565b5050858389010194505b507f222c2022696d616765223a20220000000000000000000000000000000000000084526154b0600d85018b614d86565b7f222c000000000000000000000000000000000000000000000000000000000000815294507f2270726f70657274696573223a2000000000000000000000000000000000000060028601527f7b000000000000000000000000000000000000000000000000000000000000006010860152615537615531601187018b614d86565b89614d86565b7f7d00000000000000000000000000000000000000000000000000000000000000808252918101919091526002019a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516155ab81601d850160208701614287565b91909101601d0192915050565b89815260e0602082015260006155d260e083018a8c615019565b63ffffffff891660408401526001600160a01b0388811660608501528716608084015285151560a084015282810360c0840152615610818587615019565b9c9b50505050505050505050505056fea26469706673582212203620611cb556a5437bbe468fe951b4a09dced80631c01465ad1b2a933f77eb6e64736f6c63430008110033000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000010486174732050726f746f636f6c207631000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f6261666b726569666c657a706b336b6a7a367a737632337062766f777461746e6435686d71666b64726f333378356d6832617a6c686e6533616834000000000000000000000000000000000000000000000000000000000000Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103a35760003560e01c806382afd23b116101e9578063d0221ee01161010f578063e985e9c5116100ad578063fb2849171161007c578063fb284917146108e7578063fb2aaa4c14610914578063fc3e05a414610927578063fd8a71631461093757600080fd5b8063e985e9c514610885578063f242432a146108b3578063f737401e146108c1578063f8f353b6146108d457600080fd5b8063d80a8434116100e9578063d80a843414610839578063d9fd995b1461084c578063e05209ff1461085f578063e33dd5c91461087257600080fd5b8063d0221ee0146107eb578063d3272ba8146107fe578063d395acf81461081157600080fd5b8063a2be079b11610187578063b052925e11610156578063b052925e14610792578063b56f7562146107a5578063c43dc177146107b8578063cead6304146107cb57600080fd5b8063a2be079b1461071e578063a4a1f06c1461074c578063ac9650d81461075f578063afaae55a1461077f57600080fd5b80639d6ccb9f116101c35780639d6ccb9f146106d75780639fe3b510146106ea578063a22cb465146106fd578063a29e8fa61461070b57600080fd5b806382afd23b146106885780638c0760771461069b5780639aa22f6d146106c457600080fd5b8063499c05e8116102ce578063641f776e1161026c578063756329a41161023b578063756329a41461063c5780637903e56b1461064f5780637a9df89f1461066257806380198fad1461067557600080fd5b8063641f776e146105cc5780636743175a146105df5780637178fb51146105f257806374f82e301461063457600080fd5b806354a1826c116102a857806354a1826c1461054f57806357f607721461056257806359701e44146105a65780635d5eb5af146105b957600080fd5b8063499c05e8146104f45780634e1273f41461051c57806352a6b2651461053c57600080fd5b80631a64dfad11610346578063378a848911610315578063378a8489146104a85780633fa9d544146104bb5780634352409a146104ce57806345fffac8146104e157600080fd5b80631a64dfad1461046157806321dbb8cb146104745780632eb2c2d614610487578063376792b91461049557600080fd5b806306fdde031161038257806306fdde03146104065780630b328e261461041b5780630e89341c1461043b5780631183a8c01461044e57600080fd5b8062fdd58e146103a85780630109f854146103ce57806301ffc9a7146103e3575b600080fd5b6103bb6103b63660046141ef565b61094a565b6040519081526020015b60405180910390f35b6103e16103dc366004614219565b6109a6565b005b6103f66103f1366004614245565b610af1565b60405190151581526020016103c5565b61040e610b8a565b6040516103c591906142d7565b6103bb6104293660046142fe565b60036020526000908152604090205481565b61040e610449366004614319565b610c18565b6103bb61045c366004614319565b610c23565b6103bb61046f36600461437b565b610c76565b6103e16104823660046143fc565b610cdf565b6103e16103a3366004614464565b6103e16104a336600461451f565b610e41565b6103e16104b636600461451f565b610ec4565b6103f66104c9366004614219565b610f75565b6103f66104dc3660046141ef565b611117565b6103f66104ef366004614319565b61112c565b610507610502366004614319565b611169565b60405163ffffffff90911681526020016103c5565b61052f61052a3660046145a9565b611333565b6040516103c59190614615565b6103f661054a3660046145a9565b611434565b6103f661055d3660046141ef565b6114d4565b61058e610570366004614319565b6000908152600760205260409020600101546001600160a01b031690565b6040516001600160a01b0390911681526020016103c5565b6103e16105b4366004614219565b61166e565b6103bb6105c73660046143fc565b611787565b6103f66105da366004614219565b611814565b6103f66105ed366004614319565b6119e1565b610507610600366004614319565b6000908152600760205260409020547801000000000000000000000000000000000000000000000000900463ffffffff1690565b61040e611a69565b6103e161064a366004614319565b611a76565b6103f661065d36600461471f565b611b49565b6103e1610670366004614319565b611cd2565b6103e161068336600461488a565b611d32565b6103f6610696366004614319565b611e57565b61058e6106a9366004614319565b6000908152600760205260409020546001600160a01b031690565b6105076106d2366004614319565b611e6f565b6105076106e53660046142fe565b611e86565b6103f66106f83660046148b4565b611eb6565b6103e16103a33660046148e0565b6103f66107193660046148b4565b611ef3565b6103f661072c366004614219565b600860209081526000928352604080842090915290825290205460ff1681565b6103e161075a36600461490a565b611f4f565b61077261076d366004614946565b612194565b6040516103c59190614988565b6103f661078d366004614a08565b612215565b6103bb6107a0366004614a55565b61227c565b6103f66107b33660046141ef565b612405565b6103f66107c6366004614b16565b61254c565b6103bb6107d93660046142fe565b60026020526000908152604090205481565b6103e16107f93660046148b4565b6125aa565b6103f661080c366004614319565b612628565b61082461081f366004614319565b612687565b6040516103c599989796959493929190614b39565b6103f66108473660046141ef565b6127f5565b6103e161085a366004614bb5565b61280f565b6103bb61086d3660046143fc565b61290a565b61040e610880366004614319565b612939565b6103f6610893366004614c01565b600160209081526000928352604080842090915290825290205460ff1681565b6103e16103a3366004614c1d565b6103e16108cf366004614bb5565b612cd7565b6103bb6108e2366004614c95565b612dd2565b6105076108f5366004614319565b600090815260076020526040902054600160a01b900463ffffffff1690565b610507610922366004614319565b612e4f565b6005546105079063ffffffff1681565b6103f6610945366004614319565b612eae565b60008181526007602052604081206109628184612edd565b8015610974575061097484828561301f565b1561099f576001600160a01b0384166000908152602081815260408083208684529091529020545b91505b5092915050565b6001600160a01b0381166109e6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109ef826131d1565b6000828152600760205260409020610a1f8160010154600160a01b90046b40000000000000000000000016151590565b610a55576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610a62838661321a565b915091508115610a7857610a76858261335a565b505b6001830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0386169081179091556040805187815260208101929092527ff68bc34e5f23b18f8e3a63ff51c021c8fbc9266113a63a4c73a66b4ad9033638910160405180910390a15050505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610b8457507f0e89341c000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60048054610b9790614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc390614ccc565b8015610c105780601f10610be557610100808354040283529160200191610c10565b820191906000526020600020905b815481529060010190602001808311610bf357829003601f168201915b505050505081565b6060610b84826133d3565b6000818152600760205260408120548190610c63907c0100000000000000000000000000000000000000000000000000000000900461ffff166001614d48565b9050610c6f8382612dd2565b9392505050565b6005805460009160e0918390610c919063ffffffff16614d63565b91906101000a81548163ffffffff021916908363ffffffff160217905563ffffffff16901b9050610ccc818686600160008060008a8a61368e565b610cd686826137de565b95945050505050565b610ce8826131d1565b6000828152600760205260409020610d188160010154600160a01b90046b40000000000000000000000016151590565b610d4e576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805463ffffffff780100000000000000000000000000000000000000000000000090910481169083161015610daf576040517fd2955d3b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805463ffffffff838116600160a01b9092041614610e3c5780547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff16600160a01b63ffffffff84169081029190911782556040805185815260208101929092527fb1141d53812d55ffe5a4ca05e686728ebe52d8e4fc66057d6e54724c01a77f5691015b60405180910390a15b505050565b7fffffffff0000000000000000000000000000000000000000000000000000000060e089901b16610e71816131d1565b600e610e7c89612e4f565b63ffffffff161015610ea057610e9b610e96896001612dd2565b6131d1565b610ea9565b610ea98861389a565b610eb989898989898989896138ef565b505050505050505050565b600e610ecf88612e4f565b63ffffffff161015610eee57610ee9610e96886001612dd2565b610ef7565b610ef78761389a565b63ffffffff88166000908152600260205260409020548714610f45576040517fbcb31ea200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8816600090815260026020526040812055610f6b88888888888888886138ef565b5050505050505050565b6000828152600760205260408082205490516001600160a01b038481166024830152604482018690528392839283928392169060640160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd683872000000000000000000000000000000000000000000000000000000001790525161100e9190614da2565b600060405180830381855afa9150503d8060008114611049576040519150601f19603f3d011682016040523d82523d6000602084013e61104e565b606091505b5091509150818015611061575080516040145b156110cc576000808280602001905181019061107d9190614dbe565b915091506002821080156110915750600281105b156110cc57806001146110a55760006110a8565b60015b94508480156110b75750816001145b6110c25760006110c5565b60015b95506110fe565b6040517f8bd78b5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061110c87878686613cd9565b979650505050505050565b600080611124848461094a565b119392505050565b600061113782612eae565b8015610b8457506003600061114b84611e6f565b63ffffffff1681526020810191909152604001600020541592915050565b60007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216810361119857506000919050565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff82166000036111c457506001919050565b77ffffffffffffffffffffffffffffffffffffffffffffffff82166000036111ee57506002919050565b75ffffffffffffffffffffffffffffffffffffffffffff821660000361121657506003919050565b6001600160a01b03821660000361122f57506004919050565b71ffffffffffffffffffffffffffffffffffff821660000361125357506005919050565b6fffffffffffffffffffffffffffffffff821660000361127557506006919050565b6dffffffffffffffffffffffffffff821660000361129557506007919050565b6bffffffffffffffffffffffff82166000036112b357506008919050565b69ffffffffffffffffffff82166000036112cf57506009919050565b67ffffffffffffffff82166000036112e95750600a919050565b65ffffffffffff82166000036113015750600b919050565b63ffffffff82166000036113175750600c919050565b61ffff821660000361132b5750600d919050565b50600e919050565b606083821461136e576040517ffc5221bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8367ffffffffffffffff81111561138757611387614659565b6040519080825280602002602001820160405280156113b0578160200160208202803683370190505b50905060005b8481101561142b576114068686838181106113d3576113d3614de2565b90506020020160208101906113e89190614e11565b8585848181106113fa576113fa614de2565b9050602002013561094a565b82828151811061141857611418614de2565b60209081029190910101526001016113b6565b50949350505050565b600083828114611470576040517ffc5221bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156114c7576114be87878381811061149057611490614de2565b905060200201358686848181106114a9576114a9614de2565b90506020020160208101906105da9190614e11565b50600101611473565b5060019695505050505050565b6000818152600760205260408082205490516001600160a01b038581166024830152604482018590528392839291169060640160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd683872000000000000000000000000000000000000000000000000000000001790525161156a9190614da2565b600060405180830381855afa9150503d80600081146115a5576040519150601f19603f3d011682016040523d82523d6000602084013e6115aa565b606091505b50915091508180156115bd575080516040145b1561163c57600080828060200190518101906115d99190614dbe565b915091506002821080156115ed5750600281105b1561160b5780600114611601576000611604565b60015b9450611635565b60008681526008602090815260408083206001600160a01b038b16845290915290205460ff161594505b5050611666565b60008481526008602090815260408083206001600160a01b038916845290915290205460ff161592505b505092915050565b6001600160a01b0381166116ae576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116b7826131d1565b60008281526007602052604090206116e78160010154600160a01b90046b40000000000000000000000016151590565b61171d576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03831690811782556040805185815260208101929092527f5f59e4feecec6992840f6f7c313415b21889d4445ea82fefd7f620aeb194461d9101610e33565b6000806003600061179786611e6f565b63ffffffff1663ffffffff168152602001908152602001600020549050806000036117ce576117c6848461290a565b915050610b84565b60006117de61092286600061290a565b90508363ffffffff168163ffffffff161161180a576118018561086d8387614e2c565b92505050610b84565b610cd68285611787565b60008281526007602052604081208054600160a01b900463ffffffff168203611871576040517fae231814000000000000000000000000000000000000000000000000000000008152600481018590526024015b60405180910390fd5b61187b83856127f5565b6118b1576040517ff8eb54de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118bb8185612edd565b6118f1576040517fe629aba300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118fa846131d1565b805463ffffffff600160a01b8204811678010000000000000000000000000000000000000000000000009092041610611962576040517f0cd96ab800000000000000000000000000000000000000000000000000000000815260048101859052602401611868565b6001600160a01b038316600090815260208181526040808320878452909152902054156119cd576040517f643d20830000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101859052604401611868565b6119d783856137de565b5060019392505050565b60006119ec82612eae565b156119f957506001919050565b6000611a0483611169565b9050600080611a14600184614e2c565b63ffffffff1690505b8015611a5e57611a2e81600e614e49565b611a39906010614e5c565b85901c91508161ffff16600003611a5557506000949350505050565b60001901611a1d565b506001949350505050565b60068054610b9790614ccc565b611a7f816131d1565b6000818152600760205260409020611aaf8160010154600160a01b90046b40000000000000000000000016151590565b611ae5576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001810180546001600160a01b038116600160a01b918290046bbfffffffffffffffffffffff169091021790556040518281527f10bb2a0010ffb3a037a886128a37f6574be73acb0ee505c37a4f12e8d290ed8e9060200160405180910390a15050565b60008b818b82148015611b5b5750818a145b8015611b675750885182145b8015611b735750875182145b8015611b7e57508186145b8015611b8957508184145b905080611bc2576040517ffc5221bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060005b81811015611cbd57611cb48f8f83818110611be357611be3614de2565b905060200201358e8e84818110611bfc57611bfc614de2565b9050602002810190611c0e9190614e73565b8e8e86818110611c2057611c20614de2565b9050602002016020810190611c3591906142fe565b8d8681518110611c4757611c47614de2565b60200260200101518d8781518110611c6157611c61614de2565b60200260200101518d8d89818110611c7b57611c7b614de2565b9050602002016020810190611c909190614ed8565b8c8c8a818110611ca257611ca2614de2565b90506020028101906107a09190614e73565b50600101611bc6565b5060019e9d5050505050505050505050505050565b3360009081526020818152604080832084845290915290205460011115611d25576040517fae9f0ba500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d2f3382613de8565b50565b7fffffffff0000000000000000000000000000000000000000000000000000000060e083901b16611d62816131d1565b6001600160a01b0382161580611d7f5750611d7d8282611117565b155b15611db6576040517fa60d071800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff83166000818152600360209081526040808320839055600282528083208390558483526007825280832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811682556001820180549091169055815194855291840192909252917fd89812e6ebd66026da23b9c10923f8ec2d47c2f4bea260250e66d05ab76f6b8991015b60405180910390a150505050565b6000818152600760205260408120610b849083612edd565b6000611e7d600e6010614e5c565b9190911c919050565b63ffffffff8116600090815260036020526040812054808203611eaa575090919050565b610c6f6106e582611e6f565b600080611ec284611e86565b90506000611ecf84611e6f565b90506000611edc82611e86565b63ffffffff93841693169290921495945050505050565b600081600003611f0557506001610b84565b6000611f1083611e6f565b90508063ffffffff168463ffffffff1603611f2f576000915050610b84565b63ffffffff8116600090815260036020526040902054610cd68582611ef3565b611f58836131d1565b611f618361112c565b611fc257600083815260076020526040902060010154600160a01b90046b40000000000000000000000016611fc2576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0382166000908152602081815260408083208684529091529020546001111561201e576040517fae9f0ba500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03811660009081526020818152604080832086845290915290205415612089576040517f643d20830000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260248101849052604401611868565b61209381846127f5565b6120c9576040517ff8eb54de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602052604090206120e19084612edd565b612117576040517fe629aba300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038281166000818152602081815260408083208884528252808320839055938516808352828252848320888452825291849020600190819055845188815291820152909233917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a4505050565b6060602060005281602052816121aa5760406000f35b60408260051b8085604037818101905b82518601604082018135602083018237600080833583305af46121e1573d6000803e3d6000fd5b8285526020850194503d81523d6000602083013e50503d01603f0167ffffffffffffffe0168183106121ba57604081016000f35b600084815260076020526040812080546001600160a01b03163314612266576040517f8bd78b5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61227286868686613cd9565b9695505050505050565b600061ffff8a16156122ba576040517f7c9225c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0386166122fa576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851661233a576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123438a6119e1565b612379576040517fb43a065000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123828a610c23565b905061238d816131d1565b61239e818a8a8a8a8a8a8a8a61368e565b60008a81526007602052604090208054601c906123de907c0100000000000000000000000000000000000000000000000000000000900461ffff16614ef3565b91906101000a81548161ffff021916908361ffff1602179055509998505050505050505050565b600080600061241384612eae565b15612481576003600061242586611e6f565b63ffffffff1663ffffffff16815260200190815260200160002054915081600003612454576118018585611117565b61245e8583611117565b1561246e57600192505050610b84565b61247782611169565b9050819350612499565b600161248c85611169565b6124969190614e2c565b90505b63ffffffff8116156124cc576124b3856104dc868461290a565b156124c357600192505050610b84565b60001901612499565b6124db856104dc86600061290a565b156124eb57600192505050610b84565b600360006124f886611e6f565b63ffffffff1663ffffffff1681526020019081526020016000205491508160000361252857600092505050610b84565b6125328583611117565b1561254257600192505050610b84565b610cd68583612405565b600082815260076020526040812060018101546001600160a01b031633146125a0576040517feaca8b6500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61099c848461335a565b7fffffffff0000000000000000000000000000000000000000000000000000000060e083901b166125da816131d1565b63ffffffff8316600081815260026020908152604091829020859055815192835282018490527f81ae7626a9656578e59b92a7575fdbd9cfe59e467c1e88334e305e0b428e76669101610e33565b60008181526007602052604081208180612642838661321a565b915091508161267d576040517feaca8b6500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cd6858261335a565b60606000806000806060600080600080600760008c815260200190815260200160002090508060020180546126bb90614ccc565b80601f01602080910402602001604051908101604052809291908181526020018280546126e790614ccc565b80156127345780601f1061270957610100808354040283529160200191612734565b820191906000526020600020905b81548152906001019060200180831161271757829003601f168201915b505084546001860154949e5063ffffffff600160a01b820481169e5078010000000000000000000000000000000000000000000000008204169c506001600160a01b039081169b5093909316985061279192508d91506129399050565b815460018301549196507c0100000000000000000000000000000000000000000000000000000000900461ffff1694506b400000000000000000000000600160a01b90910416151592506127e5818c612edd565b9150509193959799909294969850565b6000818152600760205260408120610c6f9084908461301f565b611b5881111561284b576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612854836131d1565b600083815260076020526040902061286b8461112c565b6128c7576128918160010154600160a01b90046b40000000000000000000000016151590565b6128c7576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381016128d6838583614f58565b507f26bb402a5ddddf109a54486ab4d62a69ae00966cbc94c57075787f70bf9cba6c848484604051611e4993929190615044565b60008061291e63ffffffff8416600e614e49565b612929906010614e5c565b600019901b939093169392505050565b6000818152600760205260408120600381018054606093919061295b90614ccc565b80601f016020809104026020016040519081016040528092919081815260200182805461298790614ccc565b80156129d45780601f106129a9576101008083540402835291602001916129d4565b820191906000526020600020905b8154815290600101906020018083116129b757829003601f168201915b505050505090506000815111156129ec579392505050565b60006129f785612e4f565b63ffffffff16905080600003612a9c5760068054612a1490614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612a4090614ccc565b8015612a8d5780601f10612a6257610100808354040283529160200191612a8d565b820191906000526020600020905b815481529060010190602001808311612a7057829003601f168201915b50505050509350505050919050565b600080612aaa600184614e49565b90505b8015612b7e57612abd8782611787565b6000818152600760205260409020600381018054919750919350612ae090614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612b0c90614ccc565b8015612b595780601f10612b2e57610100808354040283529160200191612b59565b820191906000526020600020905b815481529060010190602001808311612b3c57829003601f168201915b50505050509350600084511115612b7557509195945050505050565b60001901612aad565b50612b8a866000611787565b6000818152600760205260409020600381018054919650919250612bad90614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612bd990614ccc565b8015612c265780601f10612bfb57610100808354040283529160200191612c26565b820191906000526020600020905b815481529060010190602001808311612c0957829003601f168201915b50505050509250600083511115612c41575090949350505050565b60068054612c4e90614ccc565b80601f0160208091040260200160405190810160405280929190818152602001828054612c7a90614ccc565b8015612cc75780601f10612c9c57610100808354040283529160200191612cc7565b820191906000526020600020905b815481529060010190602001808311612caa57829003601f168201915b5050505050945050505050919050565b611b58811115612d13576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d1c836131d1565b6000838152600760205260409020612d338461112c565b612d8f57612d598160010154600160a01b90046b40000000000000000000000016151590565b612d8f576040517fbb7790e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101612d9e838583614f58565b507fb8350431af3841b22f977d0e8fd4be94669ef68e2db1d7f02a731d5d09ed44cc848484604051611e4993929190615044565b60008060005b600e811015612e1c57600019602060108302011c9150848216600003612e1457806001600e03036010028461ffff16901b851792505050610b84565b600101612dd8565b506040517f7c9225c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612e5a82611169565b9050600060036000612e6b85611e6f565b63ffffffff16815260208101919091526040016000205490508015612ea857612e9381612e4f565b612e9e83600161505e565b610c6f919061505e565b50919050565b60008082118015610b845750507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b600182015460405160248101839052600091829182916001600160a01b03169060440160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f96208f7a0000000000000000000000000000000000000000000000000000000017905251612f639190614da2565b600060405180830381855afa9150503d8060008114612f9e576040519150601f19603f3d011682016040523d82523d6000602084013e612fa3565b606091505b5091509150818015612fb6575080516020145b15613007576000612fc68261507b565b90506000819003612fda5760009350613001565b80600103612feb5760019350613001565b600180870154600160a01b9004605f1c16151593505b50611666565b600180860154600160a01b9004605f1c161515610cd6565b81546040516001600160a01b03858116602483015260448201849052600092839283929091169060640160408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd68387200000000000000000000000000000000000000000000000000000000179052516130ac9190614da2565b600060405180830381855afa9150503d80600081146130e7576040519150601f19603f3d011682016040523d82523d6000602084013e6130ec565b606091505b50915091508180156130ff575080516040145b1561319e5760008060008380602001905181019061311d9190614dbe565b915091506002821080156131315750600281105b1561316c5780600114613145576000613148565b60015b92508280156131575750816001145b613162576000613165565b60015b9550613196565b60008781526008602090815260408083206001600160a01b038d16845290915290205460ff161595505b5050506131c8565b60008481526008602090815260408083206001600160a01b038a16845290915290205460ff161592505b50509392505050565b6131db3382612405565b611d2f576040517ff921ec0100000000000000000000000000000000000000000000000000000000815233600482015260248101829052604401611868565b60008060008360405160240161323291815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f96208f7a00000000000000000000000000000000000000000000000000000000179052600187015490519192506060916001600160a01b03909116906132ae908490614da2565b600060405180830381855afa9150503d80600081146132e9576040519150601f19603f3d011682016040523d82523d6000602084013e6132ee565b606091505b509094509050838015613302575080516020145b1561334c5760008180602001905181019061331d919061509f565b9050806000036133305760009350613346565b806001036133415760019350613346565b600094505b50613351565b600093505b50509250929050565b6000828152600760205260408120600180820154600160a01b9004605f1c16151515158315151461099f5761338f8184613ea4565b6040805185815284151560208201527fd07f9843f67faeb66cef933eec38bef71b53ef4086a2aa2a5b109b6ee531c751910160405180910390a15060019392505050565b60008181526007602052604081206060916133ed8461112c565b156133f9575082613415565b61341284600161340887612e4f565b6105c79190614e2c565b90505b600061342e61342386611e6f565b63ffffffff16613f16565b61343786613f16565b613442876020613f5a565b604051602001613454939291906150b8565b604051602081830303815290604052905060006134718487612edd565b6134b0576040518060400160405280600881526020017f696e6163746976650000000000000000000000000000000000000000000000008152506134e7565b6040518060400160405280600681526020017f61637469766500000000000000000000000000000000000000000000000000008152505b8454613514907801000000000000000000000000000000000000000000000000900463ffffffff16613f16565b855461352c90600160a01b900463ffffffff16613f16565b61353586613f16565b613540876020613f5a565b8854613554906001600160a01b0316614028565b60018a015461356b906001600160a01b0316614028565b61358d8b60010154600160a01b90046b40000000000000000000000016151590565b6135cc576040518060400160405280600581526020017f66616c7365000000000000000000000000000000000000000000000000000000815250613603565b6040518060400160405280600481526020017f74727565000000000000000000000000000000000000000000000000000000008152505b60405160200161361a9897969594939291906151a2565b60405160208183030381529060405290506136648460020161363b88612939565b84846040516020016136509493929190615380565b6040516020818303038152906040526140cb565b6040516020016136749190615573565b604051602081830303815290604052945050505050919050565b6000898152600760205260409020600281016136ab898b83614f58565b5080546001600160a01b038088167fffffffffffffffffffffffff000000000000000000000000000000000000000063ffffffff8b16600160a01b0281167fffffffffffffffff000000000000000000000000000000000000000000000000909416939093171783556001830180549188169190921617905560038101613733838583614f58565b508361374b576b800000000000000000000000613759565b6bc000000000000000000000005b8160010160146101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f4d38ad12136291322128027bb8c3af7db2261be395032c1d26dd0ad20cf294158a8a8a8a8a8a8a8a8a6040516137ca999897969594939291906155b8565b60405180910390a150505050505050505050565b6001600160a01b038216600081815260208181526040808320858452825280832060019081905560078352818420805463ffffffff780100000000000000000000000000000000000000000000000080830482168501909116027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617905581518681529283015233917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291015b60405180910390a45050565b6138a43382612405565b1580156138b857506138b63382611117565b155b15611d2f576040517fbc3a32ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138f98888611ef3565b61392f576040517f402128fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff88166000908152600360205260409020548015613a2757600060e06139588b611e86565b63ffffffff16901b905061396c3382611117565b6139e5577fffffffff00000000000000000000000000000000000000000000000000000000808a169083168082148015906139a75750828214155b156139de576040517f899c1ba600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050613a25565b6139ef8a8a611eb6565b613a25576040517f899c1ba600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b507fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1660008181526007602052604090206001600160a01b03881615613ada5780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03891690811782556040805184815260208101929092527f5f59e4feecec6992840f6f7c313415b21889d4445ea82fefd7f620aeb194461d910160405180910390a15b6001600160a01b03871615613b5c576001810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0389169081179091556040805184815260208101929092527ff68bc34e5f23b18f8e3a63ff51c021c8fbc9266113a63a4c73a66b4ad9033638910160405180910390a15b848015613beb57611b58811115613b9f576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201613bae878983614f58565b507fb8350431af3841b22f977d0e8fd4be94669ef68e2db1d7f02a731d5d09ed44cc838888604051613be293929190615044565b60405180910390a15b50828015613c7b57611b58811115613c2f576040517fb11b2ad800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038201613c3e858783614f58565b507f26bb402a5ddddf109a54486ab4d62a69ae00966cbc94c57075787f70bf9cba6c838686604051613c7293929190615044565b60405180910390a15b63ffffffff8b166000818152600360209081526040918290208d9055815192835282018c90527fd89812e6ebd66026da23b9c10923f8ec2d47c2f4bea260250e66d05ab76f6b89910160405180910390a15050505050505050505050565b6001600160a01b03831660009081526020818152604080832087845290915281205415613d1a57821580613d0b575081155b15613d1a57613d1a8486613de8565b60008581526008602090815260408083206001600160a01b038816845290915290205460ff16151582151503613de0575060008481526008602090815260408083206001600160a01b03871680855290835292819020805485157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116811790915581518881529283019390935291158183015290516001917ff95db9732f1ede51ad19afa6ee380168a1be58df547533ff009c18ceacd01ef5919081900360600190a15b949350505050565b6001600160a01b03821660008181526020818152604080832085845282528083208390556007825280832080547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff811678010000000000000000000000000000000000000000000000009182900463ffffffff9081166000190116909102179055805185815260019281019290925291929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910161388e565b8015613ee7575060010180546001600160a01b0381166b800000000000000000000000600160a01b928390046bffffffffffffffffffffffff1617909102179055565b5060010180546001600160a01b038116600160a01b918290046b7fffffffffffffffffffffff16909102179055565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a900480613f305750819003601f19909101908152919050565b60408051601f196062600185901b01811690910191829052600091019081526f30313233343536373839616263646566600f5280835b600f8116517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8401936000190153600f8160041c165183536000199093019260081c83613f90578015613feb57632194895a6000526004601cfd5b50613078601f198301528190036002017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffde90910190815292915050565b60408051606001908190526f30313233343536373839616263646566600f526014825b600f8116517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8401936000190153600f8160041c165183536000199091019060081c8161404b575050613078601f19820152602a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffde909101908152919050565b6060610b84826000806060835180156141cb576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526102308515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f03603f52602083018181015b6003880197508751603f8160121c16518353603f81600c1c16516001840153603f8160061c16516002840153603f81165160038401535060048201915080821061414457600384068680156141a4576001821482151501850387526141bc565b603d821515850353603d6001831460011b8503538487525b5050601f01601f191660405250505b509392505050565b80356001600160a01b03811681146141ea57600080fd5b919050565b6000806040838503121561420257600080fd5b61420b836141d3565b946020939093013593505050565b6000806040838503121561422c57600080fd5b8235915061423c602084016141d3565b90509250929050565b60006020828403121561425757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6f57600080fd5b60005b838110156142a257818101518382015260200161428a565b50506000910152565b600081518084526142c3816020860160208601614287565b601f01601f19169290920160200192915050565b602081526000610c6f60208301846142ab565b803563ffffffff811681146141ea57600080fd5b60006020828403121561431057600080fd5b610c6f826142ea565b60006020828403121561432b57600080fd5b5035919050565b60008083601f84011261434457600080fd5b50813567ffffffffffffffff81111561435c57600080fd5b60208301915083602082850101111561437457600080fd5b9250929050565b60008060008060006060868803121561439357600080fd5b61439c866141d3565b9450602086013567ffffffffffffffff808211156143b957600080fd5b6143c589838a01614332565b909650945060408801359150808211156143de57600080fd5b506143eb88828901614332565b969995985093965092949392505050565b6000806040838503121561440f57600080fd5b8235915061423c602084016142ea565b60008083601f84011261443157600080fd5b50813567ffffffffffffffff81111561444957600080fd5b6020830191508360208260051b850101111561437457600080fd5b60008060008060008060008060a0898b03121561448057600080fd5b614489896141d3565b975061449760208a016141d3565b9650604089013567ffffffffffffffff808211156144b457600080fd5b6144c08c838d0161441f565b909850965060608b01359150808211156144d957600080fd5b6144e58c838d0161441f565b909650945060808b01359150808211156144fe57600080fd5b5061450b8b828c01614332565b999c989b5096995094979396929594505050565b60008060008060008060008060c0898b03121561453b57600080fd5b614544896142ea565b97506020890135965061455960408a016141d3565b955061456760608a016141d3565b9450608089013567ffffffffffffffff8082111561458457600080fd5b6145908c838d01614332565b909650945060a08b01359150808211156144fe57600080fd5b600080600080604085870312156145bf57600080fd5b843567ffffffffffffffff808211156145d757600080fd5b6145e38883890161441f565b909650945060208701359150808211156145fc57600080fd5b506146098782880161441f565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b8181101561464d57835183529284019291840191600101614631565b50909695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261469957600080fd5b8135602067ffffffffffffffff808311156146b6576146b6614659565b8260051b604051601f19603f830116810181811084821117156146db576146db614659565b6040529384528581018301938381019250878511156146f957600080fd5b83870191505b8482101561110c57614710826141d3565b835291830191908301906146ff565b60008060008060008060008060008060008060e08d8f03121561474157600080fd5b67ffffffffffffffff8d35111561475757600080fd5b6147648e8e358f0161441f565b909c509a5067ffffffffffffffff60208e0135111561478257600080fd5b6147928e60208f01358f0161441f565b909a50985067ffffffffffffffff60408e013511156147b057600080fd5b6147c08e60408f01358f0161441f565b909850965067ffffffffffffffff60608e013511156147de57600080fd5b6147ee8e60608f01358f01614688565b955067ffffffffffffffff60808e0135111561480957600080fd5b6148198e60808f01358f01614688565b945067ffffffffffffffff60a08e0135111561483457600080fd5b6148448e60a08f01358f0161441f565b909450925067ffffffffffffffff60c08e0135111561486257600080fd5b6148728e60c08f01358f0161441f565b81935080925050509295989b509295989b509295989b565b6000806040838503121561489d57600080fd5b6148a6836142ea565b915061423c602084016141d3565b600080604083850312156148c757600080fd5b61420b836142ea565b803580151581146141ea57600080fd5b600080604083850312156148f357600080fd5b6148fc836141d3565b915061423c602084016148d0565b60008060006060848603121561491f57600080fd5b8335925061492f602085016141d3565b915061493d604085016141d3565b90509250925092565b6000806020838503121561495957600080fd5b823567ffffffffffffffff81111561497057600080fd5b61497c8582860161441f565b90969095509350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156149fb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526149e98583516142ab565b945092850192908501906001016149af565b5092979650505050505050565b60008060008060808587031215614a1e57600080fd5b84359350614a2e602086016141d3565b9250614a3c604086016148d0565b9150614a4a606086016148d0565b905092959194509250565b600080600080600080600080600060e08a8c031215614a7357600080fd5b8935985060208a013567ffffffffffffffff80821115614a9257600080fd5b614a9e8d838e01614332565b909a509850889150614ab260408d016142ea565b9750614ac060608d016141d3565b9650614ace60808d016141d3565b9550614adc60a08d016148d0565b945060c08c0135915080821115614af257600080fd5b50614aff8c828d01614332565b915080935050809150509295985092959850929598565b60008060408385031215614b2957600080fd5b8235915061423c602084016148d0565b6000610120808352614b4d8184018d6142ab565b63ffffffff8c811660208601528b1660408501526001600160a01b038a811660608601528916608085015283810360a08501529050614b8c81886142ab565b61ffff9690961660c0840152505091151560e08301521515610100909101529695505050505050565b600080600060408486031215614bca57600080fd5b83359250602084013567ffffffffffffffff811115614be857600080fd5b614bf486828701614332565b9497909650939450505050565b60008060408385031215614c1457600080fd5b6148a6836141d3565b60008060008060008060a08789031215614c3657600080fd5b614c3f876141d3565b9550614c4d602088016141d3565b94506040870135935060608701359250608087013567ffffffffffffffff811115614c7757600080fd5b614c8389828a01614332565b979a9699509497509295939492505050565b60008060408385031215614ca857600080fd5b82359150602083013561ffff81168114614cc157600080fd5b809150509250929050565b600181811c90821680614ce057607f821691505b602082108103612ea8577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61ffff81811683821601908082111561099f5761099f614d19565b600063ffffffff808316818103614d7c57614d7c614d19565b6001019392505050565b60008151614d98818560208601614287565b9290920192915050565b60008251614db4818460208701614287565b9190910192915050565b60008060408385031215614dd157600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614e2357600080fd5b610c6f826141d3565b63ffffffff82811682821603908082111561099f5761099f614d19565b81810381811115610b8457610b84614d19565b8082028115828204841417610b8457610b84614d19565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614ea857600080fd5b83018035915067ffffffffffffffff821115614ec357600080fd5b60200191503681900382131561437457600080fd5b600060208284031215614eea57600080fd5b610c6f826148d0565b600061ffff808316818103614d7c57614d7c614d19565b601f821115610e3c57600081815260208120601f850160051c81016020861015614f315750805b601f850160051c820191505b81811015614f5057828155600101614f3d565b505050505050565b67ffffffffffffffff831115614f7057614f70614659565b614f8483614f7e8354614ccc565b83614f0a565b6000601f841160018114614fb85760008515614fa05750838201355b600019600387901b1c1916600186901b178355615012565b600083815260209020601f19861690835b82811015614fe95786850135825560209485019460019092019101614fc9565b50868210156150065760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b838152604060208201526000610cd6604083018486615019565b63ffffffff81811683821601908082111561099f5761099f614d19565b80516020808301519190811015612ea85760001960209190910360031b1b16919050565b6000602082840312156150b157600080fd5b5051919050565b7f22646f6d61696e223a20220000000000000000000000000000000000000000008152600084516150f081600b850160208901614287565b7f222c20226964223a202200000000000000000000000000000000000000000000600b91840191820152845161512d816015840160208901614287565b7f222c2022707265747479206964223a202200000000000000000000000000000060159290910191820152835161516b816026840160208801614287565b7f222c0000000000000000000000000000000000000000000000000000000000006026929091019182015260280195945050505050565b7f22737461747573223a20220000000000000000000000000000000000000000008152600089516151da81600b850160208e01614287565b7f222c202263757272656e7420737570706c79223a202200000000000000000000600b918401918201528951615217816021840160208e01614287565b01615244602182017f222c2022737570706c7920636170223a202200000000000000000000000000009052565b615251603382018a614d86565b7f222c202261646d696e2028696429223a20220000000000000000000000000000815290506152836012820189614d86565b7f222c202261646d696e202870726574747920696429223a202200000000000000815290506152b56019820188614d86565b7f222c2022656c69676962696c697479206d6f64756c65223a2022000000000000815290506152e7601a820187614d86565b7f222c2022746f67676c65206d6f64756c65223a20220000000000000000000000815290506153196015820186614d86565b7f222c20226d757461626c65223a202200000000000000000000000000000000008152905061534b600f820185614d86565b7f220000000000000000000000000000000000000000000000000000000000000081526001019b9a5050505050505050505050565b7f7b226e616d65223a20220000000000000000000000000000000000000000000081527f4861740000000000000000000000000000000000000000000000000000000000600a8201527f222c20226465736372697074696f6e223a202200000000000000000000000000600d820152600060206000875461540081614ccc565b60018281168015615418576001811461544f5761547f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00841686890152858315158402890101945061547f565b8b6000528560002060005b848110156154755781548a820189015290830190870161545a565b5050858389010194505b507f222c2022696d616765223a20220000000000000000000000000000000000000084526154b0600d85018b614d86565b7f222c000000000000000000000000000000000000000000000000000000000000815294507f2270726f70657274696573223a2000000000000000000000000000000000000060028601527f7b000000000000000000000000000000000000000000000000000000000000006010860152615537615531601187018b614d86565b89614d86565b7f7d00000000000000000000000000000000000000000000000000000000000000808252918101919091526002019a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516155ab81601d850160208701614287565b91909101601d0192915050565b89815260e0602082015260006155d260e083018a8c615019565b63ffffffff891660408401526001600160a01b0388811660608501528716608084015285151560a084015282810360c0840152615610818587615019565b9c9b50505050505050505050505056fea26469706673582212203620611cb556a5437bbe468fe951b4a09dced80631c01465ad1b2a933f77eb6e64736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000010486174732050726f746f636f6c207631000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f6261666b726569666c657a706b336b6a7a367a737632337062766f777461746e6435686d71666b64726f333378356d6832617a6c686e6533616834000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _name (string): Hats Protocol v1
Arg [1] : _baseImageURI (string): ipfs://bafkreiflezpk3kjz6zsv23pbvowtatnd5hmqfkdro33x5mh2azlhne3ah4
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000010
Arg [3] : 486174732050726f746f636f6c20763100000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000042
Arg [5] : 697066733a2f2f6261666b726569666c657a706b336b6a7a367a737632337062
Arg [6] : 766f777461746e6435686d71666b64726f333378356d6832617a6c686e653361
Arg [7] : 6834000000000000000000000000000000000000000000000000000000000000Loading...LoadingLoading...Loading
Loading...Loading
Loading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingOVERVIEW
Hats are programmable, revocable, and legible roles, which can be collectively controlled by a DAO. Hat-based roles can be flexibly imbued with responsibilities, authorities, accountabilities, context, and more.
Loading...LoadingMultichain Portfolio | 30 Chains
Chain Token Portfolio % Price Amount Value [ Download: CSV Export ][ Download: CSV Export ]A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.
Address QR Code
My Address - Private Name Tag or Note
My Name Tag:
Private Name Tags (up to 35 characters) can be used for easy identification of addressesPrivate Note:
A private note (up to 500 characters) can be attached to this address.
Please DO NOT store any passwords or private keys here.Compiler specific version warnings:
The compiled contract might be susceptible to VerbatimInvalidDeduplication (low-severity), FullInlinerNonExpressionSplitArgumentEvaluationOrder (low-severity), MissingSideEffectsOnSelectorAccess (low-severity) Solidity Compiler Bugs.
Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Before You Copy
Transaction Private Note
This website uses cookies to improve your experience. By continuing to use this website, you agree to its Terms and Privacy Policy.