ETH Price: $2,946.90 (+0.79%)
Gas: 0.03 Gwei
 

Overview

ETH Balance

57.231032877786436153 ETH

Eth Value

$168,654.32 (@ $2,946.90/ETH)

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Burn238815842025-11-26 7:58:4720 days ago1764143927IN
0x967040B0...7e57a2f04
0 ETH0.000146361.99801518
Burn238815582025-11-26 7:53:3520 days ago1764143615IN
0x967040B0...7e57a2f04
0 ETH0.000042831.05578253
Burn238494992025-11-21 19:38:1125 days ago1763753891IN
0x967040B0...7e57a2f04
0 ETH0.000171832.34581353
Safe Transfer Fr...238493792025-11-21 19:13:2325 days ago1763752403IN
0x967040B0...7e57a2f04
0 ETH0.000056342.39727544
Claim Owner Shar...237194612025-11-03 14:40:4743 days ago1762180847IN
0x967040B0...7e57a2f04
0 ETH0.000164853.02641926
Burn234725232025-09-30 1:27:2378 days ago1759195643IN
0x967040B0...7e57a2f04
0 ETH0.000096441.27055313
Burn233059052025-09-06 18:41:11101 days ago1757184071IN
0x967040B0...7e57a2f04
0 ETH0.000097911.3366524
Burn233059042025-09-06 18:40:59101 days ago1757184059IN
0x967040B0...7e57a2f04
0 ETH0.000105441.35099509
Burn232938262025-09-05 2:08:35103 days ago1757038115IN
0x967040B0...7e57a2f04
0 ETH0.00001660.2186921
Burn232137672025-08-24 21:53:59114 days ago1756072439IN
0x967040B0...7e57a2f04
0 ETH0.000098271.29464457
Burn232124832025-08-24 17:36:23114 days ago1756056983IN
0x967040B0...7e57a2f04
0 ETH0.000105551.3079162
Burn232124812025-08-24 17:35:59114 days ago1756056959IN
0x967040B0...7e57a2f04
0 ETH0.000100791.24892677
Burn232124792025-08-24 17:35:35114 days ago1756056935IN
0x967040B0...7e57a2f04
0 ETH0.000106461.31917571
Burn232124762025-08-24 17:34:59114 days ago1756056899IN
0x967040B0...7e57a2f04
0 ETH0.000115211.42761089
Burn231976602025-08-22 15:57:23116 days ago1755878243IN
0x967040B0...7e57a2f04
0 ETH0.000490996.70266065
Burn231441292025-08-15 4:44:23124 days ago1755233063IN
0x967040B0...7e57a2f04
0 ETH0.000169132.30896875
Burn231223232025-08-12 3:37:11127 days ago1754969831IN
0x967040B0...7e57a2f04
0 ETH0.000176962.4158595
Burn231043312025-08-09 15:15:23129 days ago1754752523IN
0x967040B0...7e57a2f04
0 ETH0.000093011.26982065
Burn231014212025-08-09 5:30:23130 days ago1754717423IN
0x967040B0...7e57a2f04
0 ETH0.000221953.02994811
Burn231014202025-08-09 5:30:11130 days ago1754717411IN
0x967040B0...7e57a2f04
0 ETH0.000231962.97187885
Burn231007692025-08-09 3:19:11130 days ago1754709551IN
0x967040B0...7e57a2f04
0 ETH0.000023510.32107316
Burn230964652025-08-08 12:52:35130 days ago1754657555IN
0x967040B0...7e57a2f04
0 ETH0.000180072.45830735
Burn230931842025-08-08 1:53:23131 days ago1754618003IN
0x967040B0...7e57a2f04
0 ETH0.00001840.25125695
Burn230665232025-08-04 8:31:23134 days ago1754296283IN
0x967040B0...7e57a2f04
0 ETH0.000016210.22140243
Burn230635112025-08-03 22:25:35135 days ago1754259935IN
0x967040B0...7e57a2f04
0 ETH0.000166982.19978683
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer238815842025-11-26 7:58:4720 days ago1764143927
0x967040B0...7e57a2f04
0.42709726 ETH
Transfer238815842025-11-26 7:58:4720 days ago1764143927
0x967040B0...7e57a2f04
1.50270719 ETH
Transfer238494992025-11-21 19:38:1125 days ago1763753891
0x967040B0...7e57a2f04
0.43822842 ETH
Transfer238494992025-11-21 19:38:1125 days ago1763753891
0x967040B0...7e57a2f04
6.10542191 ETH
Transfer237194612025-11-03 14:40:4743 days ago1762180847
0x967040B0...7e57a2f04
11.58640821 ETH
Transfer234725232025-09-30 1:27:2378 days ago1759195643
0x967040B0...7e57a2f04
0.56831541 ETH
Transfer234725232025-09-30 1:27:2378 days ago1759195643
0x967040B0...7e57a2f04
7.8648791 ETH
Transfer233059052025-09-06 18:41:11101 days ago1757184071
0x967040B0...7e57a2f04
0.62572328 ETH
Transfer233059052025-09-06 18:41:11101 days ago1757184071
0x967040B0...7e57a2f04
0.00004726 ETH
Transfer233059042025-09-06 18:40:59101 days ago1757184059
0x967040B0...7e57a2f04
0.62572363 ETH
Transfer233059042025-09-06 18:40:59101 days ago1757184059
0x967040B0...7e57a2f04
0.57894452 ETH
Transfer232938262025-09-05 2:08:35103 days ago1757038115
0x967040B0...7e57a2f04
0.62988869 ETH
Transfer232938262025-09-05 2:08:35103 days ago1757038115
0x967040B0...7e57a2f04
3.85829452 ETH
Transfer232137672025-08-24 21:53:59114 days ago1756072439
0x967040B0...7e57a2f04
0.65744794 ETH
Transfer232137672025-08-24 21:53:59114 days ago1756072439
0x967040B0...7e57a2f04
0.06219452 ETH
Transfer232124832025-08-24 17:36:23114 days ago1756056983
0x967040B0...7e57a2f04
0.65788904 ETH
Transfer232124832025-08-24 17:36:23114 days ago1756056983
0x967040B0...7e57a2f04
0.00009726 ETH
Transfer232124812025-08-24 17:35:59114 days ago1756056959
0x967040B0...7e57a2f04
0.65788972 ETH
Transfer232124812025-08-24 17:35:59114 days ago1756056959
0x967040B0...7e57a2f04
0.00009794 ETH
Transfer232124792025-08-24 17:35:35114 days ago1756056935
0x967040B0...7e57a2f04
0.65789041 ETH
Transfer232124792025-08-24 17:35:35114 days ago1756056935
0x967040B0...7e57a2f04
0.00014794 ETH
Transfer232124762025-08-24 17:34:59114 days ago1756056899
0x967040B0...7e57a2f04
0.65789143 ETH
Transfer232124762025-08-24 17:34:59114 days ago1756056899
0x967040B0...7e57a2f04
0.73930136 ETH
Transfer231976602025-08-22 15:57:23116 days ago1755878243
0x967040B0...7e57a2f04
0.66299006 ETH
Transfer231976602025-08-22 15:57:23116 days ago1755878243
0x967040B0...7e57a2f04
2.68824999 ETH
View All Internal Transactions
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FundingWorks

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {ERC721} from "solady/tokens/ERC721.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol";

/// @title FundingWorks
/// @author TokenWorks (https://token.works/)
contract FundingWorks is ERC721, Ownable, ReentrancyGuard {
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™                ™™™™™™™™™™™                ™™™™™™™™™™™ */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™               ™™™™™™™™™™™™               ™™™™™™™™™™  */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™              ™™™™™™™™™™™™™              ™™™™™™™™™™™  */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™            ™™™™™™™™™™™   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™            ™™™™™™™™™™™™™™™            ™™™™™™™™™™™   */
    /*                ™™™™™™™™™™™            ™™™™™™™™™™™           ™™™™™™™™™™™™™™™           ™™™™™™™™™™™    */
    /*                ™™™™™™™™™™™             ™™™™™™™™™™          ™™™™™™™™™™™™™™™™™          ™™™™™™™™™™™    */
    /*                ™™™™™™™™™™™             ™™™™™™™™™™          ™™™™™™™™™™™™™™™™™          ™™™™™™™™™™     */
    /*                ™™™™™™™™™™™              ™™™™™™™™™™        ™™™™™™™™™™™™™™™™™™™        ™™™™™™™™™™™     */
    /*                ™™™™™™™™™™™              ™™™™™™™™™™™       ™™™™™™™™™ ™™™™™™™™™       ™™™™™™™™™™™      */
    /*                ™™™™™™™™™™™               ™™™™™™™™™™      ™™™™™™™™™™ ™™™™™™™™™™      ™™™™™™™™™™™      */
    /*                ™™™™™™™™™™™               ™™™™™™™™™™      ™™™™™™™™™   ™™™™™™™™™      ™™™™™™™™™™       */
    /*                ™™™™™™™™™™™                ™™™™™™™™™™    ™™™™™™™™™™    ™™™™™™™™™    ™™™™™™™™™™        */
    /*                ™™™™™™™™™™™                 ™™™™™™™™™™   ™™™™™™™™™     ™™™™™™™™™™  ™™™™™™™™™™™        */
    /*                ™™™™™™™™™™™                 ™™™™™™™™™™  ™™™™™™™™™™     ™™™™™™™™™™  ™™™™™™™™™™         */
    /*                ™™™™™™™™™™™                  ™™™™™™™™™™™™™™™™™™™™       ™™™™™™™™™™™™™™™™™™™™          */
    /*                ™™™™™™™™™™™                   ™™™™™™™™™™™™™™™™™™         ™™™™™™™™™™™™™™™™™™           */
    /*                ™™™™™™™™™™™                   ™™™™™™™™™™™™™™™™™™         ™™™™™™™™™™™™™™™™™™           */
    /*                ™™™™™™™™™™™                    ™™™™™™™™™™™™™™™™           ™™™™™™™™™™™™™™™™            */
    /*                ™™™™™™™™™™™                     ™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™             */
    /*                ™™™™™™™™™™™                     ™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™             */
    /*                ™™™™™™™™™™™                      ™™™™™™™™™™™™               ™™™™™™™™™™™™              */

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                      CONSTANTS                      */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Struct containing all immutable release information
    struct ReleaseInfo {
        uint256 maxSupply;
        uint256 tokensMinted;
        uint256 mintPrice;
        uint256 mintPeriod;
        uint256 vestingPeriod;
    }

    /// @notice Maximum number of tokens that can be minted
    uint256 public immutable MAX_SUPPLY;
    /// @notice Price in ETH required to mint one token
    uint256 public immutable MINT_PRICE;
    /// @notice Duration of the minting period in seconds
    uint256 public immutable MINT_PERIOD;
    /// @notice Duration of the vesting period in seconds
    uint256 public immutable VESTING_PERIOD;
    /// @notice Initial payout percentage taken from mint price for owner
    uint256 public immutable INITIAL_PAYOUT_PCT;
    /// @notice Fee percentage taken from mint price for TOKEN_WORKS
    uint256 public immutable TOKEN_WORKS_FEE_PCT;
    /// @notice Immutable TOKEN_WORKS address that receives a % of mint price
    address public immutable TOKEN_WORKS;
    
    /// @notice Name of the token
    string public tokenName;
    /// @notice Symbol of the token
    string public tokenSymbol;

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                   STATE VARIABLES                   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Merkle root for allowlist verification
    bytes32 public merkleRoot;
    /// @notice Flag indicating if public minting is enabled
    bool public publicMintEnabled;
    /// @notice Flag indicating if minting is enabled
    bool public mintEnabled;
    /// @notice Timestamp when minting started
    uint256 public mintStartTime;
    /// @notice Current token ID counter
    uint256 private currentTokenId = 1;

    /// @notice Flag indicating if minting is complete
    bool public mintComplete;
    /// @notice Flag indicating if vesting has started
    bool public vestingStarted;
    /// @notice Timestamp when vesting started
    uint256 public vestingStartTime;
    /// @notice Timestamp of the last owner payout
    uint256 public lastOwnerPayoutTime;
    /// @notice Count of burned tokens
    uint256 public burnedTokenCount;
    /// @notice Initial amount paid out per token in wei
    uint256 public initialPayoutPerToken;
    /// @notice Initial amount paid to TOKEN_WORKS per token in wei
    uint256 public tokenWorksFeePerToken;
    /// @notice Initial locked amount per token in wei
    uint256 public initialLockedPerToken;

    /// @notice Total amount paid to the owner (fees + vested funds), already transferred
    uint256 public ownerPaidAmount;
    /// @notice Initial fees accrued during mint phase, awaiting payout until finalize()
    uint256 public initialPayoutAccrued;
    /// @notice TOKEN_WORKS fees accrued during mint phase, awaiting payout until finalize()
    uint256 public tokenWorksFeesAccrued;
    /// @notice IPFS hash for token metadata
    string public ipfsHash;

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    CUSTOM ERRORS                    */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Thrown when the sent amount doesn't match the required amount
    error WrongAmount();
    /// @notice Thrown when all tokens have been minted
    error TokensFullyMinted();
    /// @notice Thrown when caller is not the token owner
    error NotTokenOwner();
    /// @notice Thrown when burning is not allowed
    error CannotBurn();
    /// @notice Thrown when contract has insufficient balance
    error InsufficientContractBalance();
    /// @notice Thrown when minting is not enabled
    error MintNotEnabled();
    /// @notice Thrown when minting period has ended
    error MintPeriodEnded();
    /// @notice Thrown when vesting has already started
    error VestingAlreadyStarted();
    /// @notice Thrown when minting is not complete
    error MintNotComplete();
    /// @notice Thrown when address is not allowlisted
    error NotAllowlisted();
    /// @notice Thrown when minting is already enabled
    error MintAlreadyEnabled();
    /// @notice Thrown when the value passed is zero
    error CannotBeZero();
    /// @notice Thrown when operation is not allowed
    error NotAllowed();

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    CUSTOM EVENTS                    */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Emitted when public minting is enabled
    event PublicMintEnabled();

    /// @notice Emitted when minting starts
    /// @param startTime The timestamp when minting started
    /// @param endTime The timestamp when minting will end
    event MintStarted(uint256 startTime, uint256 endTime);

    /// @notice Emitted when the funding is finalized and vesting begins
    /// @param vestingStartTime The timestamp when vesting started
    /// @param vestingEndTime The timestamp when vesting will end
    event Finalized(uint256 vestingStartTime, uint256 vestingEndTime);

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                     CONSTRUCTOR                     */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Initializes the contract with the deployer as the owner and configurable parameters
    /// @param _maxSupply Maximum number of tokens that can be minted
    /// @param _mintPrice Price in ETH required to mint one token
    /// @param _mintPeriod Duration of the minting period in seconds
    /// @param _vestingPeriod Duration of the vesting period in seconds
    /// @param _initialPayoutPct Initial payout percentage taken from mint price for owner
    /// @param _tokenWorksFeePct Fee percentage taken from mint price for TOKEN_WORKS
    /// @param _deployer Address that becomes the owner of the contract
    /// @param _tokenWorks Address that receives the TOKEN_WORKS fee
    /// @param _ipfsHash IPFS hash for token metadata
    /// @param _tokenName Name of the token
    /// @param _tokenSymbol Symbol of the token
    constructor(
        uint256 _maxSupply,
        uint256 _mintPrice,
        uint256 _mintPeriod,
        uint256 _vestingPeriod,
        uint256 _initialPayoutPct,
        uint256 _tokenWorksFeePct,
        address _deployer,
        address _tokenWorks,
        string memory _ipfsHash,
        string memory _tokenName,
        string memory _tokenSymbol
    ) {
        MAX_SUPPLY = _maxSupply;
        MINT_PRICE = _mintPrice;
        MINT_PERIOD = _mintPeriod;
        VESTING_PERIOD = _vestingPeriod;
        INITIAL_PAYOUT_PCT = _initialPayoutPct;
        TOKEN_WORKS_FEE_PCT = _tokenWorksFeePct;
        TOKEN_WORKS = _tokenWorks;
        tokenName = _tokenName;
        tokenSymbol = _tokenSymbol;
        ipfsHash = _ipfsHash;

        _initializeOwner(_deployer);
    }

    /// @notice Returns the name of the token
    /// @return The token name as a string
    function name() public view override returns (string memory) { 
        return tokenName; 
    }

    /// @notice Returns the symbol of the token
    /// @return The token symbol as a string
    function symbol() public view override returns (string memory) { 
        return tokenSymbol; 
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    ADMIN FUNCTIONS                  */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Sets the merkle root for allowlist verification
    /// @param _merkleRoot The merkle root to set
    /// @dev Reverts if the merkle root is zero
    function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
        if (_merkleRoot == bytes32(0)) revert CannotBeZero();
        merkleRoot = _merkleRoot;
    }

    /// @notice Enables public minting
    /// @dev Can only be called by the owner
    function enablePublicMint() external onlyOwner {
        publicMintEnabled = true;
        emit PublicMintEnabled();
    }

    /// @notice Enables minting and initializes the mint period
    /// @dev Can only be called by the owner, sets initial fee calculations
    function enableMint() external onlyOwner {
        if (mintEnabled) revert MintAlreadyEnabled();
        mintEnabled = true;
        mintStartTime = block.timestamp;

        initialPayoutPerToken = (MINT_PRICE * INITIAL_PAYOUT_PCT) / 100;
        tokenWorksFeePerToken = (MINT_PRICE * TOKEN_WORKS_FEE_PCT) / 100;
        initialLockedPerToken = MINT_PRICE - initialPayoutPerToken - tokenWorksFeePerToken;

        emit MintStarted(mintStartTime, mintStartTime + MINT_PERIOD);
    }

    /// @notice Finalizes sale, pays accumulated fees, starts vesting stream.
    function finalize() external nonReentrant {
        if (msg.sender != owner() && msg.sender != TOKEN_WORKS) revert NotAllowed();
        if (!mintComplete && block.timestamp <= mintStartTime + MINT_PERIOD) revert MintNotComplete();
        if (vestingStarted) revert VestingAlreadyStarted();

        // pay the upfront fees once, here
        uint256 ownerPayoutAmount = initialPayoutAccrued;
        uint256 tokenWorksFeeAmount = tokenWorksFeesAccrued;
        
        if (ownerPayoutAmount > 0) {
            if (address(this).balance < ownerPayoutAmount) revert InsufficientContractBalance();
            initialPayoutAccrued = 0;
            ownerPaidAmount += ownerPayoutAmount;
            SafeTransferLib.forceSafeTransferETH(owner(), ownerPayoutAmount);
        }

        if (tokenWorksFeeAmount > 0) {
            if (address(this).balance < tokenWorksFeeAmount) revert InsufficientContractBalance();
            tokenWorksFeesAccrued = 0;
            SafeTransferLib.forceSafeTransferETH(TOKEN_WORKS, tokenWorksFeeAmount);
        }

        vestingStarted = true;
        vestingStartTime = block.timestamp;
        lastOwnerPayoutTime = block.timestamp;

        emit Finalized(vestingStartTime, vestingStartTime + VESTING_PERIOD);
    }

    /// @notice Owner may trigger payment of newly‑vested ETH to themselves.
    function claimOwnerShare() external onlyOwner nonReentrant {
        _payOwnerShare();
    }

    /// @notice Sets the IPFS hash for token metadata
    /// @param _ipfsHash The IPFS hash
    function updateIpfsHash(string calldata _ipfsHash) external onlyOwner {
        ipfsHash = _ipfsHash;
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    USER FUNCTIONS                   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Allows allowlisted addresses to mint tokens during the allowlist phase
    /// @param proof Merkle proof verifying the caller is on the allowlist
    /// @return The ID of the newly minted token
    /// @dev Requires the correct ETH payment and verifies caller is on the allowlist
    function allowlistMint(bytes32[] calldata proof) external payable nonReentrant returns (uint256) {
        _preMintChecks(msg.value);
        if (!MerkleProofLib.verifyCalldata(proof, merkleRoot, keccak256(abi.encodePacked(msg.sender)))) revert NotAllowlisted();
        return _mintToken();
    }

    /// @notice Allows anyone to mint tokens during the public mint phase
    /// @return The ID of the newly minted token
    /// @dev Requires the correct ETH payment and public minting to be enabled
    function mint() external payable nonReentrant returns (uint256) {
        _preMintChecks(msg.value);
        if (!publicMintEnabled) revert MintNotEnabled();
        return _mintToken();
    }

    /// @notice Burns a token and returns the remaining locked ETH to the token owner
    /// @param tokenId The ID of the token to burn
    /// @dev Requires vesting to have started and caller to be the token owner
    /// @dev Pays out any pending owner share before calculating the refund amount
    function burn(uint256 tokenId) external nonReentrant {
        if (!vestingStarted) revert CannotBurn();
        if (ownerOf(tokenId) != msg.sender) revert NotTokenOwner();
        uint256 locked = getRemainingLockedEth(tokenId);
        _payOwnerShare();
        burnedTokenCount++;
        _burn(tokenId);
        if (locked > 0) SafeTransferLib.forceSafeTransferETH(msg.sender, locked);
    }

    /// @notice Returns the rate at which ETH vests per token per second
    /// @return The amount of ETH that vests per second for each token
    /// @dev Returns 0 if vesting hasn't started yet
    function getVestingRatePerToken() public view returns (uint256) {
        if (!vestingStarted) return 0;
        return initialLockedPerToken / VESTING_PERIOD;
    }

    /// @notice Calculates the remaining locked ETH for a specific token
    /// @param tokenId The ID of the token to check
    /// @return The amount of ETH still locked for the given token
    /// @dev Returns the full initial amount if vesting hasn't started
    function getRemainingLockedEth(uint256 tokenId) public view returns (uint256) {
        if (!_exists(tokenId)) revert TokenDoesNotExist();
        if (!vestingStarted) return initialLockedPerToken;
        uint256 elapsed = block.timestamp - vestingStartTime;
        if (elapsed >= VESTING_PERIOD) return 0;
        uint256 vested = (initialLockedPerToken * elapsed) / VESTING_PERIOD;
        return initialLockedPerToken - vested;
    }

    /// @notice Returns the metadata URI for a specific token
    /// @param tokenId The ID of the token to get the URI for
    /// @return The IPFS URI containing the token's JSON metadata
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        if (!_exists(tokenId)) revert TokenDoesNotExist();
        return string.concat("ipfs://", ipfsHash);
    }

    /// @notice Returns all release information
    /// @return info Struct containing all parameters in one call
    function getReleaseInfo() external view returns (ReleaseInfo memory) {
        return ReleaseInfo({
            maxSupply: MAX_SUPPLY,
            tokensMinted: currentTokenId - 1,
            mintPrice: MINT_PRICE,
            mintPeriod: MINT_PERIOD,
            vestingPeriod: VESTING_PERIOD
        });
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                  INTERNAL FUNCTIONS                 */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Performs pre-mint validation checks
    /// @param amountSent The amount of ETH sent with the mint transaction
    /// @dev Reverts if minting is not enabled, period has ended, wrong amount sent, or max supply reached
    function _preMintChecks(uint256 amountSent) private view {
        if (!mintEnabled) revert MintNotEnabled();
        if (block.timestamp > mintStartTime + MINT_PERIOD) revert MintPeriodEnded();
        if (amountSent != MINT_PRICE) revert WrongAmount();
        if (currentTokenId > MAX_SUPPLY) revert TokensFullyMinted();
    }

    /// @notice Internal function to mint a new token
    /// @return id The ID of the newly minted token
    /// @dev Updates fee accrual and marks minting as complete if max supply is reached
    function _mintToken() private returns (uint256 id) {
        id = currentTokenId++;
        initialPayoutAccrued += initialPayoutPerToken; // accumulate owner fee
        tokenWorksFeesAccrued += tokenWorksFeePerToken; // accumulate TOKEN_WORKS fee
        _mint(msg.sender, id);
        if (currentTokenId > MAX_SUPPLY) mintComplete = true;
    }

    /// @notice Calculates the owner's share of vested ETH
    /// @return The amount of ETH that should be paid to the owner
    /// @dev Returns 0 if vesting hasn't started or no active tokens exist
    function _calculateOwnerShare() internal view returns (uint256) {
        if (!vestingStarted) return 0;
        uint256 active = currentTokenId - 1 - burnedTokenCount;
        if (active == 0) return 0;
        uint256 vestingEnd = vestingStartTime + VESTING_PERIOD;
        uint256 endTime = block.timestamp < vestingEnd ? block.timestamp : vestingEnd;
        if (endTime <= lastOwnerPayoutTime) return 0;
        uint256 secondsElapsed = endTime - lastOwnerPayoutTime;
        return getVestingRatePerToken() * secondsElapsed * active;
    }

    /// @notice Transfers the owner's share of vested ETH
    /// @dev Reverts if contract has insufficient balance
    /// @dev Updates the last payout time and owner paid amount
    function _payOwnerShare() internal {
        uint256 share = _calculateOwnerShare();
        if (share == 0) return;
        if (address(this).balance < share) revert InsufficientContractBalance();
        ownerPaidAmount += share;
        lastOwnerPayoutTime = block.timestamp;
        SafeTransferLib.forceSafeTransferETH(owner(), share);
    }

    /// @notice Hook called before any token transfer
    /// @param from The address sending the token
    /// @param to The address receiving the token
    /// @param tokenId The ID of the token being transferred
    /// @dev Only allows minting and burning, rejects all other transfers
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        // Allow minting (from == address(0))
        // Allow burning (to == address(0))
        // Reject all other transfers
        if (from != address(0) && to != address(0)) {
            revert NotAllowed();
        }
        super._beforeTokenTransfer(from, to, tokenId);
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                   BLOCKED FUNCTIONS                 */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Function is blocked - transfers are not allowed
    function setApprovalForAll(address, bool) public virtual override {
        revert NotAllowed();
    }

    /// @notice Function is blocked - transfers are not allowed
    function approve(address, uint256) public payable virtual override {
        revert NotAllowed();
    }

    /// @notice Function is blocked - direct ETH transfers are not allowed
    receive() external payable { 
        revert NotAllowed(); 
    }

    /// @notice Function is blocked - direct ETH transfers are not allowed
    fallback() external payable { 
        revert NotAllowed(); 
    }
}

File 2 of 6 : ERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC721 implementation with storage hitchhiking.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol)
///
/// @dev Note:
/// - The ERC721 standard allows for self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - For performance, methods are made payable where permitted by the ERC721 standard.
/// - The `safeTransfer` functions use the identity precompile (0x4)
///   to copy memory internally.
///
/// If you are overriding:
/// - NEVER violate the ERC721 invariant:
///   the balance of an owner MUST always be equal to their number of ownership slots.
///   The transfer functions do not have an underflow guard for user token balances.
/// - Make sure all variables written to storage are properly cleaned
///   (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC721 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev An account can hold up to 4294967295 tokens.
    uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Only the token owner or an approved account can manage the token.
    error NotOwnerNorApproved();

    /// @dev The token does not exist.
    error TokenDoesNotExist();

    /// @dev The token already exists.
    error TokenAlreadyExists();

    /// @dev Cannot query the balance for the zero address.
    error BalanceQueryForZeroAddress();

    /// @dev Cannot mint or transfer to the zero address.
    error TransferToZeroAddress();

    /// @dev The token must be owned by `from`.
    error TransferFromIncorrectOwner();

    /// @dev The recipient's balance has overflowed.
    error AccountBalanceOverflow();

    /// @dev Cannot safely transfer to a contract that does not implement
    /// the ERC721Receiver interface.
    error TransferToNonERC721ReceiverImplementer();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when token `id` is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    /// @dev Emitted when `owner` enables `account` to manage the `id` token.
    event Approval(address indexed owner, address indexed account, uint256 indexed id);

    /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
    event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership data slot of `id` is given by:
    /// ```
    ///     mstore(0x00, id)
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
    /// ```
    /// Bits Layout:
    /// - [0..159]   `addr`
    /// - [160..255] `extraData`
    ///
    /// The approved address slot is given by: `add(1, ownershipSlot)`.
    ///
    /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip
    ///
    /// The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x1c)
    /// ```
    /// Bits Layout:
    /// - [0..31]   `balance`
    /// - [32..255] `aux`
    ///
    /// The `operator` approval slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
    ///     mstore(0x00, owner)
    ///     let operatorApprovalSlot := keccak256(0x0c, 0x30)
    /// ```
    uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;

    /// @dev Pre-shifted and pre-masked constant.
    uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC721 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the token collection name.
    function name() public view virtual returns (string memory);

    /// @dev Returns the token collection symbol.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC721                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function ownerOf(uint256 id) public view virtual returns (address result) {
        result = _ownerOf(id);
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(result) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns the number of tokens owned by `owner`.
    ///
    /// Requirements:
    /// - `owner` must not be the zero address.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the `owner` is the zero address.
            if iszero(owner) {
                mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`.
                revert(0x1c, 0x04)
            }
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE)
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function getApproved(uint256 id) public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            if iszero(shl(96, sload(ownershipSlot))) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            result := sload(add(1, ownershipSlot))
        }
    }

    /// @dev Sets `account` as the approved account to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - The caller must be the owner of the token,
    ///   or an approved operator for the token owner.
    ///
    /// Emits an {Approval} event.
    function approve(address account, uint256 id) public payable virtual {
        _approve(msg.sender, account, id);
    }

    /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
    function isApprovedForAll(address owner, address operator)
        public
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x30))
        }
    }

    /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
    ///
    /// Emits an {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool isApproved) public virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`msg.sender`, `operator`).
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            // forgefmt: disable-next-item
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
        }
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 id) public payable virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller()))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if the token does not exist, or if `from` is not the owner.
            if iszero(mul(owner, eq(owner, from))) {
                // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
                mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // Revert if the caller is not the owner, nor approved.
                if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
    function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
        public
        payable
        virtual
    {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
    /// See: https://eips.ethereum.org/EIPS/eip-165
    /// This function call must use less than 30000 gas.
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL QUERY FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if token `id` exists.
    function _exists(uint256 id) internal view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))))
        }
    }

    /// @dev Returns the owner of token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _ownerOf(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            INTERNAL DATA HITCHHIKING FUNCTIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance, no events are emitted for the hitchhiking setters.
    // Please emit your own events if required.

    /// @dev Returns the auxiliary data for `owner`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _getAux(address owner) internal view virtual returns (uint224 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := shr(32, sload(keccak256(0x0c, 0x1c)))
        }
    }

    /// @dev Set the auxiliary data for `owner` to `value`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _setAux(address owner, uint224 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            let balanceSlot := keccak256(0x0c, 0x1c)
            let packed := sload(balanceSlot)
            sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed)))))
        }
    }

    /// @dev Returns the extra data for token `id`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _getExtraData(uint256 id) internal view virtual returns (uint96 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Sets the extra data for token `id` to `value`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _setExtraData(uint256 id, uint96 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let packed := sload(ownershipSlot)
            sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed)))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 id) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Revert if the token already exists.
            if shl(96, ownershipPacked) {
                mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`.
                revert(0x1c, 0x04)
            }
            // Update with the owner.
            sstore(ownershipSlot, or(ownershipPacked, to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

    /// @dev Mints token `id` to `to`, and updates the extra data for token `id` to `value`.
    /// Does NOT check if token `id` already exists (assumes `id` is auto-incrementing).
    ///
    /// Requirements:
    ///
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Update with the owner and extra data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

    /// @dev Equivalent to `_safeMint(to, id, "")`.
    function _safeMint(address to, uint256 id) internal virtual {
        _safeMint(to, id, "");
    }

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
        _mint(to, id);
        if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_burn(address(0), id)`.
    function _burn(uint256 id) internal virtual {
        _burn(address(0), id);
    }

    /// @dev Destroys token `id`, using `by`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _burn(address by, uint256 id) internal virtual {
        address owner = ownerOf(id);
        _beforeTokenTransfer(owner, address(0), id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Reload the owner in case it is changed in `_beforeTokenTransfer`.
            owner := shr(96, shl(96, ownershipPacked))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Load and check the token approval.
            {
                mstore(0x00, owner)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Clear the owner.
            sstore(ownershipSlot, xor(ownershipPacked, owner))
            // Decrement the balance of `owner`.
            {
                let balanceSlot := keccak256(0x0c, 0x1c)
                sstore(balanceSlot, sub(sload(balanceSlot), 1))
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id)
        }
        _afterTokenTransfer(owner, address(0), id);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL APPROVAL FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function _isApprovedOrOwner(address account, uint256 id)
        internal
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            // Clear the upper 96 bits.
            account := shr(96, shl(96, account))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := shr(96, shl(96, sload(ownershipSlot)))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Check if `account` is the `owner`.
            if iszero(eq(account, owner)) {
                mstore(0x00, owner)
                // Check if `account` is approved to manage the token.
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    result := eq(account, sload(add(1, ownershipSlot)))
                }
            }
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _getApproved(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Equivalent to `_approve(address(0), account, id)`.
    function _approve(address account, uint256 id) internal virtual {
        _approve(address(0), account, id);
    }

    /// @dev Sets `account` as the approved account to manage token `id`, using `by`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - If `by` is not the zero address, `by` must be the owner
    ///   or an approved operator for the token owner.
    ///
    /// Emits a {Approval} event.
    function _approve(address by, address account, uint256 id) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            account := and(bitmaskAddress, account)
            by := and(bitmaskAddress, by)
            // Load the owner of the token.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := and(bitmaskAddress, sload(ownershipSlot))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // If `by` is not the zero address, do the authorization check.
            // Revert if `by` is not the owner, nor approved.
            if iszero(or(iszero(by), eq(by, owner))) {
                mstore(0x00, owner)
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Sets `account` as the approved account to manage `id`.
            sstore(add(1, ownershipSlot), account)
            // Emit the {Approval} event.
            log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id)
        }
    }

    /// @dev Approve or remove the `operator` as an operator for `by`,
    /// without authorization checks.
    ///
    /// Emits an {ApprovalForAll} event.
    function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            operator := shr(96, shl(96, operator))
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`by`, `operator`).
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
            mstore(0x00, by)
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_transfer(address(0), from, to, id)`.
    function _transfer(address from, address to, uint256 id) internal virtual {
        _transfer(address(0), from, to, id);
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _transfer(address by, address from, address to, uint256 id) internal virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            by := and(bitmaskAddress, by)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if the token does not exist, or if `from` is not the owner.
            if iszero(mul(owner, eq(owner, from))) {
                // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
                mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `_safeTransfer(from, to, id, "")`.
    function _safeTransfer(address from, address to, uint256 id) internal virtual {
        _safeTransfer(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(address(0), from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`.
    function _safeTransfer(address by, address from, address to, uint256 id) internal virtual {
        _safeTransfer(by, from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(by, from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    HOOKS FOR OVERRIDING                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any token transfers, including minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /// @dev Hook that is called after any token transfers, including minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if `a` has bytecode of non-zero length.
    function _hasCode(address a) private view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := extcodesize(a) // Can handle dirty upper bits.
        }
    }

    /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
        private
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            let onERC721ReceivedSelector := 0x150b7a02
            mstore(m, onERC721ReceivedSelector)
            mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), 0x80)
            let n := mload(data)
            mstore(add(m, 0xa0), n)
            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
            // Load the returndata and compare it.
            if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
                mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 4 of 6 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unauthorized reentrant call.
    error Reentrancy();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
    /// 9 bytes is large enough to avoid collisions with lower slots,
    /// but not too large to result in excessive bytecode bloat.
    uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      REENTRANCY GUARD                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
            sstore(_REENTRANCY_GUARD_SLOT, address())
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            sstore(_REENTRANCY_GUARD_SLOT, codesize())
        }
    }

    /// @dev Guards a view function from read-only reentrancy.
    modifier nonReadReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
        }
        _;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The ERC20 `totalSupply` query has failed.
    error TotalSupplyQueryFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /// @dev The Permit2 approve operation has failed.
    error Permit2ApproveFailed();

    /// @dev The Permit2 lockdown operation has failed.
    error Permit2LockdownFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x34, 0) // Store 0 for the `amount`.
                    mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                    pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                    mstore(0x34, amount) // Store back the original `amount`.
                    // Retry the approval, reverting upon failure.
                    success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    if iszero(and(eq(mload(0x00), 1), success)) {
                        // Check the `extcodesize` again just in case the token selfdestructs lol.
                        if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Performs a `token.balanceOf(account)` check.
    /// `implemented` denotes whether the `token` does not implement `balanceOf`.
    /// `amount` is zero if the `token` does not implement `balanceOf`.
    function checkBalanceOf(address token, address account)
        internal
        view
        returns (bool implemented, uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            implemented :=
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                )
            amount := mul(mload(0x20), implemented)
        }
    }

    /// @dev Returns the total supply of the `token`.
    /// Reverts if the token does not exist or does not implement `totalSupply()`.
    function totalSupply(address token) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x18160ddd) // `totalSupply()`.
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
            ) {
                mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(
                and(
                    call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
                    lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
                )
            ) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(
                        add(m, 0x94),
                        lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    )
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `amount != 0` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero( // Revert if token does not have code, or if the call fails.
            mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
    function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let addressMask := shr(96, not(0))
            let m := mload(0x40)
            mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
            mstore(add(m, 0x20), and(addressMask, token))
            mstore(add(m, 0x40), and(addressMask, spender))
            mstore(add(m, 0x60), and(addressMask, amount))
            mstore(add(m, 0x80), and(0xffffffffffff, expiration))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Revokes an approval for `token` and `spender` for `address(this)`.
    function permit2Lockdown(address token, address spender) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0xcc53287f) // `Permit2.lockdown`.
            mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
            mstore(add(m, 0x40), 1) // `approvals.length`.
            mstore(add(m, 0x60), shr(96, shl(96, token)))
            mstore(add(m, 0x80), shr(96, shl(96, spender)))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            MERKLE PROOF VERIFICATION OPERATIONS            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(proof) {
                // Initialize `offset` to the offset of `proof` elements in memory.
                let offset := add(proof, 0x20)
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(offset, shl(5, mload(proof)))
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, mload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), mload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if proof.length {
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(proof.offset, shl(5, proof.length))
                // Initialize `offset` to the offset of `proof` in the calldata.
                let offset := proof.offset
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, calldataload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), calldataload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - The sum of the lengths of `proof` and `leaves` must never overflow.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The memory offset of `proof` must be non-zero
    ///   (i.e. `proof` is not pointing to the scratch space).
    function verifyMultiProof(
        bytes32[] memory proof,
        bytes32 root,
        bytes32[] memory leaves,
        bool[] memory flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // Cache the lengths of the arrays.
            let leavesLength := mload(leaves)
            let proofLength := mload(proof)
            let flagsLength := mload(flags)

            // Advance the pointers of the arrays to point to the data.
            leaves := add(0x20, leaves)
            proof := add(0x20, proof)
            flags := add(0x20, flags)

            // If the number of flags is correct.
            for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flagsLength) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof, shl(5, proofLength))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                leavesLength := shl(5, leavesLength)
                for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                    mstore(add(hashesFront, i), mload(add(leaves, i)))
                }
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, leavesLength)
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flagsLength := add(hashesBack, shl(5, flagsLength))

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(mload(flags)) {
                        // Loads the next proof.
                        b := mload(proof)
                        proof := add(proof, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag.
                    flags := add(flags, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flagsLength)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof)
                    )
                break
            }
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The calldata offset of `proof` must be non-zero
    ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
    function verifyMultiProofCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32[] calldata leaves,
        bool[] calldata flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // If the number of flags is correct.
            for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flags.length) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    // forgefmt: disable-next-item
                    isValid := eq(
                        calldataload(
                            xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                        ),
                        root
                    )
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof.offset, shl(5, proof.length))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, shl(5, leaves.length))
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flags.length := add(hashesBack, shl(5, flags.length))

                // We don't need to make a copy of `proof.offset` or `flags.offset`,
                // as they are pass-by-value (this trick may not always save gas).

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(calldataload(flags.offset)) {
                        // Loads the next proof.
                        b := calldataload(proof.offset)
                        proof.offset := add(proof.offset, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag offset.
                    flags.offset := add(flags.offset, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flags.length)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof.offset)
                    )
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes32 array.
    function emptyProof() internal pure returns (bytes32[] calldata proof) {
        /// @solidity memory-safe-assembly
        assembly {
            proof.length := 0
        }
    }

    /// @dev Returns an empty calldata bytes32 array.
    function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
        /// @solidity memory-safe-assembly
        assembly {
            leaves.length := 0
        }
    }

    /// @dev Returns an empty calldata bool array.
    function emptyFlags() internal pure returns (bool[] calldata flags) {
        /// @solidity memory-safe-assembly
        assembly {
            flags.length := 0
        }
    }
}

Settings
{
  "remappings": [
    "solady/=lib/solady/src/",
    "@uniswap/v4-core/=lib/v4-core/",
    "@uniswap/v4-periphery/=lib/v4-periphery/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@ensdomains/=lib/v4-core/node_modules/@ensdomains/",
    "ds-test/=lib/v4-core/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "hardhat/=lib/v4-core/node_modules/hardhat/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "permit2/=lib/v4-periphery/lib/permit2/",
    "solmate/=lib/v4-core/lib/solmate/",
    "v4-core/=lib/v4-core/src/",
    "v4-periphery/=lib/v4-periphery/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"uint256","name":"_maxSupply","type":"uint256"},{"internalType":"uint256","name":"_mintPrice","type":"uint256"},{"internalType":"uint256","name":"_mintPeriod","type":"uint256"},{"internalType":"uint256","name":"_vestingPeriod","type":"uint256"},{"internalType":"uint256","name":"_initialPayoutPct","type":"uint256"},{"internalType":"uint256","name":"_tokenWorksFeePct","type":"uint256"},{"internalType":"address","name":"_deployer","type":"address"},{"internalType":"address","name":"_tokenWorks","type":"address"},{"internalType":"string","name":"_ipfsHash","type":"string"},{"internalType":"string","name":"_tokenName","type":"string"},{"internalType":"string","name":"_tokenSymbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"CannotBeZero","type":"error"},{"inputs":[],"name":"CannotBurn","type":"error"},{"inputs":[],"name":"InsufficientContractBalance","type":"error"},{"inputs":[],"name":"MintAlreadyEnabled","type":"error"},{"inputs":[],"name":"MintNotComplete","type":"error"},{"inputs":[],"name":"MintNotEnabled","type":"error"},{"inputs":[],"name":"MintPeriodEnded","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotAllowed","type":"error"},{"inputs":[],"name":"NotAllowlisted","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"NotTokenOwner","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TokensFullyMinted","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VestingAlreadyStarted","type":"error"},{"inputs":[],"name":"WrongAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"vestingStartTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vestingEndTime","type":"uint256"}],"name":"Finalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"MintStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"PublicMintEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"INITIAL_PAYOUT_PCT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN_WORKS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN_WORKS_FEE_PCT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VESTING_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"allowlistMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnedTokenCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"claimOwnerShare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"enableMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enablePublicMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReleaseInfo","outputs":[{"components":[{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"tokensMinted","type":"uint256"},{"internalType":"uint256","name":"mintPrice","type":"uint256"},{"internalType":"uint256","name":"mintPeriod","type":"uint256"},{"internalType":"uint256","name":"vestingPeriod","type":"uint256"}],"internalType":"struct FundingWorks.ReleaseInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getRemainingLockedEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVestingRatePerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialLockedPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialPayoutAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialPayoutPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ipfsHash","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastOwnerPayoutTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintComplete","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerPaidAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMintEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenWorksFeePerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenWorksFeesAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"_ipfsHash","type":"string"}],"name":"updateIpfsHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vestingStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

610160604052346105cf576126468038038061001a816105d3565b928339810190610160818303126105cf5780519060208101516040820151906060830151608084015160a08501519161005560c087016105f8565b9461006260e088016105f8565b6101008801519095906001600160401b0381116105cf578a610085918a0161060c565b6101208901519098906001600160401b0381116105cf578b6100a891830161060c565b610140820151909b906001600160401b0381116105cf576100c9920161060c565b600160055560809990995260a05260c05260e05261010052610120526101405283516001600160401b038111610408575f54600181811c911680156105c5575b60208210146103ea57601f8111610563575b50602094601f8211600114610502579481929394955f926104f7575b50508160011b915f199060031b1c1916175f555b82516001600160401b03811161040857600154600181811c911680156104ed575b60208210146103ea57601f811161048a575b506020601f821160011461042757819293945f9261041c575b50508160011b915f199060031b1c1916176001555b81516001600160401b03811161040857601054600181811c911680156103fe575b60208210146103ea57601f8111610387575b50602092601f821160011461032657928192935f9261031b575b50508160011b915f199060031b1c1916176010555b6001600160a01b0316638b78c6d8198190555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3604051611fe8908161065e823960805181818161119c0152818161123601528181611c540152611d4a015260a05181818161067e01528181610e42015281816112830152611c2b015260c05181818161089801528181610da701528181610efc015281816112ad0152611bfe015260e051818181610ccd015281816112d201528181611745015281816119d001528181611ba30152611f260152610100518181816107c60152610e6b015261012051818181610e9c0152611162015261014051818181610be201528181610d1d0152610dd80152f35b015190505f806101f9565b601f1982169360105f52805f20915f5b86811061036f5750836001959610610357575b505050811b0160105561020e565b01515f1960f88460031b161c191690555f8080610349565b91926020600181928685015181550194019201610336565b60105f527f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672601f830160051c810191602084106103e0575b601f0160051c01905b8181106103d557506101df565b5f81556001016103c8565b90915081906103bf565b634e487b7160e01b5f52602260045260245ffd5b90607f16906101cd565b634e487b7160e01b5f52604160045260245ffd5b015190505f80610197565b601f1982169060015f52805f20915f5b8181106104725750958360019596971061045a575b505050811b016001556101ac565b01515f1960f88460031b161c191690555f808061044c565b9192602060018192868b015181550194019201610437565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c810191602084106104e3575b601f0160051c01905b8181106104d8575061017e565b5f81556001016104cb565b90915081906104c2565b90607f169061016c565b015190505f80610137565b601f198216955f8052805f20915f5b88811061054b57508360019596979810610533575b505050811b015f5561014b565b01515f1960f88460031b161c191690555f8080610526565b91926020600181928685015181550194019201610511565b5f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563601f830160051c810191602084106105bb575b601f0160051c01905b8181106105b0575061011b565b5f81556001016105a3565b909150819061059a565b90607f1690610109565b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761040857604052565b51906001600160a01b03821682036105cf57565b81601f820112156105cf578051906001600160401b0382116104085761063b601f8301601f19166020016105d3565b92828452602083830101116105cf57815f9260208093018386015e830101529056fe6080806040526004361015610034575b50361561002557631eb49d6d60e11b5f5260045ffd5b631eb49d6d60e11b5f5260045ffd5b5f3560e01c9081630197d972146117305750806301ffc9a7146116e357806306fdde0314611641578063075b222514611624578063081812fc146115d35780630913348f14611475578063095ea7b3146114515780630b97d964146114345780630f4161aa146114125780631249c58b146113bd5780631e1d6b771461139f57806323b872dd1461138d57806324bd281614611370578063256929621461132757806327ffb9a2146111fe5780632eb4a7ab146111e15780632f2c269c146111bf57806332cb6b0c1461118557806333e03e091461114b578063358cbf0a1461110e57806342842e0e146110d557806342966c6814610f3e57806344b28d5914610e075780634bb278f314610c11578063520f3cc814610bcd578063537924ef14610ac057806354d1f13d14610a7c5780636352211e14610a4c5780636c02a931146109aa57806370a0823114610957578063715018a61461090e5780637b61c3201461081d5780637cb64759146108d85780638285268e146108bb57806383ea6e97146108815780638da5cb5b14610855578063931e2e491461083857806395d89b411461081d578063a22cb465146107e9578063a2c00d9e146107af578063a8660a7814610792578063b7663e9714610775578063b88d4fde146106c3578063bac3bfb8146106a1578063c002d23d14610667578063c623674f146105b0578063c87b56dd146104a3578063d12397301461047e578063d62f3b1c14610433578063db1704e114610416578063e32a748f146103f9578063e985e9c5146103b5578063eab1e62c14610398578063f04e283e1461034b578063f2fde38b1461030e578063f998c8e8146102e95763fee81cf4146102b3575f61000f565b346102e55760203660031901126102e5576102cc6117c0565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b346102e5575f3660031901126102e557602060ff60065460081c166040519015158152f35b60203660031901126102e5576103226117c0565b61032a611bcc565b8060601b1561033e5761033c90611eb3565b005b637448fbae5f526004601cfd5b60203660031901126102e55761035f6117c0565b610367611bcc565b63389a75e1600c52805f526020600c20908154421161038b575f61033c9255611eb3565b636f5e88185f526004601cfd5b346102e5575f3660031901126102e5576020600d54604051908152f35b346102e55760403660031901126102e5576103ce6117c0565b6103d66117d6565b601c52670a5a2e7a000000006008525f5260206030600c20546040519015158152f35b346102e5575f3660031901126102e5576020600954604051908152f35b346102e5575f3660031901126102e5576020600c54604051908152f35b346102e5575f3660031901126102e55761044b611bcc565b600160ff1960035416176003557fbad7871e16f9b9d8b2a6bd6e38ada7c99940913046fe099cffa0040643fb064e5f80a1005b346102e5575f3660031901126102e557602060ff60035460081c166040519015158152f35b346102e55760203660031901126102e5576004355f818152673ec412a9852d173d60c11b601c52602090208101015460601b156105a15760405166697066733a2f2f60c81b60208201525f906010546104fb81611826565b906001811690811561057d5750600114610537575b50610527816105339303601f19810183528261187a565b60405191829182611768565b0390f35b915060105f525f80516020611f938339815191525f905b83821061056657505090810160270190610527610510565b60018160209254602785870101520191019061054e565b60ff1916602780850191909152821515909202830190910192506105279050610510565b63677510db60e11b5f5260045ffd5b346102e5575f3660031901126102e5576040515f6010546105d081611826565b808452906001811690811561064357506001146105f8575b610533836105278185038261187a565b60105f9081525f80516020611f93833981519152939250905b808210610629575090915081016020016105276105e8565b919260018160209254838588010152019101909291610611565b60ff191660208086019190915291151560051b8401909101915061052790506105e8565b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346102e5575f3660031901126102e55760206106bb611b8d565b604051908152f35b60803660031901126102e5576106d76117c0565b6106df6117d6565b60443560643567ffffffffffffffff81116102e557610702903690600401611792565b929093610710838383611a26565b813b61071857005b67ffffffffffffffff8411610761576040519361073f601f8201601f19166020018661187a565b80855236818701116102e5576020815f9261033c988389013786010152611ddc565b634e487b7160e01b5f52604160045260245ffd5b346102e5575f3660031901126102e5576020600b54604051908152f35b346102e5575f3660031901126102e5576020600754604051908152f35b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346102e55760403660031901126102e5576108026117c0565b50602435801515036102e557631eb49d6d60e11b5f5260045ffd5b346102e5575f3660031901126102e55761053361052761189c565b346102e5575f3660031901126102e5576020600454604051908152f35b346102e5575f3660031901126102e557638b78c6d819546040516001600160a01b039091168152602090f35b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346102e5575f3660031901126102e5576020600a54604051908152f35b346102e55760203660031901126102e5576004356108f4611bcc565b80156108ff57600255005b631e1d0ab560e01b5f5260045ffd5b5f3660031901126102e557610921611bcc565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35f638b78c6d81955005b346102e55760203660031901126102e5576109706117c0565b801561099d57673ec412a9852d173d60c11b601c525f52602063ffffffff601c600c205416604051908152f35b638f4eb6045f526004601cfd5b346102e5575f3660031901126102e5576040515f80546109c981611826565b808452906001811690811561064357506001146109f057610533836105278185038261187a565b5f8080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563939250905b808210610a32575090915081016020016105276105e8565b919260018160209254838588010152019101909291610a1a565b346102e55760203660031901126102e5576020610a6a600435611b60565b6040516001600160a01b039091168152f35b5f3660031901126102e55763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b60203660031901126102e55760043567ffffffffffffffff81116102e557366023820112156102e557806004013567ffffffffffffffff81116102e55760248160051b8301013681116102e5573068929eee149b4bd212685414610bc0573068929eee149b4bd2126855610b3334611be8565b6002549260405160208101903360601b825260148152610b5460348261187a565b51902092610b91575b505003610b82576020610b6e611ca7565b3868929eee149b4bd2126855604051908152f35b6306fb10a960e01b5f5260045ffd5b602401915b602083359182811160051b9081521852602060405f20920191818310610b96579150508280610b5d565b63ab143c065f526004601cfd5b346102e5575f3660031901126102e5576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346102e5575f3660031901126102e5573068929eee149b4bd212685414610bc0573068929eee149b4bd2126855638b78c6d81954336001600160a01b038216141580610dd4575b6100255760065460ff81161580610d9e575b610d8f5760081c60ff16610d8057600e5490600f549180610d56575b505080610d0c575b61010061ff0019600654161760065542600755426008557fb968440accd1ce5fa60b00de8bb8d8487eb2fda3c3701fb30fea3f69aa910a486040610cf27f000000000000000000000000000000000000000000000000000000000000000042611b53565b8151904282526020820152a13868929eee149b4bd2126855005b804710610d4757610d41905f600f557f0000000000000000000000000000000000000000000000000000000000000000611e6e565b80610c8e565b63786e0a9960e01b5f5260045ffd5b804710610d4757610d79915f600e55610d7182600d54611b53565b600d55611e6e565b8180610c86565b6372de7acd60e01b5f5260045ffd5b63a4bcf01360e01b5f5260045ffd5b50610dcc6004547f000000000000000000000000000000000000000000000000000000000000000090611b53565b421115610c6a565b50337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161415610c58565b346102e5575f3660031901126102e557610e1f611bcc565b60035460ff8160081c16610f2f5761ff0019166101001760035542600455610ed17f0000000000000000000000000000000000000000000000000000000000000000610ecc6064610e907f000000000000000000000000000000000000000000000000000000000000000084611960565b0480600a556064610ec17f000000000000000000000000000000000000000000000000000000000000000085611960565b049283600b55611953565b611953565b600c557fcb0654d378ad3cd02dbc858de79e52889616696769cabc0cbfb2ec86e05504516040610f217f000000000000000000000000000000000000000000000000000000000000000042611b53565b8151904282526020820152a1005b6339f3829b60e01b5f5260045ffd5b346102e55760203660031901126102e5576004353068929eee149b4bd212685414610bc0573068929eee149b4bd212685560ff60065460081c16156110c657610f8681611b60565b336001600160a01b03909116036110b757610fa081611991565b90610fa9611da1565b610fb4600954611b45565b6009556001600160a01b03610fc882611b60565b161515806110b0575b610025575f818152673ec412a9852d173d60c11b601c5260209020810181018054906001600160a01b0382169081156110a357815f5280600101928354801560011715611084575b905f94849261107b575b50189055601c600c20821981540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a48061106b575b3868929eee149b4bd2126855005b6110759033611e6e565b8061105d565b85905587611023565b906030600c2054156110965790611019565b634b6e7f185f526004601cfd5b63ceea21b65f526004601cfd5b505f610fd1565b6359dc379f60e01b5f5260045ffd5b635788079960e01b5f5260045ffd5b6110de366117ec565b6110eb8183859495611a26565b823b6110f357005b61033c926040519261110660208561187a565b5f8452611ddc565b346102e5575f3660031901126102e557611126611bcc565b3068929eee149b4bd212685414610bc0573068929eee149b4bd212685561105d611da1565b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346102e5575f3660031901126102e557602060ff600654166040519015158152f35b346102e5575f3660031901126102e5576020600254604051908152f35b346102e5575f3660031901126102e5575f608060405161121d8161185e565b82815282602082015282604082015282606082015201527f0000000000000000000000000000000000000000000000000000000000000000600554905f1982019182116113135760a0916040516112738161185e565b82815260208101918252604081017f00000000000000000000000000000000000000000000000000000000000000008152608060608301927f0000000000000000000000000000000000000000000000000000000000000000845201927f00000000000000000000000000000000000000000000000000000000000000008452604051948552516020850152516040840152516060830152516080820152f35b634e487b7160e01b5f52601160045260245ffd5b5f3660031901126102e55763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b346102e5575f3660031901126102e5576020600f54604051908152f35b61033c611399366117ec565b91611a26565b346102e55760203660031901126102e55760206106bb600435611991565b5f3660031901126102e5573068929eee149b4bd212685414610bc0573068929eee149b4bd21268556113ee34611be8565b60ff6003541615611403576020610b6e611ca7565b63447691f760e01b5f5260045ffd5b346102e5575f3660031901126102e557602060ff600354166040519015158152f35b346102e5575f3660031901126102e5576020600e54604051908152f35b60403660031901126102e5576114656117c0565b50631eb49d6d60e11b5f5260045ffd5b346102e55760203660031901126102e55760043567ffffffffffffffff81116102e5576114a6903690600401611792565b6114ae611bcc565b67ffffffffffffffff8111610761576114c8601054611826565b601f811161156f575b505f601f821160011461150a5781925f926114ff575b50505f19600383901b1c191660019190911b17601055005b0135905082806114e7565b601f198216925f80516020611f93833981519152915f5b8581106115575750836001951061153e575b505050811b01601055005b01355f19600384901b60f8161c19169055828080611533565b90926020600181928686013581550194019101611521565b601f820160051c5f80516020611f938339815191520190602083106115be575b601f0160051c5f80516020611f9383398151915201905b8181106115b357506114d1565b5f81556001016115a6565b5f80516020611f93833981519152915061158f565b346102e55760203660031901126102e5576004355f818152673ec412a9852d173d60c11b601c5260209020810101805460601b156110a357600101546040516001600160a01b039091168152602090f35b346102e5575f3660031901126102e5576020600854604051908152f35b346102e5575f3660031901126102e5576040515f805461166081611826565b8084529060018116908115610643575060011461168757610533836105278185038261187a565b5f8080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563939250905b8082106116c9575090915081016020016105276105e8565b9192600181602092548385880101520191019092916116b1565b346102e55760203660031901126102e5576004356001600160e01b0319811681036102e55760209060e01c60405190635b5e139f8114906301ffc9a76380ac58cd82149114171715158152f35b346102e5575f3660031901126102e5576020907f00000000000000000000000000000000000000000000000000000000000000008152f35b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b9181601f840112156102e55782359167ffffffffffffffff83116102e557602083818601950101116102e557565b600435906001600160a01b03821682036102e557565b602435906001600160a01b03821682036102e557565b60609060031901126102e5576004356001600160a01b03811681036102e557906024356001600160a01b03811681036102e5579060443590565b90600182811c92168015611854575b602083101461184057565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611835565b60a0810190811067ffffffffffffffff82111761076157604052565b90601f8019910116810190811067ffffffffffffffff82111761076157604052565b604051905f82600154916118af83611826565b808352926001811690811561193457506001146118d5575b6118d39250038361187a565b565b5060015f90815290917fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8183106119185750509060206118d3928201016118c7565b6020919350806001915483858901015201910190918492611900565b602092506118d394915060ff191682840152151560051b8201016118c7565b9190820391821161131357565b8181029291811591840414171561131357565b811561197d570490565b634e487b7160e01b5f52601260045260245ffd5b5f818152673ec412a9852d173d60c11b601c52602090208101015460601b156105a15760ff60065460081c1615611a20576119ce60075442611953565b7f00000000000000000000000000000000000000000000000000000000000000009081811015611a1a57611a11611a1792611a0c600c549384611960565b611973565b90611953565b90565b50505f90565b600c5490565b6001600160a01b038116151580611b33575b610025575f838152673ec412a9852d173d60c11b3317601c52602090208301830180546001600160a01b0393841693928316928116808414810215611b1e5750825f528160010180548033148533141715611b07575b611afe575b50838318189055601c600c205f198154019055815f52601c600c2060018154019063ffffffff8216840215611ae957557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b67ea553b3401336cea841560021b526004601cfd5b5f90555f611a93565b6030600c2054611a8e57634b6e7f185f526004601cfd5b67ceea21b6a1148100901560021b526004601cfd5b506001600160a01b0382161515611a38565b5f1981146113135760010190565b9190820180921161131357565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b03169081156110a357565b60ff60065460081c1615611bc857611a17600c547f000000000000000000000000000000000000000000000000000000000000000090611973565b5f90565b638b78c6d819543303611bdb57565b6382b429005f526004601cfd5b60ff60035460081c161561140357611c236004547f000000000000000000000000000000000000000000000000000000000000000090611b53565b4211611c98577f000000000000000000000000000000000000000000000000000000000000000003611c89576005547f000000000000000000000000000000000000000000000000000000000000000010611c7a57565b6370d818df60e01b5f5260045ffd5b6349986e7360e01b5f5260045ffd5b63046b084160e31b5f5260045ffd5b60055490611cb482611b45565b60055581611cc6600a54600e54611b53565b600e55611cd7600b54600f54611b53565b600f55805f52673ec412a9852d173d60c11b601c5260205f208101810180548060601b611d945733179055335f52601c600c2060018154019063ffffffff8216330215611d7f5755335f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a46005547f000000000000000000000000000000000000000000000000000000000000000010611d7057565b600160ff196006541617600655565b67ea553b3401336cea331560021b526004601cfd5b63c991cbb15f526004601cfd5b611da9611ef0565b8015611dd957804710610d475780611dc66118d392600d54611b53565b600d5542600855638b78c6d81954611e6e565b50565b9060a46020939460405195869463150b7a028652338787015260018060a01b03166040860152606085015260808085015280518091818060a0880152611e5a575b505001905f601c8401915af115611e4c575b5163757a42ff60e11b01611e3f57565b63d1a57ed65f526004601cfd5b3d15611e2f573d5f823e3d90fd5b818760c08801920160045afa50805f611e1d565b814710611ea6575f3881808585620186a0f115611e89575050565b601691600b915f526073825360ff602053f015611ea257565b3838fd5b63b12d13eb5f526004601cfd5b60018060a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3638b78c6d81955565b60ff60065460081c1615611bc8576005545f19810190811161131357600954611f1891611953565b8015611f8d57611f4b6007547f000000000000000000000000000000000000000000000000000000000000000090611b53565b8042105f14611f87575042905b60085480831115611f8057611f73611f7b91611a1794611953565b611f7b611b8d565b611960565b5050505f90565b90611f58565b505f9056fe1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672a2646970667358221220756ee714e3334f79a4569891d267d5122d773c52abbeb5d106153372c19816dd64736f6c634300081a003300000000000000000000000000000000000000000000000000000000000000fa0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000093a800000000000000000000000000000000000000000000000000000000001e1338000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000003000000000000000000000000019817ad02a31b990433542097be29d97613e8cb000000000000000000000000019817ad02a31b990433542097be29d97613e8cb000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003b6261666b7265696464746f746d6a6863326564347979667337616c6e61706b796832637567656969686b73336d6b7979356f68736c6f6b376e66610000000000000000000000000000000000000000000000000000000000000000000000001c46756e64696e67576f726b73203030313a20546f6b656e576f726b730000000000000000000000000000000000000000000000000000000000000000000000025457000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080806040526004361015610034575b50361561002557631eb49d6d60e11b5f5260045ffd5b631eb49d6d60e11b5f5260045ffd5b5f3560e01c9081630197d972146117305750806301ffc9a7146116e357806306fdde0314611641578063075b222514611624578063081812fc146115d35780630913348f14611475578063095ea7b3146114515780630b97d964146114345780630f4161aa146114125780631249c58b146113bd5780631e1d6b771461139f57806323b872dd1461138d57806324bd281614611370578063256929621461132757806327ffb9a2146111fe5780632eb4a7ab146111e15780632f2c269c146111bf57806332cb6b0c1461118557806333e03e091461114b578063358cbf0a1461110e57806342842e0e146110d557806342966c6814610f3e57806344b28d5914610e075780634bb278f314610c11578063520f3cc814610bcd578063537924ef14610ac057806354d1f13d14610a7c5780636352211e14610a4c5780636c02a931146109aa57806370a0823114610957578063715018a61461090e5780637b61c3201461081d5780637cb64759146108d85780638285268e146108bb57806383ea6e97146108815780638da5cb5b14610855578063931e2e491461083857806395d89b411461081d578063a22cb465146107e9578063a2c00d9e146107af578063a8660a7814610792578063b7663e9714610775578063b88d4fde146106c3578063bac3bfb8146106a1578063c002d23d14610667578063c623674f146105b0578063c87b56dd146104a3578063d12397301461047e578063d62f3b1c14610433578063db1704e114610416578063e32a748f146103f9578063e985e9c5146103b5578063eab1e62c14610398578063f04e283e1461034b578063f2fde38b1461030e578063f998c8e8146102e95763fee81cf4146102b3575f61000f565b346102e55760203660031901126102e5576102cc6117c0565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b346102e5575f3660031901126102e557602060ff60065460081c166040519015158152f35b60203660031901126102e5576103226117c0565b61032a611bcc565b8060601b1561033e5761033c90611eb3565b005b637448fbae5f526004601cfd5b60203660031901126102e55761035f6117c0565b610367611bcc565b63389a75e1600c52805f526020600c20908154421161038b575f61033c9255611eb3565b636f5e88185f526004601cfd5b346102e5575f3660031901126102e5576020600d54604051908152f35b346102e55760403660031901126102e5576103ce6117c0565b6103d66117d6565b601c52670a5a2e7a000000006008525f5260206030600c20546040519015158152f35b346102e5575f3660031901126102e5576020600954604051908152f35b346102e5575f3660031901126102e5576020600c54604051908152f35b346102e5575f3660031901126102e55761044b611bcc565b600160ff1960035416176003557fbad7871e16f9b9d8b2a6bd6e38ada7c99940913046fe099cffa0040643fb064e5f80a1005b346102e5575f3660031901126102e557602060ff60035460081c166040519015158152f35b346102e55760203660031901126102e5576004355f818152673ec412a9852d173d60c11b601c52602090208101015460601b156105a15760405166697066733a2f2f60c81b60208201525f906010546104fb81611826565b906001811690811561057d5750600114610537575b50610527816105339303601f19810183528261187a565b60405191829182611768565b0390f35b915060105f525f80516020611f938339815191525f905b83821061056657505090810160270190610527610510565b60018160209254602785870101520191019061054e565b60ff1916602780850191909152821515909202830190910192506105279050610510565b63677510db60e11b5f5260045ffd5b346102e5575f3660031901126102e5576040515f6010546105d081611826565b808452906001811690811561064357506001146105f8575b610533836105278185038261187a565b60105f9081525f80516020611f93833981519152939250905b808210610629575090915081016020016105276105e8565b919260018160209254838588010152019101909291610611565b60ff191660208086019190915291151560051b8401909101915061052790506105e8565b346102e5575f3660031901126102e55760206040517f0000000000000000000000000000000000000000000000000de0b6b3a76400008152f35b346102e5575f3660031901126102e55760206106bb611b8d565b604051908152f35b60803660031901126102e5576106d76117c0565b6106df6117d6565b60443560643567ffffffffffffffff81116102e557610702903690600401611792565b929093610710838383611a26565b813b61071857005b67ffffffffffffffff8411610761576040519361073f601f8201601f19166020018661187a565b80855236818701116102e5576020815f9261033c988389013786010152611ddc565b634e487b7160e01b5f52604160045260245ffd5b346102e5575f3660031901126102e5576020600b54604051908152f35b346102e5575f3660031901126102e5576020600754604051908152f35b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000078152f35b346102e55760403660031901126102e5576108026117c0565b50602435801515036102e557631eb49d6d60e11b5f5260045ffd5b346102e5575f3660031901126102e55761053361052761189c565b346102e5575f3660031901126102e5576020600454604051908152f35b346102e5575f3660031901126102e557638b78c6d819546040516001600160a01b039091168152602090f35b346102e5575f3660031901126102e55760206040517f0000000000000000000000000000000000000000000000000000000000093a808152f35b346102e5575f3660031901126102e5576020600a54604051908152f35b346102e55760203660031901126102e5576004356108f4611bcc565b80156108ff57600255005b631e1d0ab560e01b5f5260045ffd5b5f3660031901126102e557610921611bcc565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35f638b78c6d81955005b346102e55760203660031901126102e5576109706117c0565b801561099d57673ec412a9852d173d60c11b601c525f52602063ffffffff601c600c205416604051908152f35b638f4eb6045f526004601cfd5b346102e5575f3660031901126102e5576040515f80546109c981611826565b808452906001811690811561064357506001146109f057610533836105278185038261187a565b5f8080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563939250905b808210610a32575090915081016020016105276105e8565b919260018160209254838588010152019101909291610a1a565b346102e55760203660031901126102e5576020610a6a600435611b60565b6040516001600160a01b039091168152f35b5f3660031901126102e55763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b60203660031901126102e55760043567ffffffffffffffff81116102e557366023820112156102e557806004013567ffffffffffffffff81116102e55760248160051b8301013681116102e5573068929eee149b4bd212685414610bc0573068929eee149b4bd2126855610b3334611be8565b6002549260405160208101903360601b825260148152610b5460348261187a565b51902092610b91575b505003610b82576020610b6e611ca7565b3868929eee149b4bd2126855604051908152f35b6306fb10a960e01b5f5260045ffd5b602401915b602083359182811160051b9081521852602060405f20920191818310610b96579150508280610b5d565b63ab143c065f526004601cfd5b346102e5575f3660031901126102e5576040517f000000000000000000000000019817ad02a31b990433542097be29d97613e8cb6001600160a01b03168152602090f35b346102e5575f3660031901126102e5573068929eee149b4bd212685414610bc0573068929eee149b4bd2126855638b78c6d81954336001600160a01b038216141580610dd4575b6100255760065460ff81161580610d9e575b610d8f5760081c60ff16610d8057600e5490600f549180610d56575b505080610d0c575b61010061ff0019600654161760065542600755426008557fb968440accd1ce5fa60b00de8bb8d8487eb2fda3c3701fb30fea3f69aa910a486040610cf27f0000000000000000000000000000000000000000000000000000000001e1338042611b53565b8151904282526020820152a13868929eee149b4bd2126855005b804710610d4757610d41905f600f557f000000000000000000000000019817ad02a31b990433542097be29d97613e8cb611e6e565b80610c8e565b63786e0a9960e01b5f5260045ffd5b804710610d4757610d79915f600e55610d7182600d54611b53565b600d55611e6e565b8180610c86565b6372de7acd60e01b5f5260045ffd5b63a4bcf01360e01b5f5260045ffd5b50610dcc6004547f0000000000000000000000000000000000000000000000000000000000093a8090611b53565b421115610c6a565b50337f000000000000000000000000019817ad02a31b990433542097be29d97613e8cb6001600160a01b03161415610c58565b346102e5575f3660031901126102e557610e1f611bcc565b60035460ff8160081c16610f2f5761ff0019166101001760035542600455610ed17f0000000000000000000000000000000000000000000000000de0b6b3a7640000610ecc6064610e907f000000000000000000000000000000000000000000000000000000000000000784611960565b0480600a556064610ec17f000000000000000000000000000000000000000000000000000000000000000385611960565b049283600b55611953565b611953565b600c557fcb0654d378ad3cd02dbc858de79e52889616696769cabc0cbfb2ec86e05504516040610f217f0000000000000000000000000000000000000000000000000000000000093a8042611b53565b8151904282526020820152a1005b6339f3829b60e01b5f5260045ffd5b346102e55760203660031901126102e5576004353068929eee149b4bd212685414610bc0573068929eee149b4bd212685560ff60065460081c16156110c657610f8681611b60565b336001600160a01b03909116036110b757610fa081611991565b90610fa9611da1565b610fb4600954611b45565b6009556001600160a01b03610fc882611b60565b161515806110b0575b610025575f818152673ec412a9852d173d60c11b601c5260209020810181018054906001600160a01b0382169081156110a357815f5280600101928354801560011715611084575b905f94849261107b575b50189055601c600c20821981540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a48061106b575b3868929eee149b4bd2126855005b6110759033611e6e565b8061105d565b85905587611023565b906030600c2054156110965790611019565b634b6e7f185f526004601cfd5b63ceea21b65f526004601cfd5b505f610fd1565b6359dc379f60e01b5f5260045ffd5b635788079960e01b5f5260045ffd5b6110de366117ec565b6110eb8183859495611a26565b823b6110f357005b61033c926040519261110660208561187a565b5f8452611ddc565b346102e5575f3660031901126102e557611126611bcc565b3068929eee149b4bd212685414610bc0573068929eee149b4bd212685561105d611da1565b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000038152f35b346102e5575f3660031901126102e55760206040517f00000000000000000000000000000000000000000000000000000000000000fa8152f35b346102e5575f3660031901126102e557602060ff600654166040519015158152f35b346102e5575f3660031901126102e5576020600254604051908152f35b346102e5575f3660031901126102e5575f608060405161121d8161185e565b82815282602082015282604082015282606082015201527f00000000000000000000000000000000000000000000000000000000000000fa600554905f1982019182116113135760a0916040516112738161185e565b82815260208101918252604081017f0000000000000000000000000000000000000000000000000de0b6b3a76400008152608060608301927f0000000000000000000000000000000000000000000000000000000000093a80845201927f0000000000000000000000000000000000000000000000000000000001e133808452604051948552516020850152516040840152516060830152516080820152f35b634e487b7160e01b5f52601160045260245ffd5b5f3660031901126102e55763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b346102e5575f3660031901126102e5576020600f54604051908152f35b61033c611399366117ec565b91611a26565b346102e55760203660031901126102e55760206106bb600435611991565b5f3660031901126102e5573068929eee149b4bd212685414610bc0573068929eee149b4bd21268556113ee34611be8565b60ff6003541615611403576020610b6e611ca7565b63447691f760e01b5f5260045ffd5b346102e5575f3660031901126102e557602060ff600354166040519015158152f35b346102e5575f3660031901126102e5576020600e54604051908152f35b60403660031901126102e5576114656117c0565b50631eb49d6d60e11b5f5260045ffd5b346102e55760203660031901126102e55760043567ffffffffffffffff81116102e5576114a6903690600401611792565b6114ae611bcc565b67ffffffffffffffff8111610761576114c8601054611826565b601f811161156f575b505f601f821160011461150a5781925f926114ff575b50505f19600383901b1c191660019190911b17601055005b0135905082806114e7565b601f198216925f80516020611f93833981519152915f5b8581106115575750836001951061153e575b505050811b01601055005b01355f19600384901b60f8161c19169055828080611533565b90926020600181928686013581550194019101611521565b601f820160051c5f80516020611f938339815191520190602083106115be575b601f0160051c5f80516020611f9383398151915201905b8181106115b357506114d1565b5f81556001016115a6565b5f80516020611f93833981519152915061158f565b346102e55760203660031901126102e5576004355f818152673ec412a9852d173d60c11b601c5260209020810101805460601b156110a357600101546040516001600160a01b039091168152602090f35b346102e5575f3660031901126102e5576020600854604051908152f35b346102e5575f3660031901126102e5576040515f805461166081611826565b8084529060018116908115610643575060011461168757610533836105278185038261187a565b5f8080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563939250905b8082106116c9575090915081016020016105276105e8565b9192600181602092548385880101520191019092916116b1565b346102e55760203660031901126102e5576004356001600160e01b0319811681036102e55760209060e01c60405190635b5e139f8114906301ffc9a76380ac58cd82149114171715158152f35b346102e5575f3660031901126102e5576020907f0000000000000000000000000000000000000000000000000000000001e133808152f35b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b9181601f840112156102e55782359167ffffffffffffffff83116102e557602083818601950101116102e557565b600435906001600160a01b03821682036102e557565b602435906001600160a01b03821682036102e557565b60609060031901126102e5576004356001600160a01b03811681036102e557906024356001600160a01b03811681036102e5579060443590565b90600182811c92168015611854575b602083101461184057565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611835565b60a0810190811067ffffffffffffffff82111761076157604052565b90601f8019910116810190811067ffffffffffffffff82111761076157604052565b604051905f82600154916118af83611826565b808352926001811690811561193457506001146118d5575b6118d39250038361187a565b565b5060015f90815290917fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8183106119185750509060206118d3928201016118c7565b6020919350806001915483858901015201910190918492611900565b602092506118d394915060ff191682840152151560051b8201016118c7565b9190820391821161131357565b8181029291811591840414171561131357565b811561197d570490565b634e487b7160e01b5f52601260045260245ffd5b5f818152673ec412a9852d173d60c11b601c52602090208101015460601b156105a15760ff60065460081c1615611a20576119ce60075442611953565b7f0000000000000000000000000000000000000000000000000000000001e133809081811015611a1a57611a11611a1792611a0c600c549384611960565b611973565b90611953565b90565b50505f90565b600c5490565b6001600160a01b038116151580611b33575b610025575f838152673ec412a9852d173d60c11b3317601c52602090208301830180546001600160a01b0393841693928316928116808414810215611b1e5750825f528160010180548033148533141715611b07575b611afe575b50838318189055601c600c205f198154019055815f52601c600c2060018154019063ffffffff8216840215611ae957557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b67ea553b3401336cea841560021b526004601cfd5b5f90555f611a93565b6030600c2054611a8e57634b6e7f185f526004601cfd5b67ceea21b6a1148100901560021b526004601cfd5b506001600160a01b0382161515611a38565b5f1981146113135760010190565b9190820180921161131357565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b03169081156110a357565b60ff60065460081c1615611bc857611a17600c547f0000000000000000000000000000000000000000000000000000000001e1338090611973565b5f90565b638b78c6d819543303611bdb57565b6382b429005f526004601cfd5b60ff60035460081c161561140357611c236004547f0000000000000000000000000000000000000000000000000000000000093a8090611b53565b4211611c98577f0000000000000000000000000000000000000000000000000de0b6b3a764000003611c89576005547f00000000000000000000000000000000000000000000000000000000000000fa10611c7a57565b6370d818df60e01b5f5260045ffd5b6349986e7360e01b5f5260045ffd5b63046b084160e31b5f5260045ffd5b60055490611cb482611b45565b60055581611cc6600a54600e54611b53565b600e55611cd7600b54600f54611b53565b600f55805f52673ec412a9852d173d60c11b601c5260205f208101810180548060601b611d945733179055335f52601c600c2060018154019063ffffffff8216330215611d7f5755335f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a46005547f00000000000000000000000000000000000000000000000000000000000000fa10611d7057565b600160ff196006541617600655565b67ea553b3401336cea331560021b526004601cfd5b63c991cbb15f526004601cfd5b611da9611ef0565b8015611dd957804710610d475780611dc66118d392600d54611b53565b600d5542600855638b78c6d81954611e6e565b50565b9060a46020939460405195869463150b7a028652338787015260018060a01b03166040860152606085015260808085015280518091818060a0880152611e5a575b505001905f601c8401915af115611e4c575b5163757a42ff60e11b01611e3f57565b63d1a57ed65f526004601cfd5b3d15611e2f573d5f823e3d90fd5b818760c08801920160045afa50805f611e1d565b814710611ea6575f3881808585620186a0f115611e89575050565b601691600b915f526073825360ff602053f015611ea257565b3838fd5b63b12d13eb5f526004601cfd5b60018060a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3638b78c6d81955565b60ff60065460081c1615611bc8576005545f19810190811161131357600954611f1891611953565b8015611f8d57611f4b6007547f0000000000000000000000000000000000000000000000000000000001e1338090611b53565b8042105f14611f87575042905b60085480831115611f8057611f73611f7b91611a1794611953565b611f7b611b8d565b611960565b5050505f90565b90611f58565b505f9056fe1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672a2646970667358221220756ee714e3334f79a4569891d267d5122d773c52abbeb5d106153372c19816dd64736f6c634300081a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000fa0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000093a800000000000000000000000000000000000000000000000000000000001e1338000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000003000000000000000000000000019817ad02a31b990433542097be29d97613e8cb000000000000000000000000019817ad02a31b990433542097be29d97613e8cb000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003b6261666b7265696464746f746d6a6863326564347979667337616c6e61706b796832637567656969686b73336d6b7979356f68736c6f6b376e66610000000000000000000000000000000000000000000000000000000000000000000000001c46756e64696e67576f726b73203030313a20546f6b656e576f726b730000000000000000000000000000000000000000000000000000000000000000000000025457000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _maxSupply (uint256): 250
Arg [1] : _mintPrice (uint256): 1000000000000000000
Arg [2] : _mintPeriod (uint256): 604800
Arg [3] : _vestingPeriod (uint256): 31536000
Arg [4] : _initialPayoutPct (uint256): 7
Arg [5] : _tokenWorksFeePct (uint256): 3
Arg [6] : _deployer (address): 0x019817aD02a31B990433542097bE29D97613E8Cb
Arg [7] : _tokenWorks (address): 0x019817aD02a31B990433542097bE29D97613E8Cb
Arg [8] : _ipfsHash (string): bafkreiddtotmjhc2ed4yyfs7alnapkyh2cugeiihks3mkyy5ohslok7nfa
Arg [9] : _tokenName (string): FundingWorks 001: TokenWorks
Arg [10] : _tokenSymbol (string): TW

-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000fa
Arg [1] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000093a80
Arg [3] : 0000000000000000000000000000000000000000000000000000000001e13380
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [6] : 000000000000000000000000019817ad02a31b990433542097be29d97613e8cb
Arg [7] : 000000000000000000000000019817ad02a31b990433542097be29d97613e8cb
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [9] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [11] : 000000000000000000000000000000000000000000000000000000000000003b
Arg [12] : 6261666b7265696464746f746d6a6863326564347979667337616c6e61706b79
Arg [13] : 6832637567656969686b73336d6b7979356f68736c6f6b376e66610000000000
Arg [14] : 000000000000000000000000000000000000000000000000000000000000001c
Arg [15] : 46756e64696e67576f726b73203030313a20546f6b656e576f726b7300000000
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [17] : 5457000000000000000000000000000000000000000000000000000000000000


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.