ETH Price: $3,399.63 (+3.48%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...371066992025-10-20 23:25:4518 days ago1761002745IN
0x952BbDED...3CA992B88
0 ETH0.000000240.00522106
Set Approval For...358924592025-09-22 20:51:0546 days ago1758574265IN
0x952BbDED...3CA992B88
0 ETH0.000000470.01035105
Set Approval For...330726472025-07-19 14:17:21112 days ago1752934641IN
0x952BbDED...3CA992B88
0 ETH0.000000290.00631263
Set Approval For...330710622025-07-19 13:24:31112 days ago1752931471IN
0x952BbDED...3CA992B88
0 ETH0.000000230.0050551
Set Approval For...305731022025-05-22 17:39:11169 days ago1747935551IN
0x952BbDED...3CA992B88
0 ETH0.000000210.00285242
Set Approval For...292444302025-04-21 23:30:07200 days ago1745278207IN
0x952BbDED...3CA992B88
0 ETH0.000000040.00091757
Set Approval For...290519142025-04-17 12:32:55205 days ago1744893175IN
0x952BbDED...3CA992B88
0 ETH0.00000130.00309661
Set Approval For...276480572025-03-16 0:37:41237 days ago1742085461IN
0x952BbDED...3CA992B88
0 ETH0.000000090.00200938
Set Approval For...266142392025-02-20 2:17:05261 days ago1740017825IN
0x952BbDED...3CA992B88
0 ETH0.00000020.00393137
Set Approval For...256922632025-01-29 18:04:33282 days ago1738173873IN
0x952BbDED...3CA992B88
0 ETH0.000000770.00443985
Set Approval For...255610882025-01-26 17:12:03285 days ago1737911523IN
0x952BbDED...3CA992B88
0 ETH0.000001520.02999509
Set Approval For...249764692025-01-13 4:24:45299 days ago1736742285IN
0x952BbDED...3CA992B88
0 ETH0.000001110.01381524
Set Approval For...247897182025-01-08 20:39:43303 days ago1736368783IN
0x952BbDED...3CA992B88
0 ETH0.000002360.02093136
Set Approval For...247506582025-01-07 22:57:43304 days ago1736290663IN
0x952BbDED...3CA992B88
0 ETH0.000001420.02496473
Set Approval For...247002002025-01-06 18:55:47305 days ago1736189747IN
0x952BbDED...3CA992B88
0 ETH0.000003220.02301056
Mint247001452025-01-06 18:53:57305 days ago1736189637IN
0x952BbDED...3CA992B88
0 ETH0.000005380.02408306
Mint247001422025-01-06 18:53:51305 days ago1736189631IN
0x952BbDED...3CA992B88
0 ETH0.000005530.0240855
Mint247001392025-01-06 18:53:45305 days ago1736189625IN
0x952BbDED...3CA992B88
0 ETH0.000005280.02408714
Mint247001352025-01-06 18:53:37305 days ago1736189617IN
0x952BbDED...3CA992B88
0 ETH0.000005280.02409817
Mint247001312025-01-06 18:53:29305 days ago1736189609IN
0x952BbDED...3CA992B88
0 ETH0.000005430.02411017
Set Approval For...244841992025-01-01 18:55:45310 days ago1735757745IN
0x952BbDED...3CA992B88
0 ETH0.000000880.01721002
Mint236069142024-12-12 11:32:55331 days ago1734003175IN
0x952BbDED...3CA992B88
0 ETH0.00000220.01853086
Mint236069062024-12-12 11:32:39331 days ago1734003159IN
0x952BbDED...3CA992B88
0 ETH0.000002160.01859573
Mint236068972024-12-12 11:32:21331 days ago1734003141IN
0x952BbDED...3CA992B88
0 ETH0.000002080.01865731
Mint236068872024-12-12 11:32:01331 days ago1734003121IN
0x952BbDED...3CA992B88
0 ETH0.000002210.01875601
View all transactions

Latest 7 internal transactions

Parent Transaction Hash Block From To
175685412024-07-25 16:53:49470 days ago1721926429
0x952BbDED...3CA992B88
 Contract Creation0 ETH
164130052024-06-28 22:55:57497 days ago1719615357
0x952BbDED...3CA992B88
 Contract Creation0 ETH
164129052024-06-28 22:52:37497 days ago1719615157
0x952BbDED...3CA992B88
 Contract Creation0 ETH
164084462024-06-28 20:23:59497 days ago1719606239
0x952BbDED...3CA992B88
 Contract Creation0 ETH
164084462024-06-28 20:23:59497 days ago1719606239
0x952BbDED...3CA992B88
 Contract Creation0 ETH
164084462024-06-28 20:23:59497 days ago1719606239
0x952BbDED...3CA992B88
 Contract Creation0 ETH
164084462024-06-28 20:23:59497 days ago1719606239
0x952BbDED...3CA992B88
 Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xCB6ba303...3e272c98C
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
OnchainArtExperiments

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

pragma solidity ^0.8.13;


import {ERC721} from "./Solady/ERC721.sol";
import {Edition} from "./structs.sol";
import {ArtBase} from "./ArtBase.sol";
import {ISignature,IPaymentSplitter} from "./interfaces.sol";
import {LibString} from "./Solady/LibString.sol";
import {PaymentSplitter} from "openzeppelin-contracts/contracts/finance/PaymentSplitter.sol";

contract OnchainArtExperiments is ArtBase, ERC721 {

    mapping(uint => Edition) private editions;
    mapping(uint => bytes32) private idToSeed;

    address public OWNER;
    uint public EDITION_COUNTER;
    uint public PLATFORM_FEE = 5; // 5% of mint price
    uint public PLATFORM_ROYALTY = 10; // 10% of artist defined royalty
    
    modifier onlyOwner {
        if(msg.sender != OWNER) revert NotOwner();
        _;

    }

    modifier onlyArtist(uint editionId) {
        if(msg.sender != editions[editionId].artistAddress) revert NotArtist();
        _;

    }

    


    constructor()  {
        OWNER = msg.sender;
    }



    function name() public view override returns (string memory){
        return "Onchain-Experiments_V1";
    }

    function symbol() public view override returns (string memory) {
        return "O-E_V1";
    }

    function getMetadata(Edition memory edition, uint tokenId) internal view returns(string memory signature, string memory metadata) {
        // if signatureId is of the signature edition and is currently owned by the edition artist
        if(edition.signatureId/1_000_000 == 1 && _exists(edition.signatureId) && ownerOf(edition.signatureId) == edition.artistAddress){
            // try to get signature translate values if artGenerator supports it
            try edition.artGenerator.getSignatureTranslate(idToSeed[tokenId]) returns (int _translateX, int _translateY) {
                // ASSUMES SIGNATURES IS EDITION #1
                signature = ISignature(address(editions[1].artGenerator)).getPlainSignature(idToSeed[edition.signatureId], _translateX, _translateY);

            } catch {
                // if it does not support it, put signature at default coordinates
                signature = ISignature(address(editions[1].artGenerator)).getPlainSignature(idToSeed[edition.signatureId], 900, 925);
            }
        }
        metadata = string.concat('<title>', edition.name, ' #', LibString.toString(tokenId % 1_000_000),'</title><desc>art by ',bytes(edition.artistName).length == 0 ? LibString.toHexString(edition.artistAddress) : edition.artistName, '</desc>');
    }

    function getRawSvg(uint tokenId) external view returns(string memory) {
        if(!_exists(tokenId)) revert TokenDoesNotExist();
        Edition memory edition = editions[tokenId / 1_000_000];
        (string memory signature, string memory metadata) = getMetadata(edition, tokenId);
        string memory svg = edition.artGenerator.getRawSvg(idToSeed[tokenId]);
        return string.concat('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" height="100%" width="100%">',metadata, svg, signature, '</svg>');
    }

    function getDataUri(uint tokenId) external view returns(string memory) {
        return generateDataURI(this.getRawSvg(tokenId));
    }

    function getEdition(uint edition) external view returns(Edition memory) {
        return editions[edition];
    }
 

    function modify(uint tokenId, bytes calldata data) external {
        if(msg.sender != ownerOf(tokenId)) revert NotOwner();
        idToSeed[tokenId] = editions[tokenId / 1_000_000].artGenerator.modify(tokenId, data);
    }


    function exists(uint tokenId) external view returns(bool) {
        return _exists(tokenId);
    }

    
    function tokenURI(uint tokenId) public view override returns (string memory) {
        if(!_exists(tokenId)) revert TokenDoesNotExist();
        Edition memory edition = editions[tokenId / 1_000_000];
        
        (string memory svg, string memory attributes) = edition.artGenerator.getRawSvgAndAttributes(idToSeed[tokenId]);


        (string memory signature, string memory metadata) = getMetadata(edition, tokenId);
        
        svg = string.concat('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" height="100%" width="100%">',metadata, svg, signature, '</svg>');


        return generateTokenURI(edition.name, edition.artistName, edition.artistAddress, edition.description, svg, tokenId, attributes);
    }

    function getSeed(uint id) external view returns(bytes32) {
        return idToSeed[id];
    }

    // converts the packed bytes32 seed to an unpacked bytes array so we can abi.decode it off chain or in other contracts
    function unPackSeed(uint tokenId) external view returns(bytes memory) {
        return editions[tokenId / 1_000_000].artGenerator.unPackSeed(abi.encode(idToSeed[tokenId]));
    }

    ///////////////////////// artist controls: /////////////////////////

    function setMintStatus(uint edition, bool status) external onlyArtist(edition) {
        editions[edition].mintStatus = status;
    }

    function deleteEdition(uint edition) external onlyArtist(edition) {
        if(editions[edition].counter > 0) revert EditionHasMintedTokens();
        delete editions[edition];
    }
    function setRoyalty(uint edition, uint basisPoints) external onlyArtist(edition) {
        editions[edition].royalty = basisPoints;
    }

    function artistMint(uint edition) external onlyArtist(edition) returns (uint){
        return _baseMint(edition, msg.sender);
    }
    function setPrice(uint edition, uint price) external onlyArtist(edition) {
        editions[edition].price = price;
    }
    function setSignatureId(uint edition, uint id) external onlyArtist(edition)  {
        editions[edition].signatureId = id;
    }
    
    ///////////////////////////////////////////////////////////////////





    //////////////////////////owner controls///////////////////////////

    function changeOwner(address newOwner) external onlyOwner {
        OWNER = newOwner;
    }

    function setPlatformFee(uint fee) external onlyOwner {
        PLATFORM_FEE = fee;
    }
    // only affects edition deployed after this is set
    function setPlatformRoyalty(uint royalty) external onlyOwner {
        PLATFORM_ROYALTY = royalty;
    }
    function releasePlatformRoyalty(uint edition) external onlyOwner {
        IPaymentSplitter(editions[edition].royaltyReceiver).release(payable(address(this)));
    }
        
    function createNewEdition(string memory name, string memory description, uint supply, address artGenerator, address artistAddress, string memory artistName) external onlyOwner returns(uint) {

        address[] memory payees = new address[](2);
        payees[0] = artistAddress;
        payees[1] = address(this);
        uint[] memory shares = new uint[](2);
        shares[0] = 100 - PLATFORM_ROYALTY;
        shares[1] = PLATFORM_ROYALTY;
        address paymentSplitter = address(new PaymentSplitter(payees, shares));


        EDITION_COUNTER++;
        editions[EDITION_COUNTER] = _createEdition(name, description, supply, artGenerator, artistAddress, artistName, paymentSplitter);

        return EDITION_COUNTER;
    }
    
    function withdraw() external onlyOwner {
        OWNER.call{value: address(this).balance}("");
    }


    //////////////////////////////////////////////////////////////////
    




    




    function royaltyInfo(uint tokenId, uint salePrice) external view returns (address receiver, uint royaltyAmount) {
        Edition memory edition = editions[tokenId / 1_000_000];
        return (edition.royaltyReceiver, (salePrice * edition.royalty) / 10_000);

    }



    function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
        return interfaceId == this.royaltyInfo.selector || super.supportsInterface(interfaceId);
    }


    function _baseMint(uint editionId, address to) private returns (uint) {
       Edition storage edition = editions[editionId];
        if(++edition.counter > edition.supply) revert MaxSupplyReached(); 
        uint id = (editionId * 1_000_000) + edition.counter;
        idToSeed[id] = edition.artGenerator.createSeed(id, to);
        _safeMint(to, id); 
        return id;
    } 


    // mints next token id for 'edition'
    function mint(uint editionId) external payable returns (uint) {
        Edition memory edition = editions[editionId];
        if(!edition.mintStatus) revert NotMintable();
        if(msg.value != edition.price) revert IncorrectPrice();
        // opting to directly transfer the funds to the artist in 
        // the mint function for simplicity
        if(msg.value > 0) {
            uint platformFee = (msg.value * PLATFORM_FEE) / 100;
            payable(edition.artistAddress).call{value: msg.value - platformFee}("");
        }

        return _baseMint(editionId, msg.sender);

    }

    // can be used in a warpcast frame where I pay the gas for the user
    function mintTo(uint editionId, address to) external payable returns (uint){
        Edition memory edition = editions[editionId];
        if(!edition.mintStatus) revert NotMintable();
        if(msg.value != edition.price) revert IncorrectPrice();
        if(msg.value > 0) {
            uint platformFee = (msg.value * PLATFORM_FEE) / 100;
            payable(address(this)).call{value: platformFee}("");
            payable(edition.artistAddress).call{value: msg.value - platformFee}("");
        }
        
        return _baseMint(editionId, to);
    }


    

    function tokensOfOwner(address owner) public view returns(uint[] memory) {
        uint balance = balanceOf(owner);

        uint[] memory result = new uint[](balance);
        uint index;
        for (uint i = 1; i <=EDITION_COUNTER; i++) {
            for(uint j=1; j<=editions[i].counter; j++) {
                uint tokenId = (i * 1_000_000) + j;
                if(_exists(tokenId) && ownerOf(tokenId) == owner) {
                    
                    result[index++] = tokenId;
                }
            }
                
        }
        return result;
    }



    function burn(uint tokenId) external {
        _burn(msg.sender, tokenId);
    }

}

File 2 of 13 : 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 {
        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)
            }
        }
    }
}

pragma solidity ^0.8.13;

import {IArtGenerator} from "./interfaces.sol";


    struct Attributes {
        string attributes;
    }

    function add(Attributes memory self, string memory key, string memory value) pure returns(Attributes memory) {
        
        self.attributes = string.concat(self.attributes, bytes(self.attributes).length == 0 ?  '{"trait_type": "' : ',{"trait_type": "', key, '", "value":"', value, '"}');
        return self;

    }

    function json(Attributes memory self) pure returns(string memory tokenAttributes) {
        return string.concat("[",self.attributes, "]");
    }


    using{add,json} for Attributes global;


    struct Edition {
        string name;
        string description;
        string artistName;
        uint supply;
        uint counter;
        uint royalty;
        uint price;
        uint signatureId;
        bool mintStatus;
        IArtGenerator artGenerator;
        address royaltyReceiver;
        address artistAddress;
        


    }

pragma solidity ^0.8.13;

import {Base64} from "./Solady/Base64.sol";
import {LibString} from "./Solady/LibString.sol";
import {Attributes, Edition} from "./structs.sol";
import {IArtGenerator} from "./interfaces.sol";

abstract contract ArtBase {

    error NotOwner();
    error MaxSupplyReached();
    error NotMintable();
    error IncorrectPrice();
    error EditionHasMintedTokens();
    error NotArtist();
    error PlatformNotActive();
    
    function _createEdition(string memory name, string memory description, uint supply, address artGenerator, address artist, string memory artistName, address paymentSplitter) internal pure returns(Edition memory edition) {
        edition.name = name;
        edition.description = description;
        edition.supply = supply;
        edition.artGenerator = IArtGenerator(artGenerator);
        edition.royaltyReceiver = paymentSplitter;
        edition.artistAddress = artist;
        edition.artistName = artistName;
    }



    function generateDataURI(string memory rawSvg) internal pure returns(string memory) {
        return string.concat("data:image/svg+xml;base64,", Base64.encode(bytes(rawSvg)));
    }


    
    function generateTokenURI(string memory name, string memory artistName, address artistAddress, string memory description, string memory svg, uint tokenId, string memory attributes) internal pure returns(string memory) {
        return string.concat(
            "data:application/json;base64,",

                Base64.encode(
                    abi.encodePacked(
                        '{"name": "', 
                        name,
                        " #",
                        LibString.toString(tokenId % 1_000_000),
                        '", "description": "',
                        description,
                        '", "image": "data:image/svg+xml;base64,',
                        Base64.encode(bytes(svg)),
                        '", "artist": "',
                        bytes(artistName).length == 0 ? LibString.toHexString(artistAddress) : artistName,
                        '", "attributes": ', 
                        attributes,
                        '}')
                    )
                );


    }

    

    receive() external payable {}


}

pragma solidity ^0.8.13;

import {Base64} from "./Solady/Base64.sol";
import {LibString} from "./Solady/LibString.sol";
import {Edition} from "./structs.sol";



interface IPaymentSplitter {
    function release(address payable account) external;
}

interface ISignature {
    function getPlainSignature(bytes32,int256,int256) external view returns(string memory);
}

interface IArtGenerator {


    function getRawSvg(bytes32 seed) external view returns(string memory);
    function getRawSvgAndAttributes(bytes32 seed) external view returns(string memory, string memory);
    function modify(uint tokenId, bytes memory data) external view returns (bytes32);
    function unPackSeed(bytes calldata seed) external pure returns(bytes memory);
    function createSeed(uint tokenId, address minter) external view returns(bytes32);
    // function dimensions(bytes32 seed) external view returns(uint,uint);
    function getSignatureTranslate(bytes32 seed) external view returns(int, int);

}


interface IArt {

    function getRawSvg(uint id) external view returns (string memory);
    function getDataUri(uint id) external view returns (string memory);
    function dimensionsOfId(uint id) external view returns (uint,uint);
    function getEdition(uint id) external view returns (Edition memory);
    function modify(uint id, bytes calldata data) external;
    function getSeed(uint id) external view returns (bytes32);
    function unPackSeed(uint id) external view returns (bytes memory);

    function updateSeed(uint tokenId) external;
    function ownerOf(uint tokenId) external view returns(address);
    function exists(uint tokenId) external view returns(bool);
    function getSignature(int translateX, int translateY) external view returns(string memory);
    
}

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

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The length of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /// @dev The length of the string is more than 32 bytes.
    error TooBigForSmallString();

    /// @dev The input string must be a 7-bit ASCII.
    error StringNot7BitASCII();

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

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = type(uint256).max;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
    uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;

    /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;

    /// @dev Lookup for '0123456789'.
    uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;

    /// @dev Lookup for '0123456789abcdefABCDEF'.
    uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;

    /// @dev Lookup for '01234567'.
    uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
    uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;

    /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
    uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;

    /// @dev Lookup for ' \t\n\r\x0b\x0c'.
    uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;

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

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits.
            str := add(mload(0x40), 0x80)
            // Update the free memory pointer to allocate.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            let w := not(0) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 1)`.
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) {
            return toString(uint256(value));
        }
        unchecked {
            str = toString(~uint256(value) + 1);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }

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

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexStringNoPrefix(uint256 value, uint256 length)
        internal
        pure
        returns (string memory str)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let start := sub(str, add(length, length))
            let w := not(1) // Tsk.
            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {} 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(str, start)) { break }
            }

            if temp {
                mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x".
    /// The output excludes leading "0" from the `toHexString` output.
    /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
    function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
            str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
    /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
    function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := mload(str) // Get the length.
            str := add(str, o) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            str := add(mload(0x40), 0x80)
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let w := not(1) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) { break }
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksummed(address value) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            for { let i := 0 } 1 {} {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
            mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
            o := add(o, 0x20)
            mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)

            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            str := add(str, 2)
            mstore(str, 40)

            let o := add(str, 0x20)
            mstore(add(o, 40), 0)

            value := shl(96, value)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let i := 0 } 1 {} {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexString(bytes memory raw) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(raw)
            str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(str, add(length, length)) // Store the length of the output.

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let o := add(str, 0x20)
            let end := add(raw, length)

            for {} iszero(eq(raw, end)) {} {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate the memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for { result := 1 } 1 { result := add(result, 1) } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    if iszero(lt(o, end)) { break }
                }
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string.
    /// (i.e. all characters codes are in [0..127])
    function is7BitASCII(string memory s) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(7, div(not(0), 255))
            result := 1
            let n := mload(s)
            if n {
                let o := add(s, 0x20)
                let end := add(o, n)
                let last := mload(end)
                mstore(end, 0)
                for {} 1 {} {
                    if and(mask, mload(o)) {
                        result := 0
                        break
                    }
                    o := add(o, 0x20)
                    if iszero(lt(o, end)) { break }
                }
                mstore(end, last)
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string,
    /// AND all characters are in the `allowed` lookup.
    /// Note: If `s` is empty, returns true regardless of `allowed`.
    function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            if mload(s) {
                let allowed_ := shr(128, shl(128, allowed))
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for {} 1 {} {
                    result := and(result, shr(byte(0, mload(o)), allowed_))
                    o := add(o, 1)
                    if iszero(and(result, lt(o, end))) { break }
                }
            }
        }
    }

    /// @dev Converts the bytes in the 7-bit ASCII string `s` to
    /// an allowed lookup for use in `is7BitASCII(s, allowed)`.
    /// To save runtime gas, you can cache the result in an immutable variable.
    function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for {} 1 {} {
                    result := or(result, shl(byte(0, mload(o)), 1))
                    o := add(o, 1)
                    if iszero(lt(o, end)) { break }
                }
                if shr(128, result) {
                    mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance and bytecode compactness, byte string operations are restricted
    // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
    // Usage of byte string operations on charsets with runes spanning two or more bytes
    // can lead to undefined behavior.

    /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
    function replace(string memory subject, string memory search, string memory replacement)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
            mstore(last, 0)
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
            mstore(result, k) // Store the length.
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    if iszero(gt(from, subjectLength)) {
                        result := from
                        break
                    }
                    result := subjectLength
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)

                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(add(search, 0x20))

                if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }

                if iszero(lt(searchLength, 0x20)) {
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) { break }
                    }
                    break
                }
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                result := not(0) // Initialize to `NOT_FOUND`.
                let searchLength := mload(search)
                if gt(searchLength, mload(subject)) { break }
                let w := result

                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) { from := fromMax }

                let end := add(add(subject, 0x20), w)
                subject := add(add(subject, 0x20), from)
                if iszero(gt(subject, end)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(end, 1))
                        break
                    }
                    subject := add(subject, w) // `sub(subject, 1)`.
                    if iszero(gt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns true if `search` is found in `subject`, false otherwise.
    function contains(string memory subject, string memory search) internal pure returns (bool) {
        return indexOf(subject, search) != NOT_FOUND;
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(
                    keccak256(add(subject, 0x20), searchLength),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    if iszero(times) { break }
                }
                mstore(output, 0) // Zeroize the slot after the string.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength) // Store the length.
                // Allocate the memory.
                mstore(0x40, add(result, add(resultLength, 0x20)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(string memory subject, uint256 start, uint256 end)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) { end := subjectLength }
            if iszero(gt(subjectLength, start)) { start := subjectLength }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                let w := not(0x1f)
                // Copy the `subject` one word at a time, backwards.
                for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start)
        internal
        pure
        returns (string memory result)
    {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256[] memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter)
        internal
        pure
        returns (string[] memory result)
    {
        uint256[] memory indices = indicesOf(subject, delimiter);
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            let indexPtr := add(indices, 0x20)
            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
            mstore(add(indicesEnd, w), mload(subject))
            mstore(indices, add(mload(indices), 1))
            let prevIndex := 0
            for {} 1 {} {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let elementLength := sub(index, prevIndex)
                    mstore(element, elementLength)
                    // Copy the `subject` one word at a time, backwards.
                    for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                        o := add(o, w) // `sub(o, 0x20)`.
                        if iszero(o) { break }
                    }
                    // Zeroize the slot after the string.
                    mstore(add(add(element, 0x20), elementLength), 0)
                    // Allocate memory for the length and the bytes,
                    // rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
                    // Store the `element` into the array.
                    mstore(indexPtr, element)
                }
                prevIndex := add(index, mload(delimiter))
                indexPtr := add(indexPtr, 0x20)
                if iszero(lt(indexPtr, indicesEnd)) { break }
            }
            result := indices
            if iszero(mload(delimiter)) {
                result := add(indices, 0x20)
                mstore(result, sub(mload(indices), 2))
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            for { let o := and(add(aLength, 0x20), w) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, aLength)
            // Copy `b` one word at a time, backwards.
            for { let o := and(add(bLength, 0x20), w) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 0x1f), w))
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function toCase(string memory subject, bool toUpper)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                let w := not(0)
                for { let o := length } 1 {} {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    if iszero(o) { break }
                }
                result := mload(0x40)
                mstore(result, length) // Store the length.
                let last := add(add(result, 0x20), length)
                mstore(last, 0) // Zeroize the slot after the string.
                mstore(0x40, add(last, 0x20)) // Allocate the memory.
            }
        }
    }

    /// @dev Returns a string from a small bytes32 string.
    /// `s` must be null-terminated, or behavior will be undefined.
    function fromSmallString(bytes32 s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let n := 0
            for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
            mstore(result, n)
            let o := add(result, 0x20)
            mstore(o, s)
            mstore(add(o, n), 0)
            mstore(0x40, add(result, 0x40))
        }
    }

    /// @dev Returns the small string, with all bytes after the first null byte zeroized.
    function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
            mstore(0x00, s)
            mstore(result, 0x00)
            result := mload(0x00)
        }
    }

    /// @dev Returns the string as a normalized null-terminated small string.
    function toSmallString(string memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(s)
            if iszero(lt(result, 33)) {
                mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                revert(0x1c, 0x04)
            }
            result := shl(shl(3, sub(32, result)), mload(add(s, result)))
        }
    }

    /// @dev Returns a lowercased copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function upper(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            // Store the bytes of the packed offsets and strides into the scratch space.
            // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
            mstore(0x1f, 0x900094)
            mstore(0x08, 0xc0000000a6ab)
            // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
            mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(result, c)
                    result := add(result, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 0x1f)))
                result := add(result, shr(5, t))
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
    function escapeJSON(string memory s, bool addDoubleQuotes)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            // Store "\\u0000" in scratch space.
            // Store "0123456789abcdef" in scratch space.
            // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
            // into the scratch space.
            mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
            // Bitmask for detecting `["\"","\\"]`.
            let e := or(shl(0x22, 1), shl(0x5c, 1))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) {
                        // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c)
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) {
                    // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        result = escapeJSON(s, false);
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(string memory a, string memory b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
        }
    }

    /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
    function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // These should be evaluated on compile time, as far as possible.
            let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
            let x := not(or(m, or(b, add(m, and(b, m)))))
            let r := shl(7, iszero(iszero(shr(128, x))))
            r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes.
                    mload(add(a, 0x1f)),
                    // `length != 0 && length < 32`. Abuses underflow.
                    // Assumes that the length is valid and within the block gas limit.
                    lt(sub(mload(a), 1), 0x1f)
                )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behavior is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes of `a` and `b`.
                    or(
                        shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
                        mload(sub(add(b, 0x1e), aLength))
                    ),
                    // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                    // Assumes that the lengths are valid and within the block gas limit.
                    lt(sub(add(aLength, mload(b)), 1), 0x1e)
                )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
    function unpackTwo(bytes32 packed)
        internal
        pure
        returns (string memory resultA, string memory resultB)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Assumes that the string does not start from the scratch space.
            let retStart := sub(a, 0x20)
            let retUnpaddedSize := add(mload(a), 0x40)
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(retStart, retUnpaddedSize), 0)
            // Store the return offset.
            mstore(retStart, 0x20)
            // End the transaction, returning the string.
            return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (finance/PaymentSplitter.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/utils/SafeERC20.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";

/**
 * @title PaymentSplitter
 * @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
 * that the Ether will be split in this way, since it is handled transparently by the contract.
 *
 * The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
 * account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
 * an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
 * time of contract deployment and can't be updated thereafter.
 *
 * `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
 * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
 * function.
 *
 * NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
 * tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
 * to run tests before sending real value to this contract.
 */
contract PaymentSplitter is Context {
    event PayeeAdded(address account, uint256 shares);
    event PaymentReleased(address to, uint256 amount);
    event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
    event PaymentReceived(address from, uint256 amount);

    uint256 private _totalShares;
    uint256 private _totalReleased;

    mapping(address => uint256) private _shares;
    mapping(address => uint256) private _released;
    address[] private _payees;

    mapping(IERC20 => uint256) private _erc20TotalReleased;
    mapping(IERC20 => mapping(address => uint256)) private _erc20Released;

    /**
     * @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
     * the matching position in the `shares` array.
     *
     * All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
     * duplicates in `payees`.
     */
    constructor(address[] memory payees, uint256[] memory shares_) payable {
        require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
        require(payees.length > 0, "PaymentSplitter: no payees");

        for (uint256 i = 0; i < payees.length; i++) {
            _addPayee(payees[i], shares_[i]);
        }
    }

    /**
     * @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
     * reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
     * reliability of the events, and not the actual splitting of Ether.
     *
     * To learn more about this see the Solidity documentation for
     * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
     * functions].
     */
    receive() external payable virtual {
        emit PaymentReceived(_msgSender(), msg.value);
    }

    /**
     * @dev Getter for the total shares held by payees.
     */
    function totalShares() public view returns (uint256) {
        return _totalShares;
    }

    /**
     * @dev Getter for the total amount of Ether already released.
     */
    function totalReleased() public view returns (uint256) {
        return _totalReleased;
    }

    /**
     * @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
     * contract.
     */
    function totalReleased(IERC20 token) public view returns (uint256) {
        return _erc20TotalReleased[token];
    }

    /**
     * @dev Getter for the amount of shares held by an account.
     */
    function shares(address account) public view returns (uint256) {
        return _shares[account];
    }

    /**
     * @dev Getter for the amount of Ether already released to a payee.
     */
    function released(address account) public view returns (uint256) {
        return _released[account];
    }

    /**
     * @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
     * IERC20 contract.
     */
    function released(IERC20 token, address account) public view returns (uint256) {
        return _erc20Released[token][account];
    }

    /**
     * @dev Getter for the address of the payee number `index`.
     */
    function payee(uint256 index) public view returns (address) {
        return _payees[index];
    }

    /**
     * @dev Getter for the amount of payee's releasable Ether.
     */
    function releasable(address account) public view returns (uint256) {
        uint256 totalReceived = address(this).balance + totalReleased();
        return _pendingPayment(account, totalReceived, released(account));
    }

    /**
     * @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
     * IERC20 contract.
     */
    function releasable(IERC20 token, address account) public view returns (uint256) {
        uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
        return _pendingPayment(account, totalReceived, released(token, account));
    }

    /**
     * @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
     * total shares and their previous withdrawals.
     */
    function release(address payable account) public virtual {
        require(_shares[account] > 0, "PaymentSplitter: account has no shares");

        uint256 payment = releasable(account);

        require(payment != 0, "PaymentSplitter: account is not due payment");

        // _totalReleased is the sum of all values in _released.
        // If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
        _totalReleased += payment;
        unchecked {
            _released[account] += payment;
        }

        Address.sendValue(account, payment);
        emit PaymentReleased(account, payment);
    }

    /**
     * @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
     * percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
     * contract.
     */
    function release(IERC20 token, address account) public virtual {
        require(_shares[account] > 0, "PaymentSplitter: account has no shares");

        uint256 payment = releasable(token, account);

        require(payment != 0, "PaymentSplitter: account is not due payment");

        // _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
        // If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
        // cannot overflow.
        _erc20TotalReleased[token] += payment;
        unchecked {
            _erc20Released[token][account] += payment;
        }

        SafeERC20.safeTransfer(token, account, payment);
        emit ERC20PaymentReleased(token, account, payment);
    }

    /**
     * @dev internal logic for computing the pending payment of an `account` given the token historical balances and
     * already released amounts.
     */
    function _pendingPayment(
        address account,
        uint256 totalReceived,
        uint256 alreadyReleased
    ) private view returns (uint256) {
        return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
    }

    /**
     * @dev Add a new payee to the contract.
     * @param account The address of the payee to add.
     * @param shares_ The number of shares owned by the payee.
     */
    function _addPayee(address account, uint256 shares_) private {
        require(account != address(0), "PaymentSplitter: account is the zero address");
        require(shares_ > 0, "PaymentSplitter: shares are 0");
        require(_shares[account] == 0, "PaymentSplitter: account already has shares");

        _payees.push(account);
        _shares[account] = shares_;
        _totalShares = _totalShares + shares_;
        emit PayeeAdded(account, shares_);
    }
}

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

/// @notice Library to encode strings in Base64.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
/// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>.
library Base64 {
    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// See: https://datatracker.ietf.org/doc/html/rfc4648
    /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
    /// @param noPadding Whether to strip away the padding.
    function encode(bytes memory data, bool fileSafe, bool noPadding)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                // Multiply by 4/3 rounded up.
                // The `shl(2, ...)` is equivalent to multiplying by 4.
                let encodedLength := shl(2, div(add(dataLength, 2), 3))

                // Set `result` to point to the start of the free memory.
                result := mload(0x40)

                // Store the table into the scratch space.
                // Offsetted by -1 byte so that the `mload` will load the character.
                // We will rewrite the free memory pointer at `0x40` later with
                // the allocated size.
                // The magic constant 0x0670 will turn "-_" into "+/".
                mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, encodedLength)

                let dataEnd := add(add(0x20, data), dataLength)
                let dataEndValue := mload(dataEnd) // Cache the value at the `dataEnd` slot.
                mstore(dataEnd, 0x00) // Zeroize the `dataEnd` slot to clear dirty bits.

                // Run over the input, 3 bytes at a time.
                for {} 1 {} {
                    data := add(data, 3) // Advance 3 bytes.
                    let input := mload(data)

                    // Write 4 bytes. Optimized for fewer stack operations.
                    mstore8(0, mload(and(shr(18, input), 0x3F)))
                    mstore8(1, mload(and(shr(12, input), 0x3F)))
                    mstore8(2, mload(and(shr(6, input), 0x3F)))
                    mstore8(3, mload(and(input, 0x3F)))
                    mstore(ptr, mload(0x00))

                    ptr := add(ptr, 4) // Advance 4 bytes.
                    if iszero(lt(ptr, end)) { break }
                }
                mstore(dataEnd, dataEndValue) // Restore the cached value at `dataEnd`.
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                // Equivalent to `o = [0, 2, 1][dataLength % 3]`.
                let o := div(2, mod(dataLength, 3))
                // Offset `ptr` and pad with '='. We can simply write over the end.
                mstore(sub(ptr, o), shl(240, 0x3d3d))
                // Set `o` to zero if there is padding.
                o := mul(iszero(iszero(noPadding)), o)
                mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
                mstore(result, sub(encodedLength, o)) // Store the length.
            }
        }
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, false, false)`.
    function encode(bytes memory data) internal pure returns (string memory result) {
        result = encode(data, false, false);
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, fileSafe, false)`.
    function encode(bytes memory data, bool fileSafe)
        internal
        pure
        returns (string memory result)
    {
        result = encode(data, fileSafe, false);
    }

    /// @dev Decodes base64 encoded `data`.
    ///
    /// Supports:
    /// - RFC 4648 (both standard and file-safe mode).
    /// - RFC 3501 (63: ',').
    ///
    /// Does not support:
    /// - Line breaks.
    ///
    /// Note: For performance reasons,
    /// this function will NOT revert on invalid `data` inputs.
    /// Outputs for invalid inputs will simply be undefined behaviour.
    /// It is the user's responsibility to ensure that the `data`
    /// is a valid base64 encoded string.
    function decode(string memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                let decodedLength := mul(shr(2, dataLength), 3)

                for {} 1 {} {
                    // If padded.
                    if iszero(and(dataLength, 3)) {
                        let t := xor(mload(add(data, dataLength)), 0x3d3d)
                        // forgefmt: disable-next-item
                        decodedLength := sub(
                            decodedLength,
                            add(iszero(byte(30, t)), iszero(byte(31, t)))
                        )
                        break
                    }
                    // If non-padded.
                    decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                    break
                }
                result := mload(0x40)

                // Write the length of the bytes.
                mstore(result, decodedLength)

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, decodedLength)

                // Load the table into the scratch space.
                // Constants are optimized for smaller bytecode with zero gas overhead.
                // `m` also doubles as the mask of the upper 6 bits.
                let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                mstore(0x5b, m)
                mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)

                for {} 1 {} {
                    // Read 4 bytes.
                    data := add(data, 4)
                    let input := mload(data)

                    // Write 3 bytes.
                    // forgefmt: disable-next-item
                    mstore(ptr, or(
                        and(m, mload(byte(28, input))),
                        shr(6, or(
                            and(m, mload(byte(29, input))),
                            shr(6, or(
                                and(m, mload(byte(30, input))),
                                shr(6, mload(byte(31, input)))
                            ))
                        ))
                    ))
                    ptr := add(ptr, 3)
                    if iszero(lt(ptr, end)) { break }
                }
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                mstore(end, 0) // Zeroize the slot after the bytes.
                mstore(0x60, 0) // Restore the zero slot.
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"EditionHasMintedTokens","type":"error"},{"inputs":[],"name":"IncorrectPrice","type":"error"},{"inputs":[],"name":"MaxSupplyReached","type":"error"},{"inputs":[],"name":"NotArtist","type":"error"},{"inputs":[],"name":"NotMintable","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"PlatformNotActive","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"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":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"EDITION_COUNTER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLATFORM_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLATFORM_ROYALTY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"}],"name":"artistMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"address","name":"artGenerator","type":"address"},{"internalType":"address","name":"artistAddress","type":"address"},{"internalType":"string","name":"artistName","type":"string"}],"name":"createNewEdition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"}],"name":"deleteEdition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getDataUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"}],"name":"getEdition","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"artistName","type":"string"},{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"counter","type":"uint256"},{"internalType":"uint256","name":"royalty","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"signatureId","type":"uint256"},{"internalType":"bool","name":"mintStatus","type":"bool"},{"internalType":"contract IArtGenerator","name":"artGenerator","type":"address"},{"internalType":"address","name":"royaltyReceiver","type":"address"},{"internalType":"address","name":"artistAddress","type":"address"}],"internalType":"struct Edition","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getRawSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getSeed","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"editionId","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"editionId","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"mintTo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"modify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"}],"name":"releasePlatformRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","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":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setMintStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setPlatformFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"royalty","type":"uint256"}],"name":"setPlatformRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"},{"internalType":"uint256","name":"basisPoints","type":"uint256"}],"name":"setRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"edition","type":"uint256"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"setSignatureId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","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":"uint256","name":"tokenId","type":"uint256"}],"name":"unPackSeed","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

0x60806040526005600455600a60055534801561001a57600080fd5b50600280546001600160a01b031916331790556154068061003c6000396000f3fe6080604052600436106200027b5760003560e01c80638462151c1162000153578063bf113baf11620000c5578063d675fee71162000084578063d675fee714620007c8578063d6b8a82e14620007ed578063d728fcc51462000805578063e0d4ea37146200082a578063e985e9c5146200085b578063f7d97577146200089557600080fd5b8063bf113baf146200070f578063c4dcb8d71462000734578063c87b56dd1462000759578063c8e2e2a1146200077e578063ce271b5f14620007a357600080fd5b8063a642c0321162000112578063a642c0321462000663578063a6f9dae11462000697578063b52dc13214620006bc578063b723b34e14620006e1578063b88d4fde14620006f857600080fd5b80638462151c146200059d578063933a6f0d14620005d157806395d89b4114620005f6578063a0712d681462000627578063a22cb465146200063e57600080fd5b806334fbc9a111620001ed5780634f558e7911620001ac5780634f558e7914620004e457806355235348146200050957806361399552146200052e5780636352211e146200055357806370a08231146200057857600080fd5b806334fbc9a114620004515780633ccfd60b146200047857806342842e0e146200049057806342966c6814620004a7578063464ef00914620004cc57600080fd5b806312e8e2c3116200023a57806312e8e2c314620003865780631a6fe84014620003ab57806323b872dd14620003d05780632a55205a14620003e75780632a57f123146200042c57600080fd5b806301ffc9a7146200028857806306fdde0314620002c2578063081812fc146200030d578063095ea7b3146200034b578063117803e3146200036457600080fd5b366200028357005b600080fd5b3480156200029557600080fd5b50620002ad620002a736600462003457565b620008ba565b60405190151581526020015b60405180910390f35b348015620002cf57600080fd5b506040805180820190915260168152754f6e636861696e2d4578706572696d656e74735f563160501b60208201525b604051620002b99190620034d7565b3480156200031a57600080fd5b50620003326200032c366004620034ec565b62000904565b6040516001600160a01b039091168152602001620002b9565b620003626200035c3660046200351e565b62000944565b005b3480156200037157600080fd5b5060025462000332906001600160a01b031681565b3480156200039357600080fd5b5062000362620003a5366004620034ec565b62000955565b348015620003b857600080fd5b50620002fe620003ca366004620034ec565b62000986565b62000362620003e13660046200354b565b62000a50565b348015620003f457600080fd5b506200040c620004063660046200358c565b62000b56565b604080516001600160a01b039093168352602083019190915201620002b9565b3480156200043957600080fd5b50620003626200044b366004620034ec565b62000e03565b3480156200045e57600080fd5b506200046960045481565b604051908152602001620002b9565b3480156200048557600080fd5b506200036262000ea1565b62000362620004a13660046200354b565b62000f1b565b348015620004b457600080fd5b5062000362620004c6366004620034ec565b62000f4d565b348015620004d957600080fd5b506200046960035481565b348015620004f157600080fd5b50620002ad62000503366004620034ec565b62000f5c565b3480156200051657600080fd5b506200036262000528366004620035f4565b62000f69565b3480156200053b57600080fd5b50620002fe6200054d366004620034ec565b62001069565b3480156200056057600080fd5b506200033262000572366004620034ec565b620010de565b3480156200058557600080fd5b50620004696200059736600462003644565b6200111d565b348015620005aa57600080fd5b50620005c2620005bc36600462003644565b62001159565b604051620002b991906200369f565b348015620005de57600080fd5b5062000362620005f03660046200358c565b6200129f565b3480156200060357600080fd5b506040805180820190915260068152654f2d455f563160d01b6020820152620002fe565b6200046962000638366004620034ec565b620012f2565b3480156200064b57600080fd5b50620003626200065d366004620036c5565b6200163c565b3480156200067057600080fd5b506200068862000682366004620034ec565b62001692565b604051620002b99190620036fd565b348015620006a457600080fd5b5062000362620006b636600462003644565b620018fe565b348015620006c957600080fd5b50620002fe620006db366004620034ec565b6200194c565b62000469620006f236600462003801565b62001cc0565b620003626200070936600462003827565b62002055565b3480156200071c57600080fd5b50620004696200072e366004620034ec565b620020ae565b3480156200074157600080fd5b5062000362620007533660046200389e565b620020f1565b3480156200076657600080fd5b50620002fe62000778366004620034ec565b62002152565b3480156200078b57600080fd5b50620004696200079d36600462003995565b620024e8565b348015620007b057600080fd5b5062000362620007c2366004620034ec565b62002796565b348015620007d557600080fd5b5062000362620007e7366004620034ec565b620027c7565b348015620007fa57600080fd5b506200046960055481565b3480156200081257600080fd5b5062000362620008243660046200358c565b620028cb565b3480156200083757600080fd5b506200046962000849366004620034ec565b60009081526001602052604090205490565b3480156200086857600080fd5b50620002ad6200087a36600462003a55565b601c52670a5a2e7a000000006008526000526030600c205490565b348015620008a257600080fd5b5062000362620008b43660046200358c565b6200291e565b60006001600160e01b0319821663152a902d60e11b1480620008fe5750620008fe826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b92915050565b600081600052673ec412a9852d173d60c11b601c52602060002082018201805460601b6200093a5763ceea21b66000526004601cfd5b6001015492915050565b6200095133838362002971565b5050565b6002546001600160a01b0316331462000981576040516330cd747160e01b815260040160405180910390fd5b600455565b60606000806200099a620f42408562003ab0565b8152602080820192909252604090810160009081206008015485825260018452908290205482519384015261010090046001600160a01b03169163a4b3dda691016040516020818303038152906040526040518263ffffffff1660e01b815260040162000a089190620034d7565b600060405180830381865afa15801562000a26573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620008fe919081019062003afd565b6000818152673ec412a9852d173d60c11b3317601c52602090208101810180546001600160a01b03948516949384169381169190828614830262000aa35767ceea21b6a1148100831560021b526004601cfd5b85600052816001015492508233148633141762000ad3576030600c205462000ad357634b6e7f186000526004601cfd5b821562000ae257600082600101555b85851818905550601c600c8181208054600019019055600084905220805460010163ffffffff8116840262000b265767ea553b3401336cea841560021b526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a45b505050565b60008080808062000b6b620f42408862003ab0565b81526020019081526020016000206040518061018001604052908160008201805462000b979062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462000bc59062003b4b565b801562000c165780601f1062000bea5761010080835404028352916020019162000c16565b820191906000526020600020905b81548152906001019060200180831162000bf857829003601f168201915b5050505050815260200160018201805462000c319062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462000c5f9062003b4b565b801562000cb05780601f1062000c845761010080835404028352916020019162000cb0565b820191906000526020600020905b81548152906001019060200180831162000c9257829003601f168201915b5050505050815260200160028201805462000ccb9062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462000cf99062003b4b565b801562000d4a5780601f1062000d1e5761010080835404028352916020019162000d4a565b820191906000526020600020905b81548152906001019060200180831162000d2c57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a080830191909152600883015460ff8116151560c08401526001600160a01b0361010091829004811660e08501526009850154811691840191909152600a909301549092166101209091015261014082015190820151919250906127109062000dea908762003b81565b62000df6919062003ab0565b92509250505b9250929050565b6002546001600160a01b0316331462000e2f576040516330cd747160e01b815260040160405180910390fd5b60008181526020819052604090819020600901549051631916558760e01b81523060048201526001600160a01b0390911690631916558790602401600060405180830381600087803b15801562000e8557600080fd5b505af115801562000e9a573d6000803e3d6000fd5b5050505050565b6002546001600160a01b0316331462000ecd576040516330cd747160e01b815260040160405180910390fd5b6002546040516001600160a01b03909116904790600081818185875af1925050503d806000811462000b51576040519150601f19603f3d011682016040523d82523d6000602084013e505050565b62000f2883838362000a50565b813b1562000b515762000b518383836040518060200160405280600081525062002a15565b62000f59338262002aa5565b50565b6000620008fe8262002b79565b62000f7483620010de565b6001600160a01b0316336001600160a01b03161462000fa6576040516330cd747160e01b815260040160405180910390fd5b60008062000fb8620f42408662003ab0565b815260200190815260200160002060080160019054906101000a90046001600160a01b03166001600160a01b031663552353488484846040518463ffffffff1660e01b81526004016200100e9392919062003b9b565b602060405180830381865afa1580156200102c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001052919062003bd1565b600093845260016020526040909320929092555050565b604051635a96e09960e11b815260048101829052606090620008fe90309063b52dc13290602401600060405180830381865afa158015620010ae573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620010d8919081019062003c0e565b62002b9d565b6000818152673ec412a9852d173d60c11b601c526020902081018101546001600160a01b031680620011185763ceea21b66000526004601cfd5b919050565b6000816200113357638f4eb6046000526004601cfd5b673ec412a9852d173d60c11b601c528160005263ffffffff601c600c2054169050919050565b6060600062001168836200111d565b905060008167ffffffffffffffff811115620011885762001188620038c4565b604051908082528060200260200182016040528015620011b2578160200160208202803683370190505b509050600060015b6003548111620012955760015b60008281526020819052604090206004015481116200127f57600081620011f284620f424062003b81565b620011fe919062003c47565b90506200120b8162002b79565b8015620012335750876001600160a01b03166200122882620010de565b6001600160a01b0316145b156200126957808585620012478162003c5d565b9650815181106200125c576200125c62003c79565b6020026020010181815250505b5080620012768162003c5d565b915050620011c7565b50806200128c8162003c5d565b915050620011ba565b5090949350505050565b6000828152602081905260409020600a015482906001600160a01b03163314620012dc57604051636bebaa5360e01b815260040160405180910390fd5b5060009182526020829052604090912060050155565b60008060008084815260200190815260200160002060405180610180016040529081600082018054620013259062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620013539062003b4b565b8015620013a45780601f106200137857610100808354040283529160200191620013a4565b820191906000526020600020905b8154815290600101906020018083116200138657829003601f168201915b50505050508152602001600182018054620013bf9062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620013ed9062003b4b565b80156200143e5780601f1062001412576101008083540402835291602001916200143e565b820191906000526020600020905b8154815290600101906020018083116200142057829003601f168201915b50505050508152602001600282018054620014599062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620014879062003b4b565b8015620014d85780601f10620014ac57610100808354040283529160200191620014d8565b820191906000526020600020905b815481529060010190602001808311620014ba57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811682840152600a90930154909216610120909101528101519091506200157757604051630952c8a960e11b815260040160405180910390fd5b8060c0015134146200159c576040516399b5cb1d60e01b815260040160405180910390fd5b341562001629576000606460045434620015b7919062003b81565b620015c3919062003ab0565b6101608301519091506001600160a01b0316620015e1823462003c8f565b604051600081818185875af1925050503d80600081146200161f576040519150601f19603f3d011682016040523d82523d6000602084013e62001624565b606091505b505050505b62001635833362002bd2565b9392505050565b801515905081601c52670a5a2e7a0000000060085233600052806030600c2055806000528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206000a35050565b6200169c62003377565b6000828152602081905260409081902081516101808101909252805482908290620016c79062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620016f59062003b4b565b8015620017465780601f106200171a5761010080835404028352916020019162001746565b820191906000526020600020905b8154815290600101906020018083116200172857829003601f168201915b50505050508152602001600182018054620017619062003b4b565b80601f01602080910402602001604051908101604052809291908181526020018280546200178f9062003b4b565b8015620017e05780601f10620017b457610100808354040283529160200191620017e0565b820191906000526020600020905b815481529060010190602001808311620017c257829003601f168201915b50505050508152602001600282018054620017fb9062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620018299062003b4b565b80156200187a5780601f106200184e576101008083540402835291602001916200187a565b820191906000526020600020905b8154815290600101906020018083116200185c57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811691830191909152600a909201549091166101209091015292915050565b6002546001600160a01b031633146200192a576040516330cd747160e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6060620019598262002b79565b620019775760405163677510db60e11b815260040160405180910390fd5b600080806200198a620f42408662003ab0565b815260200190815260200160002060405180610180016040529081600082018054620019b69062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620019e49062003b4b565b801562001a355780601f1062001a095761010080835404028352916020019162001a35565b820191906000526020600020905b81548152906001019060200180831162001a1757829003601f168201915b5050505050815260200160018201805462001a509062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001a7e9062003b4b565b801562001acf5780601f1062001aa35761010080835404028352916020019162001acf565b820191906000526020600020905b81548152906001019060200180831162001ab157829003601f168201915b5050505050815260200160028201805462001aea9062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001b189062003b4b565b801562001b695780601f1062001b3d5761010080835404028352916020019162001b69565b820191906000526020600020905b81548152906001019060200180831162001b4b57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811691830191909152600a9092015490911661012090910152905060008062001bf8838662002cde565b610120850151600088815260016020526040808220549051631127441160e01b8152949650929450926001600160a01b039091169163112744119162001c45919060040190815260200190565b600060405180830381865afa15801562001c63573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262001c8d919081019062003c0e565b905081818460405160200162001ca69392919062003cc3565b604051602081830303815290604052945050505050919050565b6000806000808581526020019081526020016000206040518061018001604052908160008201805462001cf39062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001d219062003b4b565b801562001d725780601f1062001d465761010080835404028352916020019162001d72565b820191906000526020600020905b81548152906001019060200180831162001d5457829003601f168201915b5050505050815260200160018201805462001d8d9062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001dbb9062003b4b565b801562001e0c5780601f1062001de05761010080835404028352916020019162001e0c565b820191906000526020600020905b81548152906001019060200180831162001dee57829003601f168201915b5050505050815260200160028201805462001e279062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001e559062003b4b565b801562001ea65780601f1062001e7a5761010080835404028352916020019162001ea6565b820191906000526020600020905b81548152906001019060200180831162001e8857829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811682840152600a909301549092166101209091015281015190915062001f4557604051630952c8a960e11b815260040160405180910390fd5b8060c00151341462001f6a576040516399b5cb1d60e01b815260040160405180910390fd5b34156200204157600060646004543462001f85919062003b81565b62001f91919062003ab0565b60405190915030908290600081818185875af1925050503d806000811462001fd6576040519150601f19603f3d011682016040523d82523d6000602084013e62001fdb565b606091505b5050506101608201516001600160a01b031662001ff9823462003c8f565b604051600081818185875af1925050503d806000811462002037576040519150601f19603f3d011682016040523d82523d6000602084013e6200203c565b606091505b505050505b6200204d848462002bd2565b949350505050565b6200206285858562000a50565b833b1562000e9a5762000e9a85858585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062002a1592505050565b6000818152602081905260408120600a015482906001600160a01b031633146200162957604051636bebaa5360e01b815260040160405180910390fd5b50919050565b6000828152602081905260409020600a015482906001600160a01b031633146200212e57604051636bebaa5360e01b815260040160405180910390fd5b50600091825260208290526040909120600801805460ff1916911515919091179055565b60606200215f8262002b79565b6200217d5760405163677510db60e11b815260040160405180910390fd5b6000808062002190620f42408662003ab0565b815260200190815260200160002060405180610180016040529081600082018054620021bc9062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620021ea9062003b4b565b80156200223b5780601f106200220f576101008083540402835291602001916200223b565b820191906000526020600020905b8154815290600101906020018083116200221d57829003601f168201915b50505050508152602001600182018054620022569062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620022849062003b4b565b8015620022d55780601f10620022a957610100808354040283529160200191620022d5565b820191906000526020600020905b815481529060010190602001808311620022b757829003601f168201915b50505050508152602001600282018054620022f09062003b4b565b80601f01602080910402602001604051908101604052809291908181526020018280546200231e9062003b4b565b80156200236f5780601f1062002343576101008083540402835291602001916200236f565b820191906000526020600020905b8154815290600101906020018083116200235157829003601f168201915b505050918352505060038201546020808301919091526004808401546040808501919091526005850154606085015260068501546080850152600785015460a0850152600885015460ff8116151560c08601526001600160a01b0361010091829004811660e08701526009870154811691860191909152600a909501548516610120948501529285015160008981526001909352838320549351634055ddb560e11b81529596509194859492909216926380abbb6a9262002434920190815260200190565b600060405180830381865afa15801562002452573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200247c919081019062003d9a565b915091506000806200248f858862002cde565b91509150808483604051602001620024aa9392919062003cc3565b6040516020818303038152906040529350620024dd856000015186604001518761016001518860200151888c8962002fce565b979650505050505050565b6002546000906001600160a01b0316331462002517576040516330cd747160e01b815260040160405180910390fd5b60408051600280825260608201835260009260208301908036833701905050905083816000815181106200254f576200254f62003c79565b60200260200101906001600160a01b031690816001600160a01b031681525050308160018151811062002586576200258662003c79565b6001600160a01b039290921660209283029190910182015260408051600280825260608201835260009391929091830190803683370190505090506005546064620025d2919062003c8f565b81600081518110620025e857620025e862003c79565b602002602001018181525050600554816001815181106200260d576200260d62003c79565b602002602001018181525050600082826040516200262b90620033f5565b6200263892919062003e05565b604051809103906000f08015801562002655573d6000803e3d6000fd5b506003805491925060006200266a8362003c5d565b9190505550620026808a8a8a8a8a8a8762003069565b600354600090815260208190526040902081518190620026a1908262003eb3565b5060208201516001820190620026b8908262003eb3565b5060408201516002820190620026cf908262003eb3565b5060608201516003808301919091556080830151600483015560a0830151600583015560c0830151600683015560e08301516007830155610100808401516008840180546101208701516001600160a01b03908116909402610100600160a81b0319931515939093166001600160a81b0319909116179190911790556101408401516009840180549183166001600160a01b031992831617905561016090940151600a90930180549390911692909316919091179091555493505050509695505050505050565b6002546001600160a01b03163314620027c2576040516330cd747160e01b815260040160405180910390fd5b600555565b6000818152602081905260409020600a015481906001600160a01b031633146200280457604051636bebaa5360e01b815260040160405180910390fd5b600082815260208190526040902060040154156200283557604051632a90f20760e01b815260040160405180910390fd5b60008281526020819052604081209062002850828262003403565b6200286060018301600062003403565b6200287060028301600062003403565b5060006003820181905560048201819055600582018190556006820181905560078201556008810180546001600160a81b03191690556009810180546001600160a01b0319908116909155600a909101805490911690555050565b6000828152602081905260409020600a015482906001600160a01b031633146200290857604051636bebaa5360e01b815260040160405180910390fd5b5060009182526020829052604090912060070155565b6000828152602081905260409020600a015482906001600160a01b031633146200295b57604051636bebaa5360e01b815260040160405180910390fd5b5060009182526020829052604090912060060155565b60001960601c828116925083811693508160005283673ec412a9852d173d60c11b17601c5260206000208201820180548216915081620029b95763ceea21b66000526004601cfd5b818514851517620029e157816000526030600c2054620029e157634b6e7f186000526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600038a450505050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a0840152801562002a5d578060c08401826020870160045afa505b60208360a48301601c860160008a5af162002a82573d1562002a82573d6000843e3d83fd5b508060e01b82511462002a9d5763d1a57ed66000526004601cfd5b505050505050565b600062002ab282620010de565b90505060008181526001600160a01b03928316673ec412a9852d173d60c11b8117601c52602090912082018201805491938216918262002afa5763ceea21b66000526004601cfd5b8260005281600101548086148487141786151762002b2b576030600c205462002b2b57634b6e7f186000526004601cfd5b801562002b3a57600083600101555b5082189055601c600c208054600019019055816000827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8238a4505050565b6000818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b606062002baa82620030b2565b60405160200162002bbc919062003f80565b6040516020818303038152906040529050919050565b60008281526020819052604081206003810154600482018054849062002bf89062003c5d565b9182905550111562002c1d5760405163d05cb60960e01b815260040160405180910390fd5b600481015460009062002c3486620f424062003b81565b62002c40919062003c47565b60088301546040516315874d3d60e31b8152600481018390526001600160a01b0387811660248301529293506101009091049091169063ac3a69e890604401602060405180830381865afa15801562002c9d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002cc3919062003bd1565b6000828152600160205260409020556200204d8482620030c2565b606080620f42408460e0015162002cf6919062003ab0565b600114801562002d10575062002d108460e0015162002b79565b801562002d4157508361016001516001600160a01b031662002d368560e00151620010de565b6001600160a01b0316145b1562002f605761012084015160008481526001602052604090819020549051634d5f8a7b60e11b81526001600160a01b0390921691639abf14f69162002d8d9160040190815260200190565b6040805180830381865afa92505050801562002dc8575060408051601f3d908101601f1916820190925262002dc59181019062003fc7565b60015b62002e99576001600081815260208181527fada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e855460e0880151835292905260409081902054905163992b9d2760e01b81526004810191909152610384602482015261039d60448201526101009091046001600160a01b03169063992b9d2790606401600060405180830381865afa15801562002e67573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262002e91919081019062003c0e565b915062002f60565b6001600081815260208181527fada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e855460e08a0151835292905260409081902054905163992b9d2760e01b8152600481019190915260248101849052604481018390526101009091046001600160a01b03169063992b9d2790606401600060405180830381865afa15801562002f31573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262002f5b919081019062003c0e565b935050505b835162002f7b62002f75620f42408662003fec565b620030de565b6040860151511562002f9257856040015162002fa2565b62002fa286610160015162003123565b60405160200162002fb69392919062004003565b60405160208183030381529060405290509250929050565b60606200303b8862002fe862002f75620f42408762003fec565b8762002ff488620030b2565b8b511562003003578b6200300e565b6200300e8b62003123565b876040516020016200302696959493929190620040a4565b604051602081830303815290604052620030b2565b6040516020016200304d9190620041f0565b6040516020818303038152906040529050979650505050505050565b6200307362003377565b968752602087019590955260608601939093526001600160a01b0391821661012086015292811661014085015291909116610160830152604082015290565b6060620008fe8260008062003149565b6200095182826040518060200160405280600081525062003241565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480620030f9575050819003601f19909101908152919050565b6060620031308262003264565b8051613078825260020160011990910190815292915050565b60608351801562003239576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106708515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f52602083018181018388602001018051600082525b60038a0199508951603f8160121c1651600053603f81600c1c1651600153603f8160061c1651600253603f811651600353506000518452600484019350828410620031c6579052602001604052613d3d60f01b600384066002048083039190915260008615159091029182900352900382525b509392505050565b6200324d8383620032d4565b823b1562000b515762000b51600084848462002a15565b60606040519050608081016040526f30313233343536373839616263646566600f526002810190506028815260208101600060288201528260601b925060005b808101820184821a600f81165160018301538060041c5182535050600181019060121901620032a4575050919050565b8160601b60601c915080600052673ec412a9852d173d60c11b601c5260206000208101810180548060601b15620033135763c991cbb16000526004601cfd5b831790556000829052601c600c20805460010163ffffffff81168402620033495767ea553b3401336cea841560021b526004601cfd5b9055808260007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a45050565b604051806101800160405280606081526020016060815260200160608152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b611199806200423883390190565b508054620034119062003b4b565b6000825580601f1062003422575050565b601f01602090049060005260206000209081019062000f5991905b808211156200345357600081556001016200343d565b5090565b6000602082840312156200346a57600080fd5b81356001600160e01b0319811681146200163557600080fd5b60005b83811015620034a057818101518382015260200162003486565b50506000910152565b60008151808452620034c381602086016020860162003483565b601f01601f19169290920160200192915050565b602081526000620016356020830184620034a9565b600060208284031215620034ff57600080fd5b5035919050565b80356001600160a01b03811681146200111857600080fd5b600080604083850312156200353257600080fd5b6200353d8362003506565b946020939093013593505050565b6000806000606084860312156200356157600080fd5b6200356c8462003506565b92506200357c6020850162003506565b9150604084013590509250925092565b60008060408385031215620035a057600080fd5b50508035926020909101359150565b60008083601f840112620035c257600080fd5b50813567ffffffffffffffff811115620035db57600080fd5b60208301915083602082850101111562000dfc57600080fd5b6000806000604084860312156200360a57600080fd5b83359250602084013567ffffffffffffffff8111156200362957600080fd5b6200363786828701620035af565b9497909650939450505050565b6000602082840312156200365757600080fd5b620016358262003506565b600081518084526020808501945080840160005b83811015620036945781518752958201959082019060010162003676565b509495945050505050565b60208152600062001635602083018462003662565b803580151581146200111857600080fd5b60008060408385031215620036d957600080fd5b620036e48362003506565b9150620036f460208401620036b4565b90509250929050565b60208152600082516101808060208501526200371e6101a0850183620034a9565b91506020850151601f19808685030160408701526200373e8483620034a9565b93506040870151915080868503016060870152506200375e8382620034a9565b92505060608501516080850152608085015160a085015260a085015160c085015260c085015160e085015260e0850151610100818187015280870151915050610120620037ae8187018315159052565b8601519050610140620037cb868201836001600160a01b03169052565b8601519050610160620037e8868201836001600160a01b03169052565b8601516001600160a01b03811683870152905062001295565b600080604083850312156200381557600080fd5b82359150620036f46020840162003506565b6000806000806000608086880312156200384057600080fd5b6200384b8662003506565b94506200385b6020870162003506565b935060408601359250606086013567ffffffffffffffff8111156200387f57600080fd5b6200388d88828901620035af565b969995985093965092949392505050565b60008060408385031215620038b257600080fd5b82359150620036f460208401620036b4565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715620039065762003906620038c4565b604052919050565b600067ffffffffffffffff8211156200392b576200392b620038c4565b50601f01601f191660200190565b600082601f8301126200394b57600080fd5b8135620039626200395c826200390e565b620038da565b8181528460208386010111156200397857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c08789031215620039af57600080fd5b863567ffffffffffffffff80821115620039c857600080fd5b620039d68a838b0162003939565b97506020890135915080821115620039ed57600080fd5b620039fb8a838b0162003939565b96506040890135955062003a1260608a0162003506565b945062003a2260808a0162003506565b935060a089013591508082111562003a3957600080fd5b5062003a4889828a0162003939565b9150509295509295509295565b6000806040838503121562003a6957600080fd5b62003a748362003506565b9150620036f46020840162003506565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008262003ac25762003ac262003a84565b500490565b600062003ad86200395c846200390e565b905082815283838301111562003aed57600080fd5b6200163583602083018462003483565b60006020828403121562003b1057600080fd5b815167ffffffffffffffff81111562003b2857600080fd5b8201601f8101841362003b3a57600080fd5b6200204d8482516020840162003ac7565b600181811c9082168062003b6057607f821691505b602082108103620020eb57634e487b7160e01b600052602260045260246000fd5b8082028115828204841417620008fe57620008fe62003a9a565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b60006020828403121562003be457600080fd5b5051919050565b600082601f83011262003bfd57600080fd5b620016358383516020850162003ac7565b60006020828403121562003c2157600080fd5b815167ffffffffffffffff81111562003c3957600080fd5b6200204d8482850162003beb565b80820180821115620008fe57620008fe62003a9a565b60006001820162003c725762003c7262003a9a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b81810381811115620008fe57620008fe62003a9a565b6000815162003cb981856020860162003483565b9290920192915050565b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222076696577426f783d2230203020313030302031303030222060208201527f6865696768743d2231303025222077696474683d2231303025223e000000000060408201526000845162003d4981605b85016020890162003483565b84519083019062003d6281605b84016020890162003483565b845191019062003d7a81605b84016020880162003483565b651e17b9bb339f60d11b605b929091019182015260610195945050505050565b6000806040838503121562003dae57600080fd5b825167ffffffffffffffff8082111562003dc757600080fd5b62003dd58683870162003beb565b9350602085015191508082111562003dec57600080fd5b5062003dfb8582860162003beb565b9150509250929050565b604080825283519082018190526000906020906060840190828701845b8281101562003e495781516001600160a01b03168452928401929084019060010162003e22565b5050508381038285015262003e5f818662003662565b9695505050505050565b601f82111562000b5157600081815260208120601f850160051c8101602086101562003e925750805b601f850160051c820191505b8181101562002a9d5782815560010162003e9e565b815167ffffffffffffffff81111562003ed05762003ed0620038c4565b62003ee88162003ee1845462003b4b565b8462003e69565b602080601f83116001811462003f20576000841562003f075750858301515b600019600386901b1c1916600185901b17855562002a9d565b600085815260208120601f198616915b8281101562003f515788860151825594840194600190910190840162003f30565b508582101562003f705787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c00000000000081526000825162003fba81601a85016020870162003483565b91909101601a0192915050565b6000806040838503121562003fdb57600080fd5b505080516020909101519092909150565b60008262003ffe5762003ffe62003a84565b500690565b661e3a34ba36329f60c91b8152600084516200402781600785016020890162003483565b61202360f01b60079184019182015284516200404b81600984016020890162003483565b7401e17ba34ba36329f1e3232b9b19f30b93a10313c9605d1b6009929091019182015283516200408381601e84016020880162003483565b661e17b232b9b19f60c91b601e929091019182015260250195945050505050565b693d913730b6b2911d101160b11b81528651600090620040cc81600a850160208c0162003483565b61202360f01b600a918401918201528751620040f081600c840160208c0162003483565b72111610113232b9b1b934b83a34b7b7111d101160691b600c929091019182015286516200412681601f840160208b0162003483565b7f222c2022696d616765223a2022646174613a696d6167652f7376672b786d6c3b601f92909101918201526618985cd94d8d0b60ca1b603f820152855162004176816046840160208a0162003483565b620041e2620041d5620041ce620041b1620041aa6046868801016d1116101130b93a34b9ba111d101160911b8152600e0190565b8a62003ca5565b7001116101130ba3a3934b13aba32b9911d1607d1b815260110190565b8762003ca5565b607d60f81b815260010190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516200422a81601d85016020870162003483565b91909101601d019291505056fe6080604052604051620011993803806200119983398101604081905262000026916200042e565b8051825114620000985760405162461bcd60e51b815260206004820152603260248201527f5061796d656e7453706c69747465723a2070617965657320616e6420736861726044820152710cae640d8cadccee8d040dad2e6dac2e8c6d60731b60648201526084015b60405180910390fd5b6000825111620000eb5760405162461bcd60e51b815260206004820152601a60248201527f5061796d656e7453706c69747465723a206e6f2070617965657300000000000060448201526064016200008f565b60005b82518110156200015757620001428382815181106200011157620001116200050c565b60200260200101518383815181106200012e576200012e6200050c565b60200260200101516200016060201b60201c565b806200014e8162000538565b915050620000ee565b50505062000570565b6001600160a01b038216620001cd5760405162461bcd60e51b815260206004820152602c60248201527f5061796d656e7453706c69747465723a206163636f756e74206973207468652060448201526b7a65726f206164647265737360a01b60648201526084016200008f565b600081116200021f5760405162461bcd60e51b815260206004820152601d60248201527f5061796d656e7453706c69747465723a2073686172657320617265203000000060448201526064016200008f565b6001600160a01b038216600090815260026020526040902054156200029b5760405162461bcd60e51b815260206004820152602b60248201527f5061796d656e7453706c69747465723a206163636f756e7420616c726561647960448201526a206861732073686172657360a81b60648201526084016200008f565b60048054600181019091557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0319166001600160a01b0384169081179091556000908152600260205260408120829055546200030390829062000554565b600055604080516001600160a01b0384168152602081018390527f40c340f65e17194d14ddddb073d3c9f888e3cb52b5aae0c6c7706b4fbc905fac910160405180910390a15050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156200038d576200038d6200034c565b604052919050565b60006001600160401b03821115620003b157620003b16200034c565b5060051b60200190565b600082601f830112620003cd57600080fd5b81516020620003e6620003e08362000395565b62000362565b82815260059290921b840181019181810190868411156200040657600080fd5b8286015b848110156200042357805183529183019183016200040a565b509695505050505050565b600080604083850312156200044257600080fd5b82516001600160401b03808211156200045a57600080fd5b818501915085601f8301126200046f57600080fd5b8151602062000482620003e08362000395565b82815260059290921b84018101918181019089841115620004a257600080fd5b948201945b83861015620004d95785516001600160a01b0381168114620004c95760008081fd5b82529482019490820190620004a7565b91880151919650909350505080821115620004f357600080fd5b506200050285828601620003bb565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016200054d576200054d62000522565b5060010190565b808201808211156200056a576200056a62000522565b92915050565b610c1980620005806000396000f3fe6080604052600436106100a05760003560e01c80639852595c116100645780639852595c146101ac578063a3f8eace146101e2578063c45ac05014610202578063ce7c2ac214610222578063d79779b214610258578063e33b7de31461028e57600080fd5b806319165587146100ee5780633a98ef3914610110578063406072a91461013457806348b75044146101545780638b83209b1461017457600080fd5b366100e9577f6ef95f06320e7a25a04a175ca677b7052bdd97131872c2192525a629f51be77033604080516001600160a01b0390921682523460208301520160405180910390a1005b600080fd5b3480156100fa57600080fd5b5061010e6101093660046109aa565b6102a3565b005b34801561011c57600080fd5b506000545b6040519081526020015b60405180910390f35b34801561014057600080fd5b5061012161014f3660046109c7565b610393565b34801561016057600080fd5b5061010e61016f3660046109c7565b6103c0565b34801561018057600080fd5b5061019461018f366004610a00565b6104d1565b6040516001600160a01b03909116815260200161012b565b3480156101b857600080fd5b506101216101c73660046109aa565b6001600160a01b031660009081526003602052604090205490565b3480156101ee57600080fd5b506101216101fd3660046109aa565b610501565b34801561020e57600080fd5b5061012161021d3660046109c7565b610549565b34801561022e57600080fd5b5061012161023d3660046109aa565b6001600160a01b031660009081526002602052604090205490565b34801561026457600080fd5b506101216102733660046109aa565b6001600160a01b031660009081526005602052604090205490565b34801561029a57600080fd5b50600154610121565b6001600160a01b0381166000908152600260205260409020546102e15760405162461bcd60e51b81526004016102d890610a19565b60405180910390fd5b60006102ec82610501565b90508060000361030e5760405162461bcd60e51b81526004016102d890610a5f565b80600160008282546103209190610ac0565b90915550506001600160a01b038216600090815260036020526040902080548201905561034d82826105ef565b604080516001600160a01b0384168152602081018390527fdf20fd1e76bc69d672e4814fafb2c449bba3a5369d8359adf9e05e6fde87b056910160405180910390a15050565b6001600160a01b038083166000908152600660209081526040808320938516835292905220545b92915050565b6001600160a01b0381166000908152600260205260409020546103f55760405162461bcd60e51b81526004016102d890610a19565b60006104018383610549565b9050806000036104235760405162461bcd60e51b81526004016102d890610a5f565b6001600160a01b0383166000908152600560205260408120805483929061044b908490610ac0565b90915550506001600160a01b03808416600090815260066020908152604080832093861683529290522080548201905561048683838361070d565b604080516001600160a01b038481168252602082018490528516917f3be5b7a71e84ed12875d241991c70855ac5817d847039e17a9d895c1ceb0f18a910160405180910390a2505050565b6000600482815481106104e6576104e6610ad3565b6000918252602090912001546001600160a01b031692915050565b60008061050d60015490565b6105179047610ac0565b9050610542838261053d866001600160a01b031660009081526003602052604090205490565b61075f565b9392505050565b6001600160a01b03821660009081526005602052604081205481906040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa1580156105a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cc9190610ae9565b6105d69190610ac0565b90506105e7838261053d8787610393565b949350505050565b8047101561063f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016102d8565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461068c576040519150601f19603f3d011682016040523d82523d6000602084013e610691565b606091505b50509050806107085760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016102d8565b505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261070890849061079a565b600080546001600160a01b0385168252600260205260408220548391906107869086610b02565b6107909190610b19565b6105e79190610b3b565b60006107ef826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661086f9092919063ffffffff16565b90508051600014806108105750808060200190518101906108109190610b4e565b6107085760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016102d8565b60606105e7848460008585600080866001600160a01b031685876040516108969190610b94565b60006040518083038185875af1925050503d80600081146108d3576040519150601f19603f3d011682016040523d82523d6000602084013e6108d8565b606091505b50915091506108e9878383876108f4565b979650505050505050565b6060831561096357825160000361095c576001600160a01b0385163b61095c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102d8565b50816105e7565b6105e783838151156109785781518083602001fd5b8060405162461bcd60e51b81526004016102d89190610bb0565b6001600160a01b03811681146109a757600080fd5b50565b6000602082840312156109bc57600080fd5b813561054281610992565b600080604083850312156109da57600080fd5b82356109e581610992565b915060208301356109f581610992565b809150509250929050565b600060208284031215610a1257600080fd5b5035919050565b60208082526026908201527f5061796d656e7453706c69747465723a206163636f756e7420686173206e6f2060408201526573686172657360d01b606082015260800190565b6020808252602b908201527f5061796d656e7453706c69747465723a206163636f756e74206973206e6f742060408201526a191d59481c185e5b595b9d60aa1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b808201808211156103ba576103ba610aaa565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610afb57600080fd5b5051919050565b80820281158282048414176103ba576103ba610aaa565b600082610b3657634e487b7160e01b600052601260045260246000fd5b500490565b818103818111156103ba576103ba610aaa565b600060208284031215610b6057600080fd5b8151801515811461054257600080fd5b60005b83811015610b8b578181015183820152602001610b73565b50506000910152565b60008251610ba6818460208701610b70565b9190910192915050565b6020815260008251806020840152610bcf816040850160208701610b70565b601f01601f1916919091016040019291505056fea264697066735822122003a40507ebe9275ce48fd35c4861059c83b8c31f661853a5d502cfaa2d14204364736f6c63430008140033a2646970667358221220c16d722be6fc3a67ea66203a78dba7d9fe2929fb2e924077f6ccb18b192bfd8564736f6c63430008140033

Deployed Bytecode

0x6080604052600436106200027b5760003560e01c80638462151c1162000153578063bf113baf11620000c5578063d675fee71162000084578063d675fee714620007c8578063d6b8a82e14620007ed578063d728fcc51462000805578063e0d4ea37146200082a578063e985e9c5146200085b578063f7d97577146200089557600080fd5b8063bf113baf146200070f578063c4dcb8d71462000734578063c87b56dd1462000759578063c8e2e2a1146200077e578063ce271b5f14620007a357600080fd5b8063a642c0321162000112578063a642c0321462000663578063a6f9dae11462000697578063b52dc13214620006bc578063b723b34e14620006e1578063b88d4fde14620006f857600080fd5b80638462151c146200059d578063933a6f0d14620005d157806395d89b4114620005f6578063a0712d681462000627578063a22cb465146200063e57600080fd5b806334fbc9a111620001ed5780634f558e7911620001ac5780634f558e7914620004e457806355235348146200050957806361399552146200052e5780636352211e146200055357806370a08231146200057857600080fd5b806334fbc9a114620004515780633ccfd60b146200047857806342842e0e146200049057806342966c6814620004a7578063464ef00914620004cc57600080fd5b806312e8e2c3116200023a57806312e8e2c314620003865780631a6fe84014620003ab57806323b872dd14620003d05780632a55205a14620003e75780632a57f123146200042c57600080fd5b806301ffc9a7146200028857806306fdde0314620002c2578063081812fc146200030d578063095ea7b3146200034b578063117803e3146200036457600080fd5b366200028357005b600080fd5b3480156200029557600080fd5b50620002ad620002a736600462003457565b620008ba565b60405190151581526020015b60405180910390f35b348015620002cf57600080fd5b506040805180820190915260168152754f6e636861696e2d4578706572696d656e74735f563160501b60208201525b604051620002b99190620034d7565b3480156200031a57600080fd5b50620003326200032c366004620034ec565b62000904565b6040516001600160a01b039091168152602001620002b9565b620003626200035c3660046200351e565b62000944565b005b3480156200037157600080fd5b5060025462000332906001600160a01b031681565b3480156200039357600080fd5b5062000362620003a5366004620034ec565b62000955565b348015620003b857600080fd5b50620002fe620003ca366004620034ec565b62000986565b62000362620003e13660046200354b565b62000a50565b348015620003f457600080fd5b506200040c620004063660046200358c565b62000b56565b604080516001600160a01b039093168352602083019190915201620002b9565b3480156200043957600080fd5b50620003626200044b366004620034ec565b62000e03565b3480156200045e57600080fd5b506200046960045481565b604051908152602001620002b9565b3480156200048557600080fd5b506200036262000ea1565b62000362620004a13660046200354b565b62000f1b565b348015620004b457600080fd5b5062000362620004c6366004620034ec565b62000f4d565b348015620004d957600080fd5b506200046960035481565b348015620004f157600080fd5b50620002ad62000503366004620034ec565b62000f5c565b3480156200051657600080fd5b506200036262000528366004620035f4565b62000f69565b3480156200053b57600080fd5b50620002fe6200054d366004620034ec565b62001069565b3480156200056057600080fd5b506200033262000572366004620034ec565b620010de565b3480156200058557600080fd5b50620004696200059736600462003644565b6200111d565b348015620005aa57600080fd5b50620005c2620005bc36600462003644565b62001159565b604051620002b991906200369f565b348015620005de57600080fd5b5062000362620005f03660046200358c565b6200129f565b3480156200060357600080fd5b506040805180820190915260068152654f2d455f563160d01b6020820152620002fe565b6200046962000638366004620034ec565b620012f2565b3480156200064b57600080fd5b50620003626200065d366004620036c5565b6200163c565b3480156200067057600080fd5b506200068862000682366004620034ec565b62001692565b604051620002b99190620036fd565b348015620006a457600080fd5b5062000362620006b636600462003644565b620018fe565b348015620006c957600080fd5b50620002fe620006db366004620034ec565b6200194c565b62000469620006f236600462003801565b62001cc0565b620003626200070936600462003827565b62002055565b3480156200071c57600080fd5b50620004696200072e366004620034ec565b620020ae565b3480156200074157600080fd5b5062000362620007533660046200389e565b620020f1565b3480156200076657600080fd5b50620002fe62000778366004620034ec565b62002152565b3480156200078b57600080fd5b50620004696200079d36600462003995565b620024e8565b348015620007b057600080fd5b5062000362620007c2366004620034ec565b62002796565b348015620007d557600080fd5b5062000362620007e7366004620034ec565b620027c7565b348015620007fa57600080fd5b506200046960055481565b3480156200081257600080fd5b5062000362620008243660046200358c565b620028cb565b3480156200083757600080fd5b506200046962000849366004620034ec565b60009081526001602052604090205490565b3480156200086857600080fd5b50620002ad6200087a36600462003a55565b601c52670a5a2e7a000000006008526000526030600c205490565b348015620008a257600080fd5b5062000362620008b43660046200358c565b6200291e565b60006001600160e01b0319821663152a902d60e11b1480620008fe5750620008fe826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b92915050565b600081600052673ec412a9852d173d60c11b601c52602060002082018201805460601b6200093a5763ceea21b66000526004601cfd5b6001015492915050565b6200095133838362002971565b5050565b6002546001600160a01b0316331462000981576040516330cd747160e01b815260040160405180910390fd5b600455565b60606000806200099a620f42408562003ab0565b8152602080820192909252604090810160009081206008015485825260018452908290205482519384015261010090046001600160a01b03169163a4b3dda691016040516020818303038152906040526040518263ffffffff1660e01b815260040162000a089190620034d7565b600060405180830381865afa15801562000a26573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620008fe919081019062003afd565b6000818152673ec412a9852d173d60c11b3317601c52602090208101810180546001600160a01b03948516949384169381169190828614830262000aa35767ceea21b6a1148100831560021b526004601cfd5b85600052816001015492508233148633141762000ad3576030600c205462000ad357634b6e7f186000526004601cfd5b821562000ae257600082600101555b85851818905550601c600c8181208054600019019055600084905220805460010163ffffffff8116840262000b265767ea553b3401336cea841560021b526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600038a45b505050565b60008080808062000b6b620f42408862003ab0565b81526020019081526020016000206040518061018001604052908160008201805462000b979062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462000bc59062003b4b565b801562000c165780601f1062000bea5761010080835404028352916020019162000c16565b820191906000526020600020905b81548152906001019060200180831162000bf857829003601f168201915b5050505050815260200160018201805462000c319062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462000c5f9062003b4b565b801562000cb05780601f1062000c845761010080835404028352916020019162000cb0565b820191906000526020600020905b81548152906001019060200180831162000c9257829003601f168201915b5050505050815260200160028201805462000ccb9062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462000cf99062003b4b565b801562000d4a5780601f1062000d1e5761010080835404028352916020019162000d4a565b820191906000526020600020905b81548152906001019060200180831162000d2c57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a080830191909152600883015460ff8116151560c08401526001600160a01b0361010091829004811660e08501526009850154811691840191909152600a909301549092166101209091015261014082015190820151919250906127109062000dea908762003b81565b62000df6919062003ab0565b92509250505b9250929050565b6002546001600160a01b0316331462000e2f576040516330cd747160e01b815260040160405180910390fd5b60008181526020819052604090819020600901549051631916558760e01b81523060048201526001600160a01b0390911690631916558790602401600060405180830381600087803b15801562000e8557600080fd5b505af115801562000e9a573d6000803e3d6000fd5b5050505050565b6002546001600160a01b0316331462000ecd576040516330cd747160e01b815260040160405180910390fd5b6002546040516001600160a01b03909116904790600081818185875af1925050503d806000811462000b51576040519150601f19603f3d011682016040523d82523d6000602084013e505050565b62000f2883838362000a50565b813b1562000b515762000b518383836040518060200160405280600081525062002a15565b62000f59338262002aa5565b50565b6000620008fe8262002b79565b62000f7483620010de565b6001600160a01b0316336001600160a01b03161462000fa6576040516330cd747160e01b815260040160405180910390fd5b60008062000fb8620f42408662003ab0565b815260200190815260200160002060080160019054906101000a90046001600160a01b03166001600160a01b031663552353488484846040518463ffffffff1660e01b81526004016200100e9392919062003b9b565b602060405180830381865afa1580156200102c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001052919062003bd1565b600093845260016020526040909320929092555050565b604051635a96e09960e11b815260048101829052606090620008fe90309063b52dc13290602401600060405180830381865afa158015620010ae573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620010d8919081019062003c0e565b62002b9d565b6000818152673ec412a9852d173d60c11b601c526020902081018101546001600160a01b031680620011185763ceea21b66000526004601cfd5b919050565b6000816200113357638f4eb6046000526004601cfd5b673ec412a9852d173d60c11b601c528160005263ffffffff601c600c2054169050919050565b6060600062001168836200111d565b905060008167ffffffffffffffff811115620011885762001188620038c4565b604051908082528060200260200182016040528015620011b2578160200160208202803683370190505b509050600060015b6003548111620012955760015b60008281526020819052604090206004015481116200127f57600081620011f284620f424062003b81565b620011fe919062003c47565b90506200120b8162002b79565b8015620012335750876001600160a01b03166200122882620010de565b6001600160a01b0316145b156200126957808585620012478162003c5d565b9650815181106200125c576200125c62003c79565b6020026020010181815250505b5080620012768162003c5d565b915050620011c7565b50806200128c8162003c5d565b915050620011ba565b5090949350505050565b6000828152602081905260409020600a015482906001600160a01b03163314620012dc57604051636bebaa5360e01b815260040160405180910390fd5b5060009182526020829052604090912060050155565b60008060008084815260200190815260200160002060405180610180016040529081600082018054620013259062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620013539062003b4b565b8015620013a45780601f106200137857610100808354040283529160200191620013a4565b820191906000526020600020905b8154815290600101906020018083116200138657829003601f168201915b50505050508152602001600182018054620013bf9062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620013ed9062003b4b565b80156200143e5780601f1062001412576101008083540402835291602001916200143e565b820191906000526020600020905b8154815290600101906020018083116200142057829003601f168201915b50505050508152602001600282018054620014599062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620014879062003b4b565b8015620014d85780601f10620014ac57610100808354040283529160200191620014d8565b820191906000526020600020905b815481529060010190602001808311620014ba57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811682840152600a90930154909216610120909101528101519091506200157757604051630952c8a960e11b815260040160405180910390fd5b8060c0015134146200159c576040516399b5cb1d60e01b815260040160405180910390fd5b341562001629576000606460045434620015b7919062003b81565b620015c3919062003ab0565b6101608301519091506001600160a01b0316620015e1823462003c8f565b604051600081818185875af1925050503d80600081146200161f576040519150601f19603f3d011682016040523d82523d6000602084013e62001624565b606091505b505050505b62001635833362002bd2565b9392505050565b801515905081601c52670a5a2e7a0000000060085233600052806030600c2055806000528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206000a35050565b6200169c62003377565b6000828152602081905260409081902081516101808101909252805482908290620016c79062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620016f59062003b4b565b8015620017465780601f106200171a5761010080835404028352916020019162001746565b820191906000526020600020905b8154815290600101906020018083116200172857829003601f168201915b50505050508152602001600182018054620017619062003b4b565b80601f01602080910402602001604051908101604052809291908181526020018280546200178f9062003b4b565b8015620017e05780601f10620017b457610100808354040283529160200191620017e0565b820191906000526020600020905b815481529060010190602001808311620017c257829003601f168201915b50505050508152602001600282018054620017fb9062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620018299062003b4b565b80156200187a5780601f106200184e576101008083540402835291602001916200187a565b820191906000526020600020905b8154815290600101906020018083116200185c57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811691830191909152600a909201549091166101209091015292915050565b6002546001600160a01b031633146200192a576040516330cd747160e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6060620019598262002b79565b620019775760405163677510db60e11b815260040160405180910390fd5b600080806200198a620f42408662003ab0565b815260200190815260200160002060405180610180016040529081600082018054620019b69062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620019e49062003b4b565b801562001a355780601f1062001a095761010080835404028352916020019162001a35565b820191906000526020600020905b81548152906001019060200180831162001a1757829003601f168201915b5050505050815260200160018201805462001a509062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001a7e9062003b4b565b801562001acf5780601f1062001aa35761010080835404028352916020019162001acf565b820191906000526020600020905b81548152906001019060200180831162001ab157829003601f168201915b5050505050815260200160028201805462001aea9062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001b189062003b4b565b801562001b695780601f1062001b3d5761010080835404028352916020019162001b69565b820191906000526020600020905b81548152906001019060200180831162001b4b57829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811691830191909152600a9092015490911661012090910152905060008062001bf8838662002cde565b610120850151600088815260016020526040808220549051631127441160e01b8152949650929450926001600160a01b039091169163112744119162001c45919060040190815260200190565b600060405180830381865afa15801562001c63573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262001c8d919081019062003c0e565b905081818460405160200162001ca69392919062003cc3565b604051602081830303815290604052945050505050919050565b6000806000808581526020019081526020016000206040518061018001604052908160008201805462001cf39062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001d219062003b4b565b801562001d725780601f1062001d465761010080835404028352916020019162001d72565b820191906000526020600020905b81548152906001019060200180831162001d5457829003601f168201915b5050505050815260200160018201805462001d8d9062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001dbb9062003b4b565b801562001e0c5780601f1062001de05761010080835404028352916020019162001e0c565b820191906000526020600020905b81548152906001019060200180831162001dee57829003601f168201915b5050505050815260200160028201805462001e279062003b4b565b80601f016020809104026020016040519081016040528092919081815260200182805462001e559062003b4b565b801562001ea65780601f1062001e7a5761010080835404028352916020019162001ea6565b820191906000526020600020905b81548152906001019060200180831162001e8857829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154606082015260068201546080820152600782015460a0820152600882015460ff8116151560c08301526001600160a01b0361010091829004811660e08401526009840154811682840152600a909301549092166101209091015281015190915062001f4557604051630952c8a960e11b815260040160405180910390fd5b8060c00151341462001f6a576040516399b5cb1d60e01b815260040160405180910390fd5b34156200204157600060646004543462001f85919062003b81565b62001f91919062003ab0565b60405190915030908290600081818185875af1925050503d806000811462001fd6576040519150601f19603f3d011682016040523d82523d6000602084013e62001fdb565b606091505b5050506101608201516001600160a01b031662001ff9823462003c8f565b604051600081818185875af1925050503d806000811462002037576040519150601f19603f3d011682016040523d82523d6000602084013e6200203c565b606091505b505050505b6200204d848462002bd2565b949350505050565b6200206285858562000a50565b833b1562000e9a5762000e9a85858585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062002a1592505050565b6000818152602081905260408120600a015482906001600160a01b031633146200162957604051636bebaa5360e01b815260040160405180910390fd5b50919050565b6000828152602081905260409020600a015482906001600160a01b031633146200212e57604051636bebaa5360e01b815260040160405180910390fd5b50600091825260208290526040909120600801805460ff1916911515919091179055565b60606200215f8262002b79565b6200217d5760405163677510db60e11b815260040160405180910390fd5b6000808062002190620f42408662003ab0565b815260200190815260200160002060405180610180016040529081600082018054620021bc9062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620021ea9062003b4b565b80156200223b5780601f106200220f576101008083540402835291602001916200223b565b820191906000526020600020905b8154815290600101906020018083116200221d57829003601f168201915b50505050508152602001600182018054620022569062003b4b565b80601f0160208091040260200160405190810160405280929190818152602001828054620022849062003b4b565b8015620022d55780601f10620022a957610100808354040283529160200191620022d5565b820191906000526020600020905b815481529060010190602001808311620022b757829003601f168201915b50505050508152602001600282018054620022f09062003b4b565b80601f01602080910402602001604051908101604052809291908181526020018280546200231e9062003b4b565b80156200236f5780601f1062002343576101008083540402835291602001916200236f565b820191906000526020600020905b8154815290600101906020018083116200235157829003601f168201915b505050918352505060038201546020808301919091526004808401546040808501919091526005850154606085015260068501546080850152600785015460a0850152600885015460ff8116151560c08601526001600160a01b0361010091829004811660e08701526009870154811691860191909152600a909501548516610120948501529285015160008981526001909352838320549351634055ddb560e11b81529596509194859492909216926380abbb6a9262002434920190815260200190565b600060405180830381865afa15801562002452573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200247c919081019062003d9a565b915091506000806200248f858862002cde565b91509150808483604051602001620024aa9392919062003cc3565b6040516020818303038152906040529350620024dd856000015186604001518761016001518860200151888c8962002fce565b979650505050505050565b6002546000906001600160a01b0316331462002517576040516330cd747160e01b815260040160405180910390fd5b60408051600280825260608201835260009260208301908036833701905050905083816000815181106200254f576200254f62003c79565b60200260200101906001600160a01b031690816001600160a01b031681525050308160018151811062002586576200258662003c79565b6001600160a01b039290921660209283029190910182015260408051600280825260608201835260009391929091830190803683370190505090506005546064620025d2919062003c8f565b81600081518110620025e857620025e862003c79565b602002602001018181525050600554816001815181106200260d576200260d62003c79565b602002602001018181525050600082826040516200262b90620033f5565b6200263892919062003e05565b604051809103906000f08015801562002655573d6000803e3d6000fd5b506003805491925060006200266a8362003c5d565b9190505550620026808a8a8a8a8a8a8762003069565b600354600090815260208190526040902081518190620026a1908262003eb3565b5060208201516001820190620026b8908262003eb3565b5060408201516002820190620026cf908262003eb3565b5060608201516003808301919091556080830151600483015560a0830151600583015560c0830151600683015560e08301516007830155610100808401516008840180546101208701516001600160a01b03908116909402610100600160a81b0319931515939093166001600160a81b0319909116179190911790556101408401516009840180549183166001600160a01b031992831617905561016090940151600a90930180549390911692909316919091179091555493505050509695505050505050565b6002546001600160a01b03163314620027c2576040516330cd747160e01b815260040160405180910390fd5b600555565b6000818152602081905260409020600a015481906001600160a01b031633146200280457604051636bebaa5360e01b815260040160405180910390fd5b600082815260208190526040902060040154156200283557604051632a90f20760e01b815260040160405180910390fd5b60008281526020819052604081209062002850828262003403565b6200286060018301600062003403565b6200287060028301600062003403565b5060006003820181905560048201819055600582018190556006820181905560078201556008810180546001600160a81b03191690556009810180546001600160a01b0319908116909155600a909101805490911690555050565b6000828152602081905260409020600a015482906001600160a01b031633146200290857604051636bebaa5360e01b815260040160405180910390fd5b5060009182526020829052604090912060070155565b6000828152602081905260409020600a015482906001600160a01b031633146200295b57604051636bebaa5360e01b815260040160405180910390fd5b5060009182526020829052604090912060060155565b60001960601c828116925083811693508160005283673ec412a9852d173d60c11b17601c5260206000208201820180548216915081620029b95763ceea21b66000526004601cfd5b818514851517620029e157816000526030600c2054620029e157634b6e7f186000526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600038a450505050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a0840152801562002a5d578060c08401826020870160045afa505b60208360a48301601c860160008a5af162002a82573d1562002a82573d6000843e3d83fd5b508060e01b82511462002a9d5763d1a57ed66000526004601cfd5b505050505050565b600062002ab282620010de565b90505060008181526001600160a01b03928316673ec412a9852d173d60c11b8117601c52602090912082018201805491938216918262002afa5763ceea21b66000526004601cfd5b8260005281600101548086148487141786151762002b2b576030600c205462002b2b57634b6e7f186000526004601cfd5b801562002b3a57600083600101555b5082189055601c600c208054600019019055816000827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8238a4505050565b6000818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b606062002baa82620030b2565b60405160200162002bbc919062003f80565b6040516020818303038152906040529050919050565b60008281526020819052604081206003810154600482018054849062002bf89062003c5d565b9182905550111562002c1d5760405163d05cb60960e01b815260040160405180910390fd5b600481015460009062002c3486620f424062003b81565b62002c40919062003c47565b60088301546040516315874d3d60e31b8152600481018390526001600160a01b0387811660248301529293506101009091049091169063ac3a69e890604401602060405180830381865afa15801562002c9d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002cc3919062003bd1565b6000828152600160205260409020556200204d8482620030c2565b606080620f42408460e0015162002cf6919062003ab0565b600114801562002d10575062002d108460e0015162002b79565b801562002d4157508361016001516001600160a01b031662002d368560e00151620010de565b6001600160a01b0316145b1562002f605761012084015160008481526001602052604090819020549051634d5f8a7b60e11b81526001600160a01b0390921691639abf14f69162002d8d9160040190815260200190565b6040805180830381865afa92505050801562002dc8575060408051601f3d908101601f1916820190925262002dc59181019062003fc7565b60015b62002e99576001600081815260208181527fada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e855460e0880151835292905260409081902054905163992b9d2760e01b81526004810191909152610384602482015261039d60448201526101009091046001600160a01b03169063992b9d2790606401600060405180830381865afa15801562002e67573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262002e91919081019062003c0e565b915062002f60565b6001600081815260208181527fada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e855460e08a0151835292905260409081902054905163992b9d2760e01b8152600481019190915260248101849052604481018390526101009091046001600160a01b03169063992b9d2790606401600060405180830381865afa15801562002f31573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262002f5b919081019062003c0e565b935050505b835162002f7b62002f75620f42408662003fec565b620030de565b6040860151511562002f9257856040015162002fa2565b62002fa286610160015162003123565b60405160200162002fb69392919062004003565b60405160208183030381529060405290509250929050565b60606200303b8862002fe862002f75620f42408762003fec565b8762002ff488620030b2565b8b511562003003578b6200300e565b6200300e8b62003123565b876040516020016200302696959493929190620040a4565b604051602081830303815290604052620030b2565b6040516020016200304d9190620041f0565b6040516020818303038152906040529050979650505050505050565b6200307362003377565b968752602087019590955260608601939093526001600160a01b0391821661012086015292811661014085015291909116610160830152604082015290565b6060620008fe8260008062003149565b6200095182826040518060200160405280600081525062003241565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480620030f9575050819003601f19909101908152919050565b6060620031308262003264565b8051613078825260020160011990910190815292915050565b60608351801562003239576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106708515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f52602083018181018388602001018051600082525b60038a0199508951603f8160121c1651600053603f81600c1c1651600153603f8160061c1651600253603f811651600353506000518452600484019350828410620031c6579052602001604052613d3d60f01b600384066002048083039190915260008615159091029182900352900382525b509392505050565b6200324d8383620032d4565b823b1562000b515762000b51600084848462002a15565b60606040519050608081016040526f30313233343536373839616263646566600f526002810190506028815260208101600060288201528260601b925060005b808101820184821a600f81165160018301538060041c5182535050600181019060121901620032a4575050919050565b8160601b60601c915080600052673ec412a9852d173d60c11b601c5260206000208101810180548060601b15620033135763c991cbb16000526004601cfd5b831790556000829052601c600c20805460010163ffffffff81168402620033495767ea553b3401336cea841560021b526004601cfd5b9055808260007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a45050565b604051806101800160405280606081526020016060815260200160608152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b611199806200423883390190565b508054620034119062003b4b565b6000825580601f1062003422575050565b601f01602090049060005260206000209081019062000f5991905b808211156200345357600081556001016200343d565b5090565b6000602082840312156200346a57600080fd5b81356001600160e01b0319811681146200163557600080fd5b60005b83811015620034a057818101518382015260200162003486565b50506000910152565b60008151808452620034c381602086016020860162003483565b601f01601f19169290920160200192915050565b602081526000620016356020830184620034a9565b600060208284031215620034ff57600080fd5b5035919050565b80356001600160a01b03811681146200111857600080fd5b600080604083850312156200353257600080fd5b6200353d8362003506565b946020939093013593505050565b6000806000606084860312156200356157600080fd5b6200356c8462003506565b92506200357c6020850162003506565b9150604084013590509250925092565b60008060408385031215620035a057600080fd5b50508035926020909101359150565b60008083601f840112620035c257600080fd5b50813567ffffffffffffffff811115620035db57600080fd5b60208301915083602082850101111562000dfc57600080fd5b6000806000604084860312156200360a57600080fd5b83359250602084013567ffffffffffffffff8111156200362957600080fd5b6200363786828701620035af565b9497909650939450505050565b6000602082840312156200365757600080fd5b620016358262003506565b600081518084526020808501945080840160005b83811015620036945781518752958201959082019060010162003676565b509495945050505050565b60208152600062001635602083018462003662565b803580151581146200111857600080fd5b60008060408385031215620036d957600080fd5b620036e48362003506565b9150620036f460208401620036b4565b90509250929050565b60208152600082516101808060208501526200371e6101a0850183620034a9565b91506020850151601f19808685030160408701526200373e8483620034a9565b93506040870151915080868503016060870152506200375e8382620034a9565b92505060608501516080850152608085015160a085015260a085015160c085015260c085015160e085015260e0850151610100818187015280870151915050610120620037ae8187018315159052565b8601519050610140620037cb868201836001600160a01b03169052565b8601519050610160620037e8868201836001600160a01b03169052565b8601516001600160a01b03811683870152905062001295565b600080604083850312156200381557600080fd5b82359150620036f46020840162003506565b6000806000806000608086880312156200384057600080fd5b6200384b8662003506565b94506200385b6020870162003506565b935060408601359250606086013567ffffffffffffffff8111156200387f57600080fd5b6200388d88828901620035af565b969995985093965092949392505050565b60008060408385031215620038b257600080fd5b82359150620036f460208401620036b4565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715620039065762003906620038c4565b604052919050565b600067ffffffffffffffff8211156200392b576200392b620038c4565b50601f01601f191660200190565b600082601f8301126200394b57600080fd5b8135620039626200395c826200390e565b620038da565b8181528460208386010111156200397857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c08789031215620039af57600080fd5b863567ffffffffffffffff80821115620039c857600080fd5b620039d68a838b0162003939565b97506020890135915080821115620039ed57600080fd5b620039fb8a838b0162003939565b96506040890135955062003a1260608a0162003506565b945062003a2260808a0162003506565b935060a089013591508082111562003a3957600080fd5b5062003a4889828a0162003939565b9150509295509295509295565b6000806040838503121562003a6957600080fd5b62003a748362003506565b9150620036f46020840162003506565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008262003ac25762003ac262003a84565b500490565b600062003ad86200395c846200390e565b905082815283838301111562003aed57600080fd5b6200163583602083018462003483565b60006020828403121562003b1057600080fd5b815167ffffffffffffffff81111562003b2857600080fd5b8201601f8101841362003b3a57600080fd5b6200204d8482516020840162003ac7565b600181811c9082168062003b6057607f821691505b602082108103620020eb57634e487b7160e01b600052602260045260246000fd5b8082028115828204841417620008fe57620008fe62003a9a565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b60006020828403121562003be457600080fd5b5051919050565b600082601f83011262003bfd57600080fd5b620016358383516020850162003ac7565b60006020828403121562003c2157600080fd5b815167ffffffffffffffff81111562003c3957600080fd5b6200204d8482850162003beb565b80820180821115620008fe57620008fe62003a9a565b60006001820162003c725762003c7262003a9a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b81810381811115620008fe57620008fe62003a9a565b6000815162003cb981856020860162003483565b9290920192915050565b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222076696577426f783d2230203020313030302031303030222060208201527f6865696768743d2231303025222077696474683d2231303025223e000000000060408201526000845162003d4981605b85016020890162003483565b84519083019062003d6281605b84016020890162003483565b845191019062003d7a81605b84016020880162003483565b651e17b9bb339f60d11b605b929091019182015260610195945050505050565b6000806040838503121562003dae57600080fd5b825167ffffffffffffffff8082111562003dc757600080fd5b62003dd58683870162003beb565b9350602085015191508082111562003dec57600080fd5b5062003dfb8582860162003beb565b9150509250929050565b604080825283519082018190526000906020906060840190828701845b8281101562003e495781516001600160a01b03168452928401929084019060010162003e22565b5050508381038285015262003e5f818662003662565b9695505050505050565b601f82111562000b5157600081815260208120601f850160051c8101602086101562003e925750805b601f850160051c820191505b8181101562002a9d5782815560010162003e9e565b815167ffffffffffffffff81111562003ed05762003ed0620038c4565b62003ee88162003ee1845462003b4b565b8462003e69565b602080601f83116001811462003f20576000841562003f075750858301515b600019600386901b1c1916600185901b17855562002a9d565b600085815260208120601f198616915b8281101562003f515788860151825594840194600190910190840162003f30565b508582101562003f705787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c00000000000081526000825162003fba81601a85016020870162003483565b91909101601a0192915050565b6000806040838503121562003fdb57600080fd5b505080516020909101519092909150565b60008262003ffe5762003ffe62003a84565b500690565b661e3a34ba36329f60c91b8152600084516200402781600785016020890162003483565b61202360f01b60079184019182015284516200404b81600984016020890162003483565b7401e17ba34ba36329f1e3232b9b19f30b93a10313c9605d1b6009929091019182015283516200408381601e84016020880162003483565b661e17b232b9b19f60c91b601e929091019182015260250195945050505050565b693d913730b6b2911d101160b11b81528651600090620040cc81600a850160208c0162003483565b61202360f01b600a918401918201528751620040f081600c840160208c0162003483565b72111610113232b9b1b934b83a34b7b7111d101160691b600c929091019182015286516200412681601f840160208b0162003483565b7f222c2022696d616765223a2022646174613a696d6167652f7376672b786d6c3b601f92909101918201526618985cd94d8d0b60ca1b603f820152855162004176816046840160208a0162003483565b620041e2620041d5620041ce620041b1620041aa6046868801016d1116101130b93a34b9ba111d101160911b8152600e0190565b8a62003ca5565b7001116101130ba3a3934b13aba32b9911d1607d1b815260110190565b8762003ca5565b607d60f81b815260010190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516200422a81601d85016020870162003483565b91909101601d019291505056fe6080604052604051620011993803806200119983398101604081905262000026916200042e565b8051825114620000985760405162461bcd60e51b815260206004820152603260248201527f5061796d656e7453706c69747465723a2070617965657320616e6420736861726044820152710cae640d8cadccee8d040dad2e6dac2e8c6d60731b60648201526084015b60405180910390fd5b6000825111620000eb5760405162461bcd60e51b815260206004820152601a60248201527f5061796d656e7453706c69747465723a206e6f2070617965657300000000000060448201526064016200008f565b60005b82518110156200015757620001428382815181106200011157620001116200050c565b60200260200101518383815181106200012e576200012e6200050c565b60200260200101516200016060201b60201c565b806200014e8162000538565b915050620000ee565b50505062000570565b6001600160a01b038216620001cd5760405162461bcd60e51b815260206004820152602c60248201527f5061796d656e7453706c69747465723a206163636f756e74206973207468652060448201526b7a65726f206164647265737360a01b60648201526084016200008f565b600081116200021f5760405162461bcd60e51b815260206004820152601d60248201527f5061796d656e7453706c69747465723a2073686172657320617265203000000060448201526064016200008f565b6001600160a01b038216600090815260026020526040902054156200029b5760405162461bcd60e51b815260206004820152602b60248201527f5061796d656e7453706c69747465723a206163636f756e7420616c726561647960448201526a206861732073686172657360a81b60648201526084016200008f565b60048054600181019091557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0319166001600160a01b0384169081179091556000908152600260205260408120829055546200030390829062000554565b600055604080516001600160a01b0384168152602081018390527f40c340f65e17194d14ddddb073d3c9f888e3cb52b5aae0c6c7706b4fbc905fac910160405180910390a15050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156200038d576200038d6200034c565b604052919050565b60006001600160401b03821115620003b157620003b16200034c565b5060051b60200190565b600082601f830112620003cd57600080fd5b81516020620003e6620003e08362000395565b62000362565b82815260059290921b840181019181810190868411156200040657600080fd5b8286015b848110156200042357805183529183019183016200040a565b509695505050505050565b600080604083850312156200044257600080fd5b82516001600160401b03808211156200045a57600080fd5b818501915085601f8301126200046f57600080fd5b8151602062000482620003e08362000395565b82815260059290921b84018101918181019089841115620004a257600080fd5b948201945b83861015620004d95785516001600160a01b0381168114620004c95760008081fd5b82529482019490820190620004a7565b91880151919650909350505080821115620004f357600080fd5b506200050285828601620003bb565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016200054d576200054d62000522565b5060010190565b808201808211156200056a576200056a62000522565b92915050565b610c1980620005806000396000f3fe6080604052600436106100a05760003560e01c80639852595c116100645780639852595c146101ac578063a3f8eace146101e2578063c45ac05014610202578063ce7c2ac214610222578063d79779b214610258578063e33b7de31461028e57600080fd5b806319165587146100ee5780633a98ef3914610110578063406072a91461013457806348b75044146101545780638b83209b1461017457600080fd5b366100e9577f6ef95f06320e7a25a04a175ca677b7052bdd97131872c2192525a629f51be77033604080516001600160a01b0390921682523460208301520160405180910390a1005b600080fd5b3480156100fa57600080fd5b5061010e6101093660046109aa565b6102a3565b005b34801561011c57600080fd5b506000545b6040519081526020015b60405180910390f35b34801561014057600080fd5b5061012161014f3660046109c7565b610393565b34801561016057600080fd5b5061010e61016f3660046109c7565b6103c0565b34801561018057600080fd5b5061019461018f366004610a00565b6104d1565b6040516001600160a01b03909116815260200161012b565b3480156101b857600080fd5b506101216101c73660046109aa565b6001600160a01b031660009081526003602052604090205490565b3480156101ee57600080fd5b506101216101fd3660046109aa565b610501565b34801561020e57600080fd5b5061012161021d3660046109c7565b610549565b34801561022e57600080fd5b5061012161023d3660046109aa565b6001600160a01b031660009081526002602052604090205490565b34801561026457600080fd5b506101216102733660046109aa565b6001600160a01b031660009081526005602052604090205490565b34801561029a57600080fd5b50600154610121565b6001600160a01b0381166000908152600260205260409020546102e15760405162461bcd60e51b81526004016102d890610a19565b60405180910390fd5b60006102ec82610501565b90508060000361030e5760405162461bcd60e51b81526004016102d890610a5f565b80600160008282546103209190610ac0565b90915550506001600160a01b038216600090815260036020526040902080548201905561034d82826105ef565b604080516001600160a01b0384168152602081018390527fdf20fd1e76bc69d672e4814fafb2c449bba3a5369d8359adf9e05e6fde87b056910160405180910390a15050565b6001600160a01b038083166000908152600660209081526040808320938516835292905220545b92915050565b6001600160a01b0381166000908152600260205260409020546103f55760405162461bcd60e51b81526004016102d890610a19565b60006104018383610549565b9050806000036104235760405162461bcd60e51b81526004016102d890610a5f565b6001600160a01b0383166000908152600560205260408120805483929061044b908490610ac0565b90915550506001600160a01b03808416600090815260066020908152604080832093861683529290522080548201905561048683838361070d565b604080516001600160a01b038481168252602082018490528516917f3be5b7a71e84ed12875d241991c70855ac5817d847039e17a9d895c1ceb0f18a910160405180910390a2505050565b6000600482815481106104e6576104e6610ad3565b6000918252602090912001546001600160a01b031692915050565b60008061050d60015490565b6105179047610ac0565b9050610542838261053d866001600160a01b031660009081526003602052604090205490565b61075f565b9392505050565b6001600160a01b03821660009081526005602052604081205481906040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa1580156105a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cc9190610ae9565b6105d69190610ac0565b90506105e7838261053d8787610393565b949350505050565b8047101561063f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016102d8565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461068c576040519150601f19603f3d011682016040523d82523d6000602084013e610691565b606091505b50509050806107085760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016102d8565b505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261070890849061079a565b600080546001600160a01b0385168252600260205260408220548391906107869086610b02565b6107909190610b19565b6105e79190610b3b565b60006107ef826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661086f9092919063ffffffff16565b90508051600014806108105750808060200190518101906108109190610b4e565b6107085760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016102d8565b60606105e7848460008585600080866001600160a01b031685876040516108969190610b94565b60006040518083038185875af1925050503d80600081146108d3576040519150601f19603f3d011682016040523d82523d6000602084013e6108d8565b606091505b50915091506108e9878383876108f4565b979650505050505050565b6060831561096357825160000361095c576001600160a01b0385163b61095c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102d8565b50816105e7565b6105e783838151156109785781518083602001fd5b8060405162461bcd60e51b81526004016102d89190610bb0565b6001600160a01b03811681146109a757600080fd5b50565b6000602082840312156109bc57600080fd5b813561054281610992565b600080604083850312156109da57600080fd5b82356109e581610992565b915060208301356109f581610992565b809150509250929050565b600060208284031215610a1257600080fd5b5035919050565b60208082526026908201527f5061796d656e7453706c69747465723a206163636f756e7420686173206e6f2060408201526573686172657360d01b606082015260800190565b6020808252602b908201527f5061796d656e7453706c69747465723a206163636f756e74206973206e6f742060408201526a191d59481c185e5b595b9d60aa1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b808201808211156103ba576103ba610aaa565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610afb57600080fd5b5051919050565b80820281158282048414176103ba576103ba610aaa565b600082610b3657634e487b7160e01b600052601260045260246000fd5b500490565b818103818111156103ba576103ba610aaa565b600060208284031215610b6057600080fd5b8151801515811461054257600080fd5b60005b83811015610b8b578181015183820152602001610b73565b50506000910152565b60008251610ba6818460208701610b70565b9190910192915050565b6020815260008251806020840152610bcf816040850160208701610b70565b601f01601f1916919091016040019291505056fea264697066735822122003a40507ebe9275ce48fd35c4861059c83b8c31f661853a5d502cfaa2d14204364736f6c63430008140033a2646970667358221220c16d722be6fc3a67ea66203a78dba7d9fe2929fb2e924077f6ccb18b192bfd8564736f6c63430008140033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.