creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/renderers/PartyNFTRenderer.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport { LibSafeCast } from \"../utils/LibSafeCast.sol\";\nimport { LibRawResult } from \"../utils/LibRawResult.sol\";\nimport { LibRenderer, Color, ColorType } from \"../utils/LibRenderer.sol\";\nimport { Strings } from \"../utils/vendor/Strings.sol\";\nimport { Base64 } from \"../utils/vendor/Base64.sol\";\n\nimport { RendererBase } from \"./RendererBase.sol\";\nimport { RendererStorage } from \"./RendererStorage.sol\";\nimport { MetadataRegistry } from \"./MetadataRegistry.sol\";\nimport { IMetadataRegistry1_1 } from \"./IMetadataRegistry1_1.sol\";\nimport { Party, PartyGovernance } from \"../party/PartyGovernance.sol\";\nimport { PartyGovernanceNFT } from \"../party/PartyGovernanceNFT.sol\";\nimport { TokenDistributor } from \"../distribution/TokenDistributor.sol\";\nimport { IGlobals } from \"../globals/IGlobals.sol\";\nimport { LibGlobals } from \"../globals/LibGlobals.sol\";\nimport { IFont } from \"./fonts/IFont.sol\";\n\ncontract PartyNFTRenderer is RendererBase {\n    using LibSafeCast for uint256;\n    using LibRawResult for bytes;\n    using Strings for uint256;\n    using Strings for address;\n\n    error InvalidTokenIdError();\n    error ExternalURLTooLarge();\n\n    // The crowdfund type used to create this party.\n    enum CrowdfundType {\n        // Party was created using flexible crowdfund configuration which\n        // allowed members to contribute an amount in range and variable voting\n        // power awarded in proportion to the amount contributed (e.g. 1 ETH\n        // contributed with 10 ETH total contributed resulting in 10% voting\n        // power for contribution).\n        Flexible,\n        // Party was created using fixed crowdfund configuration which awarded\n        // members with fixed voting power per membership minted with each mint\n        // costing a fixed amount (e.g. 1 ETH for 10% voting power per mint,\n        // total of 10 mints available).\n        Fixed\n    }\n\n    enum RenderingMethod {\n        ENUM_OFFSET, // Apply an enum offset so that first valid value is 1\n        FlexibleCrowdfund,\n        FixedCrowdfund\n    }\n\n    struct ProposalData {\n        uint256 id;\n        string status;\n    }\n\n    struct Metadata {\n        string name;\n        string description;\n        string externalURL;\n        string image;\n        string banner;\n        string animationURL;\n        string collectionName;\n        string collectionDescription;\n        string collectionExternalURL;\n        address royaltyReceiver;\n        uint256 royaltyAmount;\n        RenderingMethod renderingMethod;\n    }\n\n    address immutable IMPL;\n\n    uint256 constant PARTY_CARD_DATA = 1;\n    address constant PARTYSTAR_PARTY_ADDRESS = 0x118928CCAc2035B578ae2D35FBFc2c120B6c4B82;\n    address constant PARTYSTAR_CROWDFUND_ADDRESS = 0x0Bf08f7b6474C2aCCB9b9e325acb6FbcC682dE82;\n    IMetadataRegistry1_1 constant OLD_METADATA_REGISTRY =\n        IMetadataRegistry1_1(0x175487875F0318EdbAB54BBA442fF53b36e96015);\n    /// @notice The old token distributor contract address.\n    /// @dev Cannot store immutable arrays...\n    address immutable TOKEN_DISTRIBUTOR_V1;\n    address immutable TOKEN_DISTRIBUTOR_V2;\n    address immutable TOKEN_DISTRIBUTOR_V3;\n\n    /// @notice The base url for external URLs. External URL is BASE_EXTERNAL_URL + PARTY_ADDRESS\n    /// @dev First byte is the size of the data, the rest is the data (starting from MSB)\n    bytes32 private immutable BASE_EXTERNAL_URL_DATA;\n\n    constructor(\n        IGlobals globals,\n        RendererStorage rendererStorage,\n        IFont font,\n        address tokenDistributionV1,\n        address tokenDistributionV2,\n        address tokenDistributionV3,\n        string memory baseExternalURL\n    ) RendererBase(globals, rendererStorage, font) {\n        IMPL = address(this);\n        TOKEN_DISTRIBUTOR_V1 = tokenDistributionV1;\n        TOKEN_DISTRIBUTOR_V2 = tokenDistributionV2;\n        TOKEN_DISTRIBUTOR_V3 = tokenDistributionV3;\n\n        bytes memory baseExternalURLBytes = bytes(baseExternalURL);\n        if (baseExternalURLBytes.length > 31) {\n            // Must be less than or equal to 31 bytes because 1 byte is used for the length.\n            revert ExternalURLTooLarge();\n        }\n        bytes32 baseExternalUrlData = bytes32(baseExternalURLBytes.length) << 248;\n        baseExternalUrlData |= bytes32(baseExternalURLBytes) >> 8;\n        BASE_EXTERNAL_URL_DATA = baseExternalUrlData;\n    }\n\n    function royaltyInfo(\n        uint256,\n        uint256\n    ) external view returns (address receiver, uint256 royaltyAmount) {\n        // Get any custom metadata for this party.\n        Metadata memory metadata = getCustomMetadata(0);\n\n        // By default, there are no royalties.\n        return (metadata.royaltyReceiver, metadata.royaltyAmount);\n    }\n\n    function contractURI() external view override returns (string memory) {\n        (bool isDarkMode, Color color) = getCustomizationChoices();\n        (string memory image, string memory banner) = LibRenderer.getCollectionImageAndBanner(\n            color,\n            isDarkMode\n        );\n\n        // Get any custom metadata for this party.\n        Metadata memory metadata = getCustomMetadata(0);\n\n        return\n            string.concat(\n                \"data:application/json;base64,\",\n                Base64.encode(\n                    abi.encodePacked(\n                        '{\"name\":\"',\n                        bytes(metadata.collectionName).length == 0\n                            ? generateCollectionName()\n                            : metadata.collectionName,\n                        '\", \"description\":\"',\n                        bytes(metadata.collectionDescription).length == 0\n                            ? generateCollectionDescription()\n                            : metadata.collectionDescription,\n                        '\", \"external_url\":\"',\n                        bytes(metadata.collectionExternalURL).length == 0\n                            ? generateExternalURL()\n                            : metadata.collectionExternalURL,\n                        '\", \"image\":\"',\n                        bytes(metadata.image).length == 0 ? image : metadata.image,\n                        // Determine which banner to render.\n                        bytes(metadata.banner).length == 0\n                            ? bytes(metadata.image).length == 0 // No custom banner and no custom image, use the default Party banner.\n                                ? string.concat('\", \"banner\":\"', banner) // No custom banner but custom image, do not include banner in metadata.\n                                : \"\"\n                            : string.concat('\", \"banner\":\"', metadata.banner), // Custom banner, use it.\n                        '\"}'\n                    )\n                )\n            );\n    }\n\n    function tokenURI(uint256 tokenId) external view returns (string memory) {\n        if (PartyGovernanceNFT(address(this)).ownerOf(tokenId) == address(0)) {\n            revert InvalidTokenIdError();\n        }\n\n        // Add backward compatibility for rendering custom metadata for\n        // Partystar party using old `MetadataRegistry` contract.\n        if (address(this) == PARTYSTAR_PARTY_ADDRESS) {\n            (\n                string memory customName,\n                string memory customDescription,\n                string memory customImage\n            ) = OLD_METADATA_REGISTRY.customPartyMetadataByCrowdfund(PARTYSTAR_CROWDFUND_ADDRESS);\n\n            return\n                string.concat(\n                    \"data:application/json;base64,\",\n                    Base64.encode(\n                        abi.encodePacked(\n                            '{\"name\":\"',\n                            string.concat(customName, \" #\", tokenId.toString()),\n                            '\", \"description\":\"',\n                            customDescription,\n                            '\", \"external_url\":\"',\n                            generateExternalURL(),\n                            '\", \"attributes\": [',\n                            generateAttributes(tokenId),\n                            '], \"image\":\"',\n                            customImage,\n                            '\"}'\n                        )\n                    )\n                );\n        }\n\n        string memory image = generateSVG(\n            PartyGovernanceNFT(address(this)).name(),\n            generateVotingPowerPercentage(tokenId),\n            getLatestProposalStatuses(),\n            PartyGovernance(address(this)).lastProposalId(),\n            tokenId\n        );\n\n        // Get any custom metadata for this party.\n        Metadata memory metadata = getCustomMetadata(tokenId);\n\n        // Construct metadata.\n        return\n            string.concat(\n                \"data:application/json;base64,\",\n                Base64.encode(\n                    abi.encodePacked(\n                        '{\"name\":\"',\n                        generateName(\n                            bytes(metadata.name).length == 0\n                                ? PartyGovernanceNFT(address(this)).name()\n                                : metadata.name,\n                            tokenId,\n                            metadata.renderingMethod\n                        ),\n                        '\", \"description\":\"',\n                        bytes(metadata.description).length == 0\n                            ? generateDescription(PartyGovernanceNFT(address(this)).name(), tokenId)\n                            : string.concat(\n                                metadata.description,\n                                \"\\\\n\\\\n\",\n                                // Append default description.\n                                generateDescription(\n                                    PartyGovernanceNFT(address(this)).name(),\n                                    tokenId\n                                )\n                            ),\n                        '\", \"external_url\":\"',\n                        bytes(metadata.externalURL).length == 0\n                            ? generateExternalURL()\n                            : metadata.externalURL,\n                        bytes(metadata.animationURL).length == 0\n                            ? \"\"\n                            : string.concat('\", \"animation_url\":\"', metadata.animationURL),\n                        '\", \"party_card_url\":\"', // Custom metadata field.\n                        image,\n                        '\", \"image\":\"',\n                        bytes(metadata.image).length == 0 ? image : metadata.image,\n                        hasPartyStarted()\n                            ? string.concat('\", \"attributes\": [', generateAttributes(tokenId), \"]\")\n                            : '\"',\n                        \"}\"\n                    )\n                )\n            );\n    }\n\n    function generateName(\n        string memory partyName,\n        uint256 tokenId,\n        RenderingMethod renderingMethod\n    ) private view returns (string memory) {\n        if (\n            renderingMethod == RenderingMethod.FixedCrowdfund ||\n            (renderingMethod == RenderingMethod.ENUM_OFFSET &&\n                getCrowdfundType() == CrowdfundType.Fixed)\n        ) {\n            return string.concat(partyName, \" #\", tokenId.toString());\n        } else {\n            if (hasPartyStarted()) {\n                return string.concat(generateVotingPowerPercentage(tokenId), \"% Voting Power\");\n            } else {\n                return \"Party Membership\";\n            }\n        }\n    }\n\n    function generateExternalURL() private view returns (string memory) {\n        bytes32 baseExternalUrlData = BASE_EXTERNAL_URL_DATA;\n        uint8 externalUrlLength = uint8(uint256(baseExternalUrlData >> 248));\n        string memory baseExternalUrl;\n        assembly {\n            let freeMem := mload(0x40)\n            baseExternalUrl := freeMem\n            // Store the length of the data\n            mstore(baseExternalUrl, externalUrlLength)\n            // Store the data to memory\n            mstore(add(baseExternalUrl, 0x20), shl(8, baseExternalUrlData))\n            // Update free mem\n            mstore(0x40, add(freeMem, 0x40))\n        }\n        return string.concat(baseExternalUrl, address(this).toHexString());\n    }\n\n    function generateDescription(\n        string memory partyName,\n        uint256 tokenId\n    ) private view returns (string memory) {\n        if (hasPartyStarted()) {\n            return\n                string.concat(\n                    \"This membership represents \",\n                    generateVotingPowerPercentage(tokenId),\n                    \"% voting power in \",\n                    partyName,\n                    \". Head to \",\n                    generateExternalURL(),\n                    \" to view the Party's latest activity.\"\n                );\n        } else {\n            return\n                string.concat(\n                    \"This item represents membership in \",\n                    partyName,\n                    \". Exact voting power will be determined when the crowdfund ends. Head to \",\n                    generateExternalURL(),\n                    \" to view the Party's latest activity.\"\n                );\n        }\n    }\n\n    function generateAttributes(uint256 tokenId) private view returns (string memory) {\n        string memory votingPowerPercentage = generateVotingPowerPercentage(tokenId);\n\n        if (\n            keccak256(abi.encodePacked(votingPowerPercentage)) == keccak256(abi.encodePacked(\"--\"))\n        ) {\n            votingPowerPercentage = \"0\";\n        }\n\n        return\n            string.concat(\n                '{\"trait_type\":\"Voting Power\", \"value\": \"',\n                votingPowerPercentage,\n                '\", \"max_value\":100}'\n            );\n    }\n\n    function generateCollectionName() internal view returns (string memory) {\n        return string.concat(\"Party Memberships: \", PartyGovernanceNFT(address(this)).name());\n    }\n\n    function generateCollectionDescription() internal view returns (string memory) {\n        return\n            string.concat(\n                \"This collection represents memberships in the following Party: \",\n                PartyGovernanceNFT(address(this)).name(),\n                \". Head to \",\n                generateExternalURL(),\n                \" to view the Party's latest activity.\"\n            );\n    }\n\n    function generateSVG(\n        string memory partyName,\n        string memory votingPowerPercentage,\n        PartyGovernance.ProposalStatus[4] memory proposalStatuses,\n        uint256 latestProposalId,\n        uint256 tokenId\n    ) public view returns (string memory) {\n        // Get the customization data for this party.\n        (bool isDarkMode, Color color) = getCustomizationChoices();\n\n        return\n            generateSVG(\n                partyName,\n                votingPowerPercentage,\n                proposalStatuses,\n                latestProposalId,\n                tokenId,\n                color,\n                isDarkMode\n            );\n    }\n\n    function generateSVG(\n        string memory partyName,\n        string memory votingPowerPercentage,\n        PartyGovernance.ProposalStatus[4] memory proposalStatuses,\n        uint256 latestProposalId,\n        uint256 tokenId,\n        Color color,\n        bool isDarkMode\n    ) public view returns (string memory) {\n        return\n            string.concat(\n                \"data:image/svg+xml;base64,\",\n                Base64.encode(\n                    abi.encodePacked(\n                        // Split to avoid stack too deep errors\n                        generateSVG1(color, isDarkMode),\n                        generateSVG2(partyName, color),\n                        generateSVG3(partyName, color),\n                        generateSVG4(latestProposalId, proposalStatuses),\n                        generateSVG5(votingPowerPercentage, color),\n                        generateSVG6(tokenId, color)\n                    )\n                )\n            );\n    }\n\n    function generateSVG1(Color color, bool isDarkMode) private pure returns (string memory) {\n        return\n            string.concat(\n                '<svg width=\"540\" height=\"540\" viewBox=\"0 -10 360 560\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs><linearGradient id=\"d\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"',\n                isDarkMode ? \"#2e3847\" : \"#ffffff\",\n                '\"/><stop offset=\"1\" stop-color=\"',\n                isDarkMode ? \"#000000\" : \"#e6edf5\",\n                '\"/></linearGradient><linearGradient id=\"e\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"',\n                isDarkMode ? \"#8091a8\" : \"#e6edf6\",\n                '\"/><stop offset=\"1\" stop-color=\"',\n                isDarkMode ? \"#2e3848\" : \"#bccbdd\",\n                '\"/></linearGradient><linearGradient id=\"f\" x1=\"0\" x2=\"0\" y1=\"1\" y2=\"0\"><stop offset=\"0\" stop-color=\"',\n                LibRenderer.generateColorHex(color, ColorType.SECONDARY),\n                '\"/><stop offset=\"1\" stop-color=\"',\n                LibRenderer.generateColorHex(color, ColorType.PRIMARY),\n                '\"/></linearGradient><linearGradient id=\"f2\" x1=\"0\" x2=\"0\" y1=\"-.5\" y2=\"1\"><stop offset=\"0\" stop-color=\"',\n                LibRenderer.generateColorHex(color, ColorType.SECONDARY),\n                '\"/><stop offset=\"1\" stop-color=\"',\n                LibRenderer.generateColorHex(color, ColorType.PRIMARY),\n                '\"/></linearGradient><linearGradient id=\"h\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"',\n                isDarkMode ? \"#ffffff\" : \"#3f485f\",\n                '\"/><stop offset=\".5\"  stop-color=\"',\n                isDarkMode ? \"#a7b8cf\" : \"#000000\",\n                '\"/></linearGradient><radialGradient cx=\"1\" cy=\"-.5\" id=\"i\" r=\"2\"><stop offset=\"0\" stop-color=\"#dce5f0\"/><stop offset=\".5\" stop-color=\"#dce5f0\" stop-opacity=\"0\"/></radialGradient>'\n            );\n    }\n\n    function generateSVG2(\n        string memory partyName,\n        Color color\n    ) private view returns (string memory) {\n        (uint256 duration, uint256 steps, uint256 delay, uint256 translateX) = LibRenderer\n            .calcAnimationVariables(partyName);\n\n        return\n            string.concat(\n                '<symbol id=\"a\" viewBox=\"0 0 300.15 300\"><path d=\"M6.07 0v300m-3-300v300M.07 0v300m9-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300m3-300v300\" style=\"stroke-width:.15px;stroke:',\n                LibRenderer.generateColorHex(color, ColorType.PRIMARY),\n                ';fill:none;0\"/></symbol><style>.z{animation:x ',\n                duration.toString(),\n                \"s steps(\",\n                steps.toString(),\n                \") infinite;}.y{animation-delay:\",\n                delay.toString(),\n                \"s}@keyframes x{to{transform:translateX(-\",\n                translateX.toString(),\n                \"px)}}.v{fill:\",\n                LibRenderer.generateColorHex(color, ColorType.DARK),\n                \";font-family:pixeldroidConsoleRegular,Console;font-size:48px}.w{animation:W 1s steps(1, jump-end) infinite;}@keyframes W{50%{fill:\",\n                LibRenderer.generateColorHex(color, ColorType.LIGHT),\n                \"}}@font-face{font-family:pixeldroidConsoleRegular;src:url(\",\n                _font.getFont(),\n                \");}</style></defs>\"\n            );\n    }\n\n    function generateSVG3(\n        string memory partyName,\n        Color color\n    ) private pure returns (string memory) {\n        return\n            string.concat(\n                '<rect height=\"539\" rx=\"29.5\" ry=\"29.5\" style=\"fill:url(#d);stroke:url(#e)\" width=\"359\" x=\".5\" y=\".5\"/><rect rx=\"15.5\" ry=\"15.5\" style=\"stroke:url(#f);fill:',\n                LibRenderer.generateColorHex(color, ColorType.PRIMARY),\n                '\" width=\"331\" height=\"346\" x=\"14.5\" y=\"179.5\"/><path d=\"M321 501H198v-27h123v27Zm9-282H30v27h300v-27Zm0 60H30v27h300v-27Zm0 30H30v27h300v-27Zm0 30H30v27h300v-27Zm0 30H30v27h300v-27Z\" style=\"fill:',\n                LibRenderer.generateColorHex(color, ColorType.LIGHT),\n                ';\"/><clipPath id=\"clip\"><path d=\"M31 501H198v-27h123v27Zm9-282H30v27h300v-27Zm0\"/></clipPath><g clip-path=\"url(#clip)\"><g class=\"z\"><text class=\"v\" x=\"327\" y=\"240\">',\n                partyName,\n                '</text></g><g class=\"z y\"><text class=\"v\" x=\"327\" y=\"240\">',\n                partyName,\n                \"</text></g></g>\"\n            );\n    }\n\n    function generateSVG4(\n        uint256 latestProposalId,\n        PartyGovernance.ProposalStatus[4] memory proposalStatuses\n    ) private pure returns (string memory part) {\n        // Render latest 4 proposals, or up to the latest proposal if there are\n        // less than 4 proposals.\n        for (uint256 i; i < (latestProposalId < 4 ? latestProposalId : 4); i++) {\n            // Should produce something like this:\n            //   '<text class=\"v\" x=\"30\" y=\"300\">',\n            //   latestProposalStatus1,\n            //   '</text><text class=\"v\" x=\"30\" y=\"330\">',\n            //   latestProposalStatus2,\n            //   '</text><text class=\"v\" x=\"30\" y=\"360\">',\n            //   latestProposalStatus3,\n            //   '</text><text class=\"v\" x=\"30\" y=\"390\">',\n            //   latestProposalStatus4,\n            //   '</text><text class=\"v\" x=\"201\" y=\"495\">'\n            part = string(\n                abi.encodePacked(\n                    part,\n                    '<text class=\"v\" x=\"30\" y=\"',\n                    (300 + (i * 30)).toString(),\n                    '\">',\n                    generateProposalStatus(latestProposalId - i, proposalStatuses[i]),\n                    \"</text>\"\n                )\n            );\n        }\n\n        part = string(abi.encodePacked(part, '<text class=\"v\" x=\"201\" y=\"495\">'));\n    }\n\n    function generateSVG5(\n        string memory votingPowerPercentage,\n        Color color\n    ) private pure returns (string memory) {\n        return\n            string.concat(\n                votingPowerPercentage,\n                '</text><text class=\"v\" x=\"297\" y=\"495\">%</text>'\n                '<use height=\"300\" x=\"30\" y=\"210\" width=\"300.15\" xlink:href=\"#a\"/><use height=\"300\" transform=\"rotate(-90 270 240)\" width=\"300.15\" xlink:href=\"#a\"/><rect rx=\"3.5\" ry=\"3.5\" style=\"fill:none;stroke:',\n                LibRenderer.generateColorHex(color, ColorType.DARK),\n                ';stroke-width:3px\" width=\"138\" height=\"42\" x=\"190.5\" y=\"466.5\"/><path fill=\"',\n                LibRenderer.generateColorHex(color, ColorType.DARK)\n            );\n    }\n\n    function generateSVG6(uint256 tokenId, Color color) private view returns (string memory) {\n        return\n            string.concat(\n                '\"',\n                _storage.readFile(PARTY_CARD_DATA),\n                '<path d=\"M285 164c-7.72 0-14-6.28-14-14s6.28-14 14-14h45c7.72 0 14 6.28 14 14s-6.28 14-14 14h-45Z\" style=\"fill:none;stroke:',\n                LibRenderer.generateColorHex(color, ColorType.PRIMARY),\n                ';stroke-width:2px\"/><path d=\"M307.5 68.19c-20.71 0-37.5 11.6-37.5 25.91s16.79 25.91 37.5 25.91S345 108.41 345 94.1s-16.79-25.91-37.5-25.91Zm12.84 41.66v-7.31l9.74-3.17-25.51-8.29 14.01 19.28a43.35 43.35 0 0 1-20.64.38v-8.33l9.74-3.17-25.51-8.29 14.06 19.35c-10.92-3-18.63-10.23-18.63-18.68 0-6.22 4.17-11.78 10.73-15.48l14.24 19.6V85.5l9.74-3.17-22.07-7.17a40.24 40.24 0 0 1 17.25-3.7c11.3 0 21.13 4.23 26.21 10.47l-23.17-7.53 15.76 21.7V85.86l8.32-2.7a14.9 14.9 0 0 1 2.77 8.48c0 8.04-6.97 14.98-17.05 18.22Z\" style=\"fill:#A7B8CF\"/><clipPath id=\"C\"><path d=\"m98.43 483.54 1.39 5.02h-3.77l1.42-5.02.38-2.02h.17l.41 2.02ZM171 470v35a5 5 0 0 1-5 5H35a5 5 0 0 1-5-5v-35a5 5 0 0 1 5-5h131a5 5 0 0 1 5 5Zm-93.49 10.56c-1.71-1.4-3.1-2.06-5.5-2.06-3.82 0-7.39 3.19-7.39 8.64 0 6.35 3.17 9.36 7.34 9.36 2.4 0 4.12-.82 5.47-2.47v-4.06c-1.06 1.58-2.35 2.95-4.54 2.95-3.19 0-4.13-3.44-4.13-5.38 0-3.42 1.76-4.94 4.1-4.94 1.78 0 3.26.71 4.63 2.93v-4.97Zm11.28 11.81h-4.36v-13.54h-4.25v17.3h8.61v-3.77Zm17.27 3.77-5.06-17.3h-6.1l-5.09 17.3h4.27l1.06-4.06h5.59l1.06 4.06h4.27Zm6.21-17.3H108v17.3h4.27v-17.3Zm22.1 0h-6.39l-2.45 9.73-.28 3.64h-.24l-.32-3.67-2.46-9.71h-6.39v17.3h4.15v-10.44c0-.38-.1-2.16-.17-3.07h.24l3.07 13.51h3.96l3.07-13.51h.24l-.19 3.07v10.44h4.15v-17.3Z\" /></clipPath><g clip-path=\"url(#C)\"><rect class=\"w\" x=\"30\" y=\"465\" width=\"142\" height=\"50\" fill=\"',\n                hasUnclaimedDistribution(tokenId)\n                    ? LibRenderer.generateColorHex(color, ColorType.DARK)\n                    : LibRenderer.generateColorHex(color, ColorType.LIGHT),\n                '\"/></g><rect height=\"345\" rx=\"15\" ry=\"15\" style=\"fill:url(#i)\" width=\"330\" x=\"15\" y=\"180\"/><text text-anchor=\"middle\" style=\"font-family:ui-monospace,Cascadia Mono,Menlo,Monaco,Segoe UI Mono,Roboto Mono,Oxygen Mono,Ubuntu Monospace,Source Code Pro,Droid Sans Mono,Fira Mono,Courier,monospace;fill:',\n                LibRenderer.generateColorHex(color, ColorType.PRIMARY),\n                ';font-weight:500;\" x=\"307.5\" y=\"156\">',\n                // Always render token ID with 3 digits.\n                LibRenderer.prependNumWithZeros(tokenId.toString(), 3),\n                \"</text></svg>\"\n            );\n    }\n\n    function generateVotingPowerPercentage(uint256 tokenId) private view returns (string memory) {\n        Party party = Party(payable(address(this)));\n        uint256 totalVotingPower = getTotalVotingPower();\n\n        if (totalVotingPower == 0) {\n            return \"--\";\n        }\n\n        uint256 intrinsicVotingPowerPercentage = (party.votingPowerByTokenId(tokenId) * 1e18) /\n            totalVotingPower;\n\n        if (intrinsicVotingPowerPercentage == 1e18) {\n            return \"100\";\n        } else if (intrinsicVotingPowerPercentage < 0.1e18) {\n            return LibRenderer.formatAsDecimalString(intrinsicVotingPowerPercentage, 16, 3);\n        } else {\n            return LibRenderer.formatAsDecimalString(intrinsicVotingPowerPercentage, 16, 4);\n        }\n    }\n\n    function generateProposalStatus(\n        uint256 proposalId,\n        PartyGovernance.ProposalStatus status\n    ) private pure returns (string memory) {\n        string memory statusMessage;\n        if (status == PartyGovernance.ProposalStatus.Voting) {\n            statusMessage = \"Voting now\";\n        } else if (status == PartyGovernance.ProposalStatus.Passed) {\n            statusMessage = \"Passing\";\n        } else if (status == PartyGovernance.ProposalStatus.Ready) {\n            statusMessage = \"Executable\";\n        } else if (status == PartyGovernance.ProposalStatus.InProgress) {\n            statusMessage = \"In progress\";\n        } else if (status == PartyGovernance.ProposalStatus.Complete) {\n            statusMessage = \"Complete\";\n        } else if (status == PartyGovernance.ProposalStatus.Defeated) {\n            statusMessage = \"Defeated\";\n        } else if (status == PartyGovernance.ProposalStatus.Cancelled) {\n            statusMessage = \"Cancelled\";\n        } else {\n            return \"\";\n        }\n\n        return string.concat(\"#\", proposalId.toString(), \" - \", statusMessage);\n    }\n\n    function getCustomMetadata(uint256 tokenId) private view returns (Metadata memory metadata) {\n        MetadataRegistry registry = MetadataRegistry(\n            _GLOBALS.getAddress(LibGlobals.GLOBAL_METADATA_REGISTRY)\n        );\n\n        bytes memory encodedMetadata = registry.getMetadata(address(this), tokenId);\n        return encodedMetadata.length != 0 ? abi.decode(encodedMetadata, (Metadata)) : metadata;\n    }\n\n    function getLatestProposalStatuses()\n        private\n        view\n        returns (PartyGovernance.ProposalStatus[4] memory proposalStatuses)\n    {\n        uint256 latestProposalId = PartyGovernance(address(this)).lastProposalId();\n        uint256 numOfProposalsToDisplay = latestProposalId < 4 ? latestProposalId : 4;\n        for (uint256 i; i < numOfProposalsToDisplay; ++i) {\n            uint256 proposalId = latestProposalId - i;\n            PartyGovernance.ProposalStatus proposalStatus;\n\n            // Get the status of the proposal, regardless of the version of the\n            // Party contract.\n            (bool s, bytes memory r) = address(this).staticcall(\n                abi.encodeCall(PartyGovernance.getProposalStateInfo, (proposalId))\n            );\n            if (!s) {\n                r.rawRevert();\n            }\n            assembly {\n                proposalStatus := mload(add(r, 0x20))\n            }\n\n            proposalStatuses[i] = proposalStatus;\n        }\n    }\n\n    function getCrowdfundType() private view returns (CrowdfundType crowdfundType) {\n        Party party = Party(payable(address(this)));\n        uint256 numOfTokensToCheck = 5;\n        uint256 tokenCount = party.tokenCount();\n        if (tokenCount < numOfTokensToCheck) {\n            // Default to flexible membership.\n            return CrowdfundType.Flexible;\n        } else {\n            // Check the voting power of several tokens. If they are all the\n            // same, assume it is a fixed membership.\n\n            uint256 lastTokenId = tokenCount / numOfTokensToCheck;\n            for (uint256 i = 2; i <= numOfTokensToCheck; ++i) {\n                uint256 tokenId = i * (tokenCount / numOfTokensToCheck);\n\n                if (\n                    party.votingPowerByTokenId(tokenId) != party.votingPowerByTokenId(lastTokenId)\n                ) {\n                    return CrowdfundType.Flexible;\n                }\n\n                lastTokenId = tokenId;\n            }\n\n            return CrowdfundType.Fixed;\n        }\n    }\n\n    function getTotalVotingPower() private view returns (uint96 totalVotingPower) {\n        // Get the total voting power of the Party regardless of\n        // the version of the Party contract.\n        (bool s, bytes memory r) = address(this).staticcall(\n            abi.encodeCall(PartyGovernance.getGovernanceValues, ())\n        );\n        if (!s) {\n            r.rawRevert();\n        }\n        assembly {\n            totalVotingPower := mload(add(r, 0x80))\n        }\n    }\n\n    function hasUnclaimedDistribution(uint256 tokenId) private view returns (bool) {\n        if (address(this) == IMPL) return false;\n\n        // There will only be one distributor if old token distributor is not set\n        TokenDistributor[] memory distributors = new TokenDistributor[](4);\n        distributors[0] = TokenDistributor(\n            _GLOBALS.getAddress(LibGlobals.GLOBAL_TOKEN_DISTRIBUTOR)\n        );\n        uint256 l = 1;\n        if (TOKEN_DISTRIBUTOR_V3 != address(0)) {\n            distributors[l++] = TokenDistributor(TOKEN_DISTRIBUTOR_V3);\n        }\n        if (TOKEN_DISTRIBUTOR_V2 != address(0)) {\n            distributors[l++] = TokenDistributor(TOKEN_DISTRIBUTOR_V2);\n        }\n        if (TOKEN_DISTRIBUTOR_V1 != address(0)) {\n            distributors[l++] = TokenDistributor(TOKEN_DISTRIBUTOR_V1);\n        }\n        assembly {\n            // Update length of `distributors`\n            mstore(distributors, l)\n        }\n\n        Party party = Party(payable(address(this)));\n        for (uint256 i; i < distributors.length; ++i) {\n            TokenDistributor distributor = distributors[i];\n            uint256 lastDistributionId = distributor.lastDistributionIdPerParty(party);\n\n            for (\n                uint256 distributionId = 1;\n                distributionId <= lastDistributionId;\n                ++distributionId\n            ) {\n                if (!distributor.hasPartyTokenIdClaimed(party, tokenId, distributionId)) {\n                    TokenDistributor.DistributionInfo memory info;\n                    info.party = party;\n                    info.distributionId = distributionId;\n                    info.totalShares = 1; // low amount to avoid div by 0\n                    info.memberSupply = 1e18; // arbitrary amount\n\n                    // `TokenIdAboveMaxError` may prevent it from being claimed.\n                    (bool success, bytes memory response) = address(distributor).staticcall(\n                        abi.encodeCall(TokenDistributor.getClaimAmount, (info, tokenId))\n                    );\n\n                    if (success) {\n                        uint128 amount = abi.decode(response, (uint128));\n                        if (amount != 0) return true;\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    function hasPartyStarted() private view returns (bool) {\n        return getTotalVotingPower() != 0;\n    }\n}\n"
      },
      "contracts/utils/LibSafeCast.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nlibrary LibSafeCast {\n    error Uint256ToUint96CastOutOfRange(uint256 v);\n    error Uint256ToInt192CastOutOfRange(uint256 v);\n    error Int192ToUint96CastOutOfRange(int192 i192);\n    error Uint256ToInt128CastOutOfRangeError(uint256 u256);\n    error Uint256ToUint128CastOutOfRangeError(uint256 u256);\n    error Uint256ToUint40CastOutOfRangeError(uint256 u256);\n    error Uint96ToUint16CastOutOfRange(uint96 u96);\n\n    function safeCastUint256ToUint96(uint256 v) internal pure returns (uint96) {\n        if (v > uint256(type(uint96).max)) {\n            revert Uint256ToUint96CastOutOfRange(v);\n        }\n        return uint96(v);\n    }\n\n    function safeCastUint256ToUint128(uint256 v) internal pure returns (uint128) {\n        if (v > uint256(type(uint128).max)) {\n            revert Uint256ToUint128CastOutOfRangeError(v);\n        }\n        return uint128(v);\n    }\n\n    function safeCastUint256ToUint160(uint256 v) internal pure returns (uint160) {\n        if (v > uint256(type(uint160).max)) {\n            revert Uint256ToUint128CastOutOfRangeError(v);\n        }\n        return uint160(v);\n    }\n\n    function safeCastUint256ToInt192(uint256 v) internal pure returns (int192) {\n        if (v > uint256(uint192(type(int192).max))) {\n            revert Uint256ToInt192CastOutOfRange(v);\n        }\n        return int192(uint192(v));\n    }\n\n    function safeCastUint96ToUint16(uint96 v) internal pure returns (uint16) {\n        if (v > uint96(type(uint16).max)) {\n            revert Uint96ToUint16CastOutOfRange(v);\n        }\n        return uint16(v);\n    }\n\n    function safeCastUint96ToInt192(uint96 v) internal pure returns (int192) {\n        return int192(uint192(v));\n    }\n\n    function safeCastInt192ToUint96(int192 i192) internal pure returns (uint96) {\n        if (i192 < 0 || i192 > int192(uint192(type(uint96).max))) {\n            revert Int192ToUint96CastOutOfRange(i192);\n        }\n        return uint96(uint192(i192));\n    }\n\n    function safeCastUint256ToInt128(uint256 x) internal pure returns (int128) {\n        if (x > uint256(uint128(type(int128).max))) {\n            revert Uint256ToInt128CastOutOfRangeError(x);\n        }\n        return int128(uint128(x));\n    }\n\n    function safeCastUint256ToUint40(uint256 x) internal pure returns (uint40) {\n        if (x > uint256(type(uint40).max)) {\n            revert Uint256ToUint40CastOutOfRangeError(x);\n        }\n        return uint40(x);\n    }\n}\n"
      },
      "contracts/utils/LibRawResult.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nlibrary LibRawResult {\n    // Revert with the data in `b`.\n    function rawRevert(bytes memory b) internal pure {\n        assembly {\n            revert(add(b, 32), mload(b))\n        }\n    }\n\n    // Return with the data in `b`.\n    function rawReturn(bytes memory b) internal pure {\n        assembly {\n            return(add(b, 32), mload(b))\n        }\n    }\n}\n"
      },
      "contracts/utils/LibRenderer.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport { Strings } from \"../utils/vendor/Strings.sol\";\n\nenum Color {\n    DEFAULT,\n    GREEN,\n    CYAN,\n    BLUE,\n    PURPLE,\n    PINK,\n    ORANGE,\n    RED\n}\n\nenum ColorType {\n    PRIMARY,\n    SECONDARY,\n    LIGHT,\n    DARK\n}\n\nlibrary LibRenderer {\n    using Strings for uint256;\n    using Strings for string;\n\n    function calcAnimationVariables(\n        string memory partyName\n    ) external pure returns (uint256 duration, uint256 steps, uint256 delay, uint256 translateX) {\n        translateX = bytes(partyName).length * 30 + 300;\n        duration = translateX / 56;\n\n        // Make duration even so that the animation delay is always exactly\n        // half of the duration.\n        if (duration % 2 != 0) duration += 1;\n\n        delay = duration / 2;\n        steps = translateX / 6;\n    }\n\n    function formatAsDecimalString(\n        uint256 n,\n        uint256 decimals,\n        uint256 maxChars\n    ) external pure returns (string memory) {\n        string memory str = n.toString();\n        uint256 oneUnit = 10 ** decimals;\n        if (n < 10 ** (decimals - 2)) {\n            return \"&lt;0.01\";\n        } else if (n < oneUnit) {\n            // Preserve leading zeros for decimals.\n            // (e.g. if 0.01, `n` will \"1\" so we need to prepend a \"0\").\n            return\n                string.concat(\"0.\", prependNumWithZeros(str, decimals).substring(0, maxChars - 1));\n        } else if (n >= 1000 * oneUnit) {\n            return str.substring(0, maxChars);\n        } else {\n            uint256 i = bytes((n / oneUnit).toString()).length;\n            return string.concat(str.substring(0, i), \".\", str.substring(i, maxChars));\n        }\n    }\n\n    function prependNumWithZeros(\n        string memory numStr,\n        uint256 expectedLength\n    ) public pure returns (string memory) {\n        uint256 length = bytes(numStr).length;\n        if (length < expectedLength) {\n            for (uint256 i; i < expectedLength - length; ++i) {\n                numStr = string.concat(\"0\", numStr);\n            }\n        }\n\n        return numStr;\n    }\n\n    function generateColorHex(\n        Color color,\n        ColorType colorType\n    ) external pure returns (string memory colorHex) {\n        if (color == Color.DEFAULT) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#A7B8CF\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#DCE5F0\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#91A6C3\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#50586D\";\n            }\n        } else if (color == Color.GREEN) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#10B173\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#93DCB7\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#00A25A\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#005E3B\";\n            }\n        } else if (color == Color.CYAN) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#00C1FA\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#B1EFFD\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#00B4EA\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#005669\";\n            }\n        } else if (color == Color.BLUE) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#2C78F3\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#B3D4FF\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#0E70E0\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#00286A\";\n            }\n        } else if (color == Color.PURPLE) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#9B45DF\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#D2ACF2\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#832EC9\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#47196B\";\n            }\n        } else if (color == Color.PINK) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#FF6BF3\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#FFC8FB\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#E652E2\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#911A96\";\n            }\n        } else if (color == Color.ORANGE) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#FF8946\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#FFE38B\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#E47B2F\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#732700\";\n            }\n        } else if (color == Color.RED) {\n            if (colorType == ColorType.PRIMARY) {\n                return \"#EC0000\";\n            } else if (colorType == ColorType.SECONDARY) {\n                return \"#FFA6A6\";\n            } else if (colorType == ColorType.LIGHT) {\n                return \"#D70000\";\n            } else if (colorType == ColorType.DARK) {\n                return \"#6F0000\";\n            }\n        }\n    }\n\n    function getCollectionImageAndBanner(\n        Color color,\n        bool isDarkMode\n    ) external pure returns (string memory image, string memory banner) {\n        if (isDarkMode) {\n            if (color == Color.GREEN) {\n                image = \"QmdcjXrxj7EimjuNTLQp1uKM2zYhuF1WVkVjF6TpfNNXrf\";\n                banner = \"QmR3vqAV17SJiwksiCHV1cLQuf9TKZuar8NQu8GmKkHRXM\";\n            } else if (color == Color.CYAN) {\n                image = \"QmS678DTkTTzFQEDiqj3AsW6wt6bi4bNhWbKcBM29HBhhB\";\n                banner = \"QmYSbXyPh9Lx2wmv6Z7SK8iFD9kyADxtaohV1gv6ZVKFj4\";\n            } else if (color == Color.BLUE) {\n                image = \"QmX2k8beAjyVhPk1ZrK6KrwbqLk3fRNPPmknRti7zEtGQa\";\n                banner = \"QmaXN8MbcrjkHPt97Z7xeuTCx6wPkJ9xZN7ExZZwMabspy\";\n            } else if (color == Color.PURPLE) {\n                image = \"Qmf8SrxKH3QZQCEzcMbA3UoJGZ1j2coTLaQFptWhZZvqhg\";\n                banner = \"QmWhpo9kN2Nf8ioWb7BKwqsCQKt3M2TduHPNqMqXz9jUK4\";\n            } else if (color == Color.PINK) {\n                image = \"QmV5eT9DWvU5BJa4LVSemkoDKyjJBC56adk2JLWMXYEQfn\";\n                banner = \"QmP2NTyvMQ5yN1RY4nH1HfoTX2r6Ug6WZfN9GFf9toak8M\";\n            } else if (color == Color.ORANGE) {\n                image = \"QmPirB7VFaao2ZUxLtM5WTCwZhE7c9Uy2heyNZF5t9PgsS\";\n                banner = \"QmUMgkjhrxedcLUvWt8VKrZdvrCGmjxWDSDXfm2M96nKUx\";\n            } else if (color == Color.RED) {\n                image = \"QmNRZ3syuEiiAkWYRFs9BpQ5M38wv8tEu17J2sYwmMdeta\";\n                banner = \"QmQV1EjzwQsXdgi6C6ubfopZrprk6Ab9LVnggZBgJDg5C2\";\n            } else {\n                image = \"QmNwGtGyYwDfS6ghQbDw5a9buv7auFXz63W3rDhzmxjVhw\";\n                banner = \"QmYUujTBgH6RTswZSiSzuy2GUojE6H9edaXeoSwwvh2T7o\";\n            }\n        } else {\n            if (color == Color.GREEN) {\n                image = \"QmR7t2g2hrkMYyzhUMEANzEGX74FQcbg3c7eTvCcQucMst\";\n                banner = \"Qmc3zRfT6nC2G1KgoWpCvLDVjNi7x7KFgwButecj3sq9qg\";\n            } else if (color == Color.CYAN) {\n                image = \"QmeiBRb9muNXej3dn4usjjdtbUpgYASfA5jmWqJRshivGH\";\n                banner = \"QmcsvMB2xiBKKMyKsBkC51TjWnyk42nNJnsudoGFsvvXCt\";\n            } else if (color == Color.BLUE) {\n                image = \"QmaErgGsanUTo73RMgvizMg3c7x1d1X4t76Tti22Pc1xan\";\n                banner = \"QmSjhHF994xBd7wavV4mhj7GpZmw5euQUBxpiPbbmxExqp\";\n            } else if (color == Color.PURPLE) {\n                image = \"QmeVJTcUpKQFSz5aBsVpQk8quoXEEZBNPAAMo3wHvdRzHa\";\n                banner = \"QmYx6aHYGitr6p8dHUa7n4nyew3pWy1Mfdn8fpFLejmhHC\";\n            } else if (color == Color.PINK) {\n                image = \"QmY4JJkBEeHVYHdfCCXPd7bWkAhNRxuKTWdf9MssGxSmCG\";\n                banner = \"QmQH5CFf3qXG2oymGzwTDgUmPFBsaTw2Qbfhjk58VHcABd\";\n            } else if (color == Color.ORANGE) {\n                image = \"QmYhB3vjBLPwPTC5SBidNbZhB5oBMBMpKy4g6ejTxmGLkK\";\n                banner = \"QmRP8cVjJyPRV7wXs5ApwbCFyTBpHXmHsDexbcN4o7aomd\";\n            } else if (color == Color.RED) {\n                image = \"QmfG8HPMEsKwKJ8xX3i2JhJtCskuiUjZLe8NhvXFdYyFR2\";\n                banner = \"QmazXbqfFtQexkwFDbYkyvLF4xRSDrmEVQS4PSAs2ZtxDn\";\n            } else {\n                image = \"QmZKE4XkPvU7Z8CdgK2Cn7gLQ4t8CDkkfnR1j5bZ2AfRJu\";\n                banner = \"QmTKCqLUQJt3VxGuUqLMj1jcCRRsaZwY4k757Wb7YPzmH2\";\n            }\n        }\n\n        image = string.concat(\"ipfs://\", image);\n        banner = string.concat(\"ipfs://\", banner);\n    }\n}\n"
      },
      "contracts/utils/vendor/Strings.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8;\n\n// Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol\nlibrary Strings {\n    bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        // Inspired by OraclizeAPI's implementation - MIT licence\n        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n        if (value == 0) {\n            return \"0\";\n        }\n        uint256 temp = value;\n        uint256 digits;\n        while (temp != 0) {\n            digits++;\n            temp /= 10;\n        }\n        bytes memory buffer = new bytes(digits);\n        while (value != 0) {\n            digits -= 1;\n            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n            value /= 10;\n        }\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        if (value == 0) {\n            return \"0x00\";\n        }\n        uint256 temp = value;\n        uint256 length = 0;\n        while (temp != 0) {\n            length++;\n            temp >>= 8;\n        }\n        return toHexString(value, length);\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _HEX_SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n\n    function substring(\n        string memory str,\n        uint startIndex,\n        uint endIndex\n    ) internal pure returns (string memory) {\n        bytes memory strBytes = bytes(str);\n        bytes memory result = new bytes(endIndex - startIndex);\n        for (uint i = startIndex; i < endIndex; i++) {\n            result[i - startIndex] = strBytes[i];\n        }\n        return string(result);\n    }\n}\n"
      },
      "contracts/utils/vendor/Base64.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8;\n\n/// [MIT License]\n/// @title Base64\n/// @notice Provides a function for encoding some bytes in base64\n/// @author Brecht Devos <brecht@loopring.org>\nlibrary Base64 {\n    bytes internal constant TABLE =\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n    /// @notice Encodes some bytes to the base64 representation\n    function encode(bytes memory data) internal pure returns (string memory) {\n        uint256 len = data.length;\n        if (len == 0) return \"\";\n\n        // multiply by 4/3 rounded up\n        uint256 encodedLen = 4 * ((len + 2) / 3);\n\n        // Add some extra buffer at the end\n        bytes memory result = new bytes(encodedLen + 32);\n\n        bytes memory table = TABLE;\n\n        assembly {\n            let tablePtr := add(table, 1)\n            let resultPtr := add(result, 32)\n\n            for {\n                let i := 0\n            } lt(i, len) {\n\n            } {\n                i := add(i, 3)\n                let input := and(mload(add(data, i)), 0xffffff)\n\n                let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))\n                out := shl(8, out)\n                out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))\n                out := shl(8, out)\n                out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))\n                out := shl(8, out)\n                out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))\n                out := shl(224, out)\n\n                mstore(resultPtr, out)\n\n                resultPtr := add(resultPtr, 4)\n            }\n\n            switch mod(len, 3)\n            case 1 {\n                mstore(sub(resultPtr, 2), shl(240, 0x3d3d))\n            }\n            case 2 {\n                mstore(sub(resultPtr, 1), shl(248, 0x3d))\n            }\n\n            mstore(result, encodedLen)\n        }\n\n        return string(result);\n    }\n}\n"
      },
      "contracts/renderers/RendererBase.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport { Strings } from \"../utils/vendor/Strings.sol\";\nimport { Color } from \"../utils/LibRenderer.sol\";\nimport { Party } from \"contracts/party/Party.sol\";\nimport { IGlobals } from \"../globals/IGlobals.sol\";\nimport { IFont } from \"./fonts/IFont.sol\";\nimport { IERC721Renderer } from \"./IERC721Renderer.sol\";\nimport { RendererStorage } from \"./RendererStorage.sol\";\n\nabstract contract RendererBase is IERC721Renderer {\n    using Strings for uint256;\n    using Strings for string;\n\n    IGlobals immutable _GLOBALS;\n    RendererStorage immutable _storage;\n    IFont immutable _font;\n\n    constructor(IGlobals globals, RendererStorage rendererStorage, IFont font) {\n        _GLOBALS = globals;\n        _storage = rendererStorage;\n        _font = font;\n    }\n\n    function contractURI() external view virtual returns (string memory);\n\n    function getCustomizationChoices() internal view returns (bool isDarkMode, Color color) {\n        // Get the customization preset ID chosen by the crowdfund or party instance.\n        uint256 presetId = _storage.getPresetFor(address(this));\n\n        if (presetId == 0) {\n            // Preset ID 0 is reserved. It is used to indicate to party instances\n            // to use the same customization preset as the crowdfund.\n            (bool success, bytes memory result) = address(this).staticcall(\n                // Call mintAuthority\n                abi.encodeWithSignature(\"mintAuthority()\")\n            );\n\n            if (success && result.length == 32) {\n                address crowdfund = abi.decode(result, (address));\n                // Should return the crowdfund used to create the party, if the\n                // party was created conventionally. Use the customization\n                // preset chosen during crowdfund initialization.\n                presetId = _storage.getPresetFor(crowdfund);\n                // If the preset ID is still 0 (this shouldn't happen), fallback\n                // to the default customization options.\n                if (presetId == 0) return (false, Color.DEFAULT);\n            } else {\n                // Fallback to the default customization options. May happen if\n                // called from a non-party contract (e.g. a crowdfund contract,\n                // although this shouldn't happen).\n                return (false, Color.DEFAULT);\n            }\n        }\n\n        // Get the customization data for the preset chosen.\n        bytes memory customizationData = _storage.customizationPresets(presetId);\n        if (customizationData.length == 0) {\n            // If the customization preset doesn't exist, fallback to the\n            // default customization options.\n            return (false, Color.DEFAULT);\n        }\n\n        // Check version number. Fallback to default if using different version.\n        if (abi.decode(customizationData, (uint8)) == 1) {\n            (, isDarkMode, color) = abi.decode(customizationData, (uint8, bool, Color));\n        } else {\n            // Fallback to the default customization options.\n            return (false, Color.DEFAULT);\n        }\n    }\n}\n"
      },
      "contracts/renderers/RendererStorage.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"solmate/utils/SSTORE2.sol\";\nimport \"../utils/Multicall.sol\";\n\ncontract RendererStorage is Multicall {\n    error AlreadySetError();\n    error NotOwnerError(address caller, address owner);\n\n    event OwnershipTransferred(address previousOwner, address newOwner);\n\n    uint256 constant CROWDFUND_CARD_DATA = 0;\n    uint256 constant PARTY_CARD_DATA = 1;\n\n    /// @notice Address allowed to store new data.\n    address public owner;\n\n    /// @notice Customization presets by ID, used for rendering cards. Begins at\n    ///         1, 0 is reserved to indicate in `getPresetFor()` that a\n    ///         party instance use the preset set by the crowdfund instance that\n    ///         created it.\n    mapping(uint256 => bytes) public customizationPresets;\n    /// @notice Customization preset used by a crowdfund or party instance.\n    mapping(address => uint256) public getPresetFor;\n    /// @notice Addresses where URI data chunks are stored.\n    mapping(uint256 => address) public files;\n\n    modifier onlyOwner() {\n        address owner_ = owner;\n        if (msg.sender != owner_) {\n            revert NotOwnerError(msg.sender, owner_);\n        }\n\n        _;\n    }\n\n    constructor(address _owner) {\n        // Set the address allowed to write new data.\n        owner = _owner;\n\n        // Write URI data used by V1 of the renderers:\n\n        files[CROWDFUND_CARD_DATA] = SSTORE2.write(\n            bytes(\n                '<path class=\"o\" d=\"M118.4 419.5h5.82v1.73h-4.02v1.87h3.74v1.73h-3.74v1.94h4.11v1.73h-5.91v-9Zm9.93 1.76h-2.6v-1.76h7.06v1.76h-2.61v7.24h-1.85v-7.24Zm6.06-1.76h1.84v3.55h3.93v-3.55H142v9h-1.84v-3.67h-3.93v3.67h-1.84v-9Z\"/><path class=\"o\" d=\"M145 413a4 4 0 0 1 4 4v14a4 4 0 0 1-4 4H35a4 4 0 0 1-4-4v-14a4 4 0 0 1 4-4h110m0-1H35a5 5 0 0 0-5 5v14a5 5 0 0 0 5 5h110a5 5 0 0 0 5-5v-14a5 5 0 0 0-5-5Z\"/><path d=\"M239.24 399.83h3.04c1.7 0 2.82 1 2.82 2.55 0 2.1-1.27 3.32-3.57 3.32h-1.97l-.71 3.3h-1.56l1.96-9.17Zm2.34 4.38c1.23 0 1.88-.58 1.88-1.68 0-.73-.49-1.2-1.48-1.2h-1.51l-.6 2.88h1.7Zm3.57 1.86c0-2.27 1.44-3.83 3.57-3.83 1.82 0 3.06 1.25 3.06 3.09 0 2.28-1.43 3.83-3.57 3.83-1.82 0-3.06-1.25-3.06-3.09Zm3.13 1.74c1.19 0 1.93-1.02 1.93-2.52 0-1.06-.62-1.69-1.56-1.69-1.19 0-1.93 1.02-1.93 2.52 0 1.06.62 1.69 1.56 1.69Zm4.74-5.41h1.49l.28 4.73 2.25-4.73h1.64l.23 4.77 2.25-4.77h1.56l-3.3 6.61h-1.62l-.25-5.04-2.42 5.04h-1.63l-.48-6.61Zm9.54 3.66c0-2.27 1.45-3.81 3.6-3.81 2 0 3.05 1.58 2.33 3.92h-4.46c0 1.1.81 1.68 2.05 1.68.8 0 1.45-.2 2.1-.59l-.31 1.46a4.2 4.2 0 0 1-2.04.44c-2.06 0-3.26-1.19-3.26-3.11Zm4.7-1.07c.12-.86-.31-1.46-1.22-1.46s-1.57.61-1.82 1.46h3.05Zm3.46-2.59h1.55l-.28 1.28c.81-1.7 2.56-1.36 2.77-1.29l-.35 1.46c-.18-.06-2.3-.63-2.82 1.68l-.74 3.48h-1.55l1.42-6.61Zm3.91 3.66c0-2.27 1.45-3.81 3.6-3.81 2 0 3.05 1.58 2.33 3.92h-4.46c0 1.1.81 1.68 2.05 1.68.8 0 1.45-.2 2.1-.59l-.31 1.46a4.2 4.2 0 0 1-2.04.44c-2.06 0-3.26-1.19-3.26-3.11Zm4.7-1.07c.12-.86-.31-1.46-1.22-1.46s-1.57.61-1.82 1.46h3.05Zm2.25 1.36c0-2.44 1.36-4.1 3.26-4.1 1 0 1.76.53 2.05 1.31l.79-3.72h1.55l-1.96 9.17h-1.55l.2-.92a2.15 2.15 0 0 1-1.92 1.08c-1.49 0-2.43-1.18-2.43-2.82Zm3 1.51c.88 0 1.51-.58 1.73-1.56l.17-.81c.24-1.1-.31-1.93-1.36-1.93-1.19 0-1.94 1.08-1.94 2.59 0 1.06.55 1.71 1.4 1.71Zm9.6-.01-.25 1.16h-1.55l1.96-9.17h1.55l-.73 3.47a2.35 2.35 0 0 1 1.99-1.05c1.49 0 2.35 1.16 2.35 2.76 0 2.52-1.36 4.16-3.21 4.16-.98 0-1.81-.53-2.1-1.32Zm1.83.01c1.16 0 1.87-1.06 1.87-2.61 0-1.04-.5-1.69-1.39-1.69s-1.52.56-1.73 1.55l-.17.79c-.24 1.14.34 1.97 1.42 1.97Zm5.68 1.16-1.04-6.62h1.52l.66 4.75 2.66-4.75h1.69l-5.31 9.13h-1.73l1.55-2.51Zm23.48-6.8a42.14 42.14 0 0 0-.75 6.01 43.12 43.12 0 0 0 5.58 2.35 42.54 42.54 0 0 0 5.58-2.35 45.32 45.32 0 0 0-.75-6.01c-.91-.79-2.6-2.21-4.83-3.66a42.5 42.5 0 0 0-4.83 3.66Zm13.07-7.95s.82-.29 1.76-.45a14.9 14.9 0 0 0-9.53-3.81c.66.71 1.28 1.67 1.84 2.75 1.84.22 4.07.7 5.92 1.51Zm-2.71 18.36c-2.06-.4-4.05-.97-5.53-1.51a38.65 38.65 0 0 1-5.53 1.51c.12 1.5.35 3.04.76 4.58 0 0 1.54 1.82 4.78 2.8 3.23-.98 4.78-2.8 4.78-2.8.4-1.53.64-3.08.76-4.58Zm-13.77-18.37a22.3 22.3 0 0 1 5.93-1.51 12.4 12.4 0 0 1 1.84-2.75 14.97 14.97 0 0 0-9.53 3.81c.95.16 1.76.45 1.76.45Zm-4.72 8.77a25.74 25.74 0 0 0 3.58 2.94 37.48 37.48 0 0 1 4.08-4.04c.27-1.56.77-3.57 1.46-5.55a25.24 25.24 0 0 0-4.34-1.63s-2.35.42-4.81 2.74c-.77 3.29.04 5.54.04 5.54Zm25.92 0s.81-2.25.04-5.54c-2.46-2.31-4.81-2.74-4.81-2.74-1.53.42-2.99.99-4.34 1.63a37.79 37.79 0 0 1 1.46 5.55 37.44 37.44 0 0 1 4.08 4.04 25.86 25.86 0 0 0 3.58-2.94Zm-26.38.2s-.66-.56-1.27-1.3c-.7 3.34-.27 6.93 1.46 10.16.28-.93.8-1.94 1.46-2.97a22.32 22.32 0 0 1-1.66-5.88Zm8.24 14.27a22.07 22.07 0 0 1-4.27-4.38c-1.22.06-2.36 0-3.3-.22a14.91 14.91 0 0 0 8.07 6.34c-.34-.9-.5-1.75-.5-1.75Zm18.6-14.27s.66-.56 1.27-1.3c.7 3.34.27 6.93-1.46 10.16-.28-.93-.8-1.94-1.46-2.97a22.32 22.32 0 0 0 1.66-5.88Zm-8.24 14.27a22.07 22.07 0 0 0 4.27-4.38c1.22.06 2.36 0 3.3-.22a14.91 14.91 0 0 1-8.07 6.34c.34-.9.5-1.75.5-1.75ZM330 391.84l-4.12 2.45 1.26 3.91h5.72l1.26-3.91-4.12-2.45Zm-11.4 19.74 4.18 2.35 2.75-3.05-2.86-4.95-4.02.86-.06 4.79Zm22.79 0-.06-4.79-4.02-.86-2.86 4.95 2.75 3.05 4.18-2.35Z\" style=\"fill:#00c1fa\"/><use height=\"300\" transform=\"matrix(1 0 0 .09 29.85 444)\" width=\"300.15\" xlink:href=\"#a\"/><use height=\"21.15\" transform=\"translate(30 446.92)\" width=\"300\" xlink:href=\"#b\"/><g><path d=\"m191.54 428.67-28.09-24.34A29.98 29.98 0 0 0 143.8 397H30a15 15 0 0 0-15 15v98a15 15 0 0 0 15 15h300a15 15 0 0 0 15-15v-59a15 15 0 0 0-15-15H211.19a30 30 0 0 1-19.65-7.33Z\" style=\"fill:url(#i)\"/></g></svg>'\n            )\n        );\n\n        files[PARTY_CARD_DATA] = SSTORE2.write(\n            bytes(\n                ' d=\"M188 444.3h2.4l2.6 8.2 2.7-8.2h2.3l-3.7 10.7h-2.8l-3.5-10.7zm10.5 5.3c0-3.2 2.2-5.6 5.3-5.6 3.1 0 5.3 2.3 5.3 5.6 0 3.2-2.2 5.5-5.3 5.5-3.1.1-5.3-2.2-5.3-5.5zm5.3 3.5c1.8 0 3-1.3 3-3.4 0-2.1-1.1-3.5-3-3.5s-3 1.3-3 3.5c0 2.1 1.1 3.4 3 3.4zm8.7-6.7h-3.1v-2.1h8.4v2.1h-3.1v8.6h-2.2v-8.6zm6.9-2.1h2.2V455h-2.2v-10.7zm4.3 0h2.9l4 8.2v-8.2h2.1V455h-2.9l-4-8.2v8.2h-2.1v-10.7zm10.6 5.4c0-3.4 2.3-5.6 6-5.6 1.2 0 2.3.2 3.1.6v2.3c-.9-.6-1.9-.8-3.1-.8-2.4 0-3.8 1.3-3.8 3.5 0 2.1 1.3 3.4 3.5 3.4.5 0 .9-.1 1.3-.2v-2.2h-2.2v-1.9h4.3v5.6c-1 .5-2.2.8-3.4.8-3.5 0-5.7-2.2-5.7-5.5zm15.1-5.4h4.3c2.3 0 3.7 1.3 3.7 3.5s-1.4 3.5-3.7 3.5h-2.1v3.7h-2.2v-10.7zm4.1 5c1.1 0 1.6-.5 1.6-1.5s-.5-1.5-1.6-1.5h-1.9v2.9h1.9zm4.8.3c0-3.2 2.2-5.6 5.3-5.6 3.1 0 5.3 2.3 5.3 5.6 0 3.2-2.2 5.5-5.3 5.5-3.1.1-5.3-2.2-5.3-5.5zm5.3 3.5c1.8 0 3-1.3 3-3.4 0-2.1-1.1-3.5-3-3.5s-3 1.3-3 3.5c0 2.1 1.1 3.4 3 3.4zm5.8-8.8h2.3l1.7 7.8 1.9-7.8h2.4l1.8 7.8 1.8-7.8h2.3l-2.7 10.7h-2.5l-1.9-8.2-1.8 8.2h-2.5l-2.8-10.7zm15.4 0h6.9v2.1H287v2.2h4.5v2.1H287v2.3h4.9v2.1h-7v-10.8zm9 0h4.5c2 0 3.3 1.3 3.3 3.2 0 1.9-1.2 3.1-3 3.2l3.5 4.3h-2.7l-3.5-4.4v4.4h-2.1v-10.7zm4.1 4.8c1 0 1.5-.5 1.5-1.4 0-.9-.6-1.4-1.5-1.4h-2v2.9h2zM30 444.3h4.3c3 0 5.2 2.1 5.2 5.4s-2.1 5.4-5.2 5.4H30v-10.8zm4 8.6c2.1 0 3.2-1.2 3.2-3.2s-1.2-3.3-3.2-3.3h-1.8v6.5H34zm7.7-8.6h2.2V455h-2.2v-10.7zm4.8 10V452c1 .7 2.1 1.1 3.2 1.1s1.7-.5 1.7-1.2-.4-1-1.2-1.2l-1.2-.3c-1.8-.5-2.7-1.5-2.7-3.1 0-2 1.5-3.2 3.9-3.2 1 0 2.1.2 2.9.7v2.3c-.9-.6-1.9-.8-3-.8-.9 0-1.6.4-1.6 1.1 0 .6.4.9 1.2 1.1l1.3.4c1.8.5 2.6 1.4 2.6 3.1 0 2.1-1.5 3.4-3.8 3.4-1.1-.2-2.3-.5-3.3-1.1zm12-7.9h-3.1v-2.1h8.4v2.1h-3.1v8.6h-2.2v-8.6zm7.5-2.1h4.5c2 0 3.3 1.3 3.3 3.2 0 1.9-1.2 3.1-3 3.2l3.5 4.3h-2.7l-3.5-4.4v4.4H66v-10.7zm4.1 4.8c1 0 1.5-.5 1.5-1.4s-.6-1.4-1.5-1.4h-2v2.9h2zm6.1-4.8h2.2V455h-2.2v-10.7zm5 0h4.5c2 0 3.2 1.1 3.2 2.8 0 1.1-.5 1.9-1.4 2.3 1.1.3 1.8 1.3 1.8 2.5 0 1.9-1.3 3.1-3.5 3.1h-4.6v-10.7zm4.2 4.4c.9 0 1.4-.5 1.4-1.3s-.5-1.3-1.4-1.3h-2.1v2.5l2.1.1zm.3 4.4c.9 0 1.5-.5 1.5-1.3s-.6-1.3-1.5-1.3h-2.4v2.6h2.4zm5.7-2.5v-6.3h2.2v6.3c0 1.6.9 2.5 2.3 2.5s2.3-.9 2.3-2.5v-6.3h2.2v6.3c0 2.9-1.7 4.6-4.5 4.6s-4.6-1.7-4.5-4.6zm14.2-4.2h-3.1v-2.1h8.4v2.1h-3.1v8.6h-2.2v-8.6zm7.5-2.1h2.2V455h-2.2v-10.7zm4.5 5.3c0-3.2 2.2-5.6 5.3-5.6s5.3 2.3 5.3 5.6-2.2 5.5-5.3 5.5-5.3-2.2-5.3-5.5zm5.3 3.5c1.8 0 3-1.3 3-3.5s-1.2-3.5-3-3.5-3 1.3-3 3.5 1.1 3.5 3 3.5zm7.5-8.8h2.9l4 8.2v-8.2h2.1V455h-2.9l-4-8.2v8.2h-2.1v-10.7zm11.7 10V452c1 .7 2.1 1.1 3.2 1.1s1.7-.5 1.7-1.2-.4-1-1.2-1.2l-1.2-.3c-1.8-.5-2.6-1.5-2.6-3.1 0-2 1.5-3.2 3.9-3.2 1.1 0 2.1.2 2.9.7v2.3c-.9-.6-1.9-.8-3-.8-.9 0-1.6.4-1.6 1.1 0 .6.4.9 1.2 1.1l1.3.4c1.8.5 2.6 1.4 2.6 3.1 0 2.1-1.5 3.4-3.8 3.4a9.7 9.7 0 0 1-3.4-1.1zM30 259.3h4.3c2.2 0 3.7 1.3 3.7 3.5s-1.4 3.5-3.7 3.5h-2.1v3.7H30v-10.7zm4.1 5c1.1 0 1.6-.5 1.6-1.5s-.5-1.5-1.6-1.5h-1.9v2.9h1.9zm6.1-5h4.5c2 0 3.3 1.3 3.3 3.2 0 1.9-1.2 3.1-3 3.2l3.5 4.3h-2.7l-3.5-4.4v4.4h-2.1v-10.7zm4.1 4.8c1 0 1.5-.5 1.5-1.4s-.6-1.4-1.5-1.4h-2v2.9h2zm5.4.5c0-3.2 2.2-5.6 5.3-5.6s5.3 2.3 5.3 5.6-2.2 5.5-5.3 5.5-5.3-2.2-5.3-5.5zm5.3 3.5c1.8 0 3-1.3 3-3.5s-1.2-3.5-3-3.5-3 1.3-3 3.5 1.1 3.5 3 3.5zm7.6-8.8h4.3c2.2 0 3.7 1.3 3.7 3.5s-1.4 3.5-3.7 3.5h-2.1v3.7h-2.2v-10.7zm4.1 5c1.1 0 1.6-.5 1.6-1.5s-.6-1.5-1.6-1.5h-1.9v2.9h1.9zm5.4.4c0-3.2 2.2-5.6 5.3-5.6s5.3 2.3 5.3 5.6-2.2 5.5-5.3 5.5-5.3-2.3-5.3-5.5zm5.4 3.4c1.8 0 3-1.3 3-3.5s-1.2-3.5-3-3.5-3 1.3-3 3.5 1.1 3.5 3 3.5zm7.2 1.2V267c1 .7 2.1 1.1 3.2 1.1s1.7-.5 1.7-1.2-.4-1-1.2-1.2l-1.2-.3c-1.8-.5-2.7-1.5-2.7-3.1 0-2 1.5-3.2 3.9-3.2 1.1 0 2.1.2 2.9.7v2.3c-.9-.6-1.9-.8-3-.8-.9 0-1.6.4-1.6 1.1 0 .6.4.9 1.2 1.1l1.3.4c1.8.5 2.6 1.4 2.6 3.1 0 2.1-1.5 3.4-3.8 3.4-1.1-.2-2.3-.5-3.3-1.1zm12.2-10h2.8l3.7 10.7h-2.3l-.8-2.5h-4l-.8 2.5h-2.2l3.6-10.7zm2.8 6.3-1.4-4.2-1.4 4.2h2.8zm5.7-6.3h2.2v8.6h4.7v2.1h-6.9v-10.7zm9.1 10V267c1 .7 2.1 1.1 3.2 1.1s1.7-.5 1.7-1.2-.4-1-1.2-1.2l-1.2-.3c-1.8-.5-2.7-1.5-2.7-3.1 0-2 1.5-3.2 3.9-3.2 1.1 0 2.1.2 2.9.7v2.3c-.9-.6-1.9-.8-3-.8-.9 0-1.6.4-1.6 1.1 0 .6.4.9 1.2 1.1l1.3.4c1.8.5 2.6 1.4 2.6 3.1 0 2.1-1.5 3.4-3.8 3.4-1.1-.2-2.3-.5-3.3-1.1zm-84.5-70h2.9l4 8.2v-8.2H39V210h-2.9l-4-8.2v8.2H30v-10.7zm14.7 0h2.8l3.7 10.7h-2.3l-.8-2.6h-4l-.8 2.6H41l3.7-10.7zm2.8 6.2-1.4-4.2-1.4 4.2h2.8zm5.7-6.2h3.3l2.5 8.2 2.5-8.2h3.3V210h-2v-8.6L60 210h-2.1l-2.7-8.5v8.5h-2v-10.7zm14.4 0h6.9v2.1h-4.8v2.2h4.4v2.1h-4.4v2.3h4.9v2.1h-7v-10.8z\" /><path d=\"M239.24 24.83h3.04c1.7 0 2.82 1 2.82 2.55 0 2.1-1.27 3.32-3.57 3.32h-1.97l-.71 3.3h-1.56l1.96-9.17Zm2.34 4.38c1.23 0 1.88-.58 1.88-1.68 0-.73-.49-1.2-1.48-1.2h-1.51l-.6 2.88h1.7Zm3.57 1.86c0-2.27 1.44-3.83 3.57-3.83 1.82 0 3.06 1.25 3.06 3.09 0 2.28-1.43 3.83-3.57 3.83-1.82 0-3.06-1.25-3.06-3.09Zm3.13 1.74c1.19 0 1.93-1.02 1.93-2.52 0-1.06-.62-1.69-1.56-1.69-1.19 0-1.93 1.02-1.93 2.52 0 1.06.62 1.69 1.56 1.69Zm4.74-5.41h1.49l.28 4.73 2.25-4.73h1.64l.23 4.77 2.25-4.77h1.56l-3.3 6.61h-1.62l-.25-5.04-2.42 5.04h-1.63l-.48-6.61Zm9.54 3.66c0-2.27 1.45-3.81 3.6-3.81 2 0 3.05 1.58 2.33 3.92h-4.46c0 1.1.81 1.68 2.05 1.68.8 0 1.45-.2 2.1-.59l-.31 1.46a4.2 4.2 0 0 1-2.04.44c-2.06 0-3.26-1.19-3.26-3.11Zm4.7-1.07c.12-.86-.31-1.46-1.22-1.46s-1.57.61-1.82 1.46h3.05Zm3.46-2.59h1.55l-.28 1.28c.81-1.7 2.56-1.36 2.77-1.29l-.35 1.46c-.18-.06-2.3-.63-2.82 1.68l-.74 3.48h-1.55l1.42-6.61Zm3.91 3.66c0-2.27 1.45-3.81 3.6-3.81 2 0 3.05 1.58 2.33 3.92h-4.46c0 1.1.81 1.68 2.05 1.68.8 0 1.45-.2 2.1-.59l-.31 1.46a4.2 4.2 0 0 1-2.04.44c-2.06 0-3.26-1.19-3.26-3.11Zm4.7-1.07c.12-.86-.31-1.46-1.22-1.46s-1.57.61-1.82 1.46h3.05Zm2.25 1.36c0-2.44 1.36-4.1 3.26-4.1 1 0 1.76.53 2.05 1.31l.79-3.72h1.55l-1.96 9.17h-1.55l.2-.92a2.15 2.15 0 0 1-1.92 1.08c-1.49 0-2.43-1.18-2.43-2.82Zm3 1.51c.88 0 1.51-.58 1.73-1.56l.17-.81c.24-1.1-.31-1.93-1.36-1.93-1.19 0-1.94 1.08-1.94 2.59 0 1.06.55 1.71 1.4 1.71Zm9.6-.01-.25 1.16h-1.55l1.96-9.17h1.55l-.73 3.47a2.35 2.35 0 0 1 1.99-1.05c1.49 0 2.35 1.16 2.35 2.76 0 2.52-1.36 4.16-3.21 4.16-.98 0-1.81-.53-2.1-1.32Zm1.83.01c1.16 0 1.87-1.06 1.87-2.61 0-1.04-.5-1.69-1.39-1.69s-1.52.56-1.73 1.55l-.17.79c-.24 1.14.34 1.97 1.42 1.97Zm5.68 1.16-1.04-6.62h1.52l.66 4.75 2.66-4.75h1.69l-5.31 9.13h-1.73l1.55-2.51Zm23.47-6.8c.91-.79 2.6-2.21 4.83-3.66a42.5 42.5 0 0 1 4.83 3.66c.23 1.18.62 3.36.75 6.01a43.12 43.12 0 0 1-5.58 2.35 42.54 42.54 0 0 1-5.58-2.35c.14-2.65.53-4.83.75-6.01Zm13.07-7.95s.82-.29 1.76-.45a14.9 14.9 0 0 0-9.53-3.81c.66.71 1.28 1.67 1.84 2.75 1.84.22 4.07.7 5.92 1.51Zm-2.71 18.36c-2.06-.4-4.05-.97-5.53-1.51a38.65 38.65 0 0 1-5.53 1.51c.12 1.5.35 3.04.76 4.58 0 0 1.54 1.82 4.78 2.8 3.23-.98 4.78-2.8 4.78-2.8.4-1.53.64-3.08.76-4.58Zm-13.77-18.37a22.3 22.3 0 0 1 5.93-1.51 12.4 12.4 0 0 1 1.84-2.75 14.97 14.97 0 0 0-9.53 3.81c.95.16 1.76.45 1.76.45Zm-4.72 8.77a25.74 25.74 0 0 0 3.58 2.94 37.48 37.48 0 0 1 4.08-4.04c.27-1.56.77-3.57 1.46-5.55a25.24 25.24 0 0 0-4.34-1.63s-2.35.42-4.81 2.74c-.77 3.29.04 5.54.04 5.54Zm25.92 0s.81-2.25.04-5.54c-2.46-2.31-4.81-2.74-4.81-2.74-1.53.42-2.99.99-4.34 1.63a37.79 37.79 0 0 1 1.46 5.55 37.44 37.44 0 0 1 4.08 4.04 25.86 25.86 0 0 0 3.58-2.94Zm-26.38.2s-.66-.56-1.27-1.3c-.7 3.34-.27 6.93 1.46 10.16.28-.93.8-1.94 1.46-2.97a22.32 22.32 0 0 1-1.66-5.88Zm8.24 14.27a22.07 22.07 0 0 1-4.27-4.38c-1.22.06-2.36 0-3.3-.22a14.91 14.91 0 0 0 8.07 6.34c-.34-.9-.5-1.75-.5-1.75Zm18.6-14.27s.66-.56 1.27-1.3c.7 3.34.27 6.93-1.46 10.16-.28-.93-.8-1.94-1.46-2.97a22.32 22.32 0 0 0 1.66-5.88Zm-8.24 14.27a22.07 22.07 0 0 0 4.27-4.38c1.22.06 2.36 0 3.3-.22a14.91 14.91 0 0 1-8.07 6.34c.34-.9.5-1.75.5-1.75Zm-5.18-25.66-4.12 2.45 1.26 3.91h5.72l1.26-3.91-4.12-2.45Zm-11.4 19.74 4.18 2.35 2.75-3.05-2.86-4.95-4.02.86-.06 4.79Zm22.79 0-.06-4.79-4.02-.86-2.86 4.95 2.75 3.05 4.18-2.35Z\" style=\"fill:#00c1fa\"/><path d=\"M106.67 109.1a304.9 304.9 0 0 0-3.72-10.89c5.04-5.53 35.28-40.74 24.54-68.91 10.57 10.67 8.19 28.85 3.59 41.95-4.79 13.14-13.43 26.48-24.4 37.84Zm30.89 20.82c-5.87 6.12-20.46 17.92-21.67 18.77a99.37 99.37 0 0 0 7.94 6.02 133.26 133.26 0 0 0 20.09-18.48 353.47 353.47 0 0 0-6.36-6.31Zm-29.65-16.74a380.9 380.9 0 0 1 3.13 11.56c-4.8-1.37-8.66-2.53-12.36-3.82a123.4 123.4 0 0 1-21.16 13.21l15.84 5.47c14.83-8.23 28.13-20.82 37.81-34.68 0 0 8.56-12.55 12.42-23.68 2.62-7.48 4.46-16.57 3.49-24.89-2.21-12.27-6.95-15.84-9.32-17.66 6.16 5.72 3.25 27.8-2.79 39.89-6.08 12.16-15.73 24.27-27.05 34.59Zm59.05-37.86c-.03 7.72-3.05 15.69-6.44 22.69 1.7 2.2 3.18 4.36 4.42 6.49 7.97-16.51 3.74-26.67 2.02-29.18ZM61.18 128.51l12.5 4.3a101.45 101.45 0 0 0 21.42-13.19 163.26 163.26 0 0 1-10.61-4.51 101.28 101.28 0 0 1-23.3 13.4Zm87.78-42.73c.86.77 5.44 5.18 6.75 6.59 6.39-16.61.78-28.86-1.27-30.56.72 8.05-2.02 16.51-5.48 23.98Zm-14.29 40.62-2.47-15.18a142.42 142.42 0 0 1-35.74 29.45c6.81 2.36 12.69 4.4 15.45 5.38a115.98 115.98 0 0 0 22.75-19.66Zm-42.62 34.73c4.48 2.93 12.94 4.24 18.8 1.23 6.03-3.84-.6-8.34-8.01-9.88-9.8-2.03-16.82 1.22-13.4 6.21.41.6 1.19 1.5 2.62 2.44m-1.84.4c-3.56-2.37-6.77-7.2-.23-10.08 10.41-3.43 28.39 3.2 24.99 9.22-.58 1.04-1.46 1.6-2.38 2.19h-.03v.02h-.03v.02h-.03c-7.04 3.65-17.06 2.13-22.3-1.36m5.48-3.86a4.94 4.94 0 0 0 5.06.49l1.35-.74-4.68-2.38-1.47.79c-.38.22-1.53.88-.26 1.84m-1.7.59c-2.35-1.57-.78-2.61-.02-3.11 1.09-.57 2.19-1.15 3.28-1.77 6.95 3.67 7.22 3.81 13.19 6.17l-1.38.81c-1.93-.78-4.52-1.82-6.42-2.68.86 1.4 1.99 3.27 2.9 4.64l-1.68.87c-.75-1.28-1.76-2.99-2.47-4.29-3.19 2.06-6.99-.36-7.42-.64\" style=\"fill:url(#f2)\"/><path d=\"M159.13 52.37C143.51 24.04 119.45 15 103.6 15c-11.92 0-25.97 5.78-36.84 13.17 9.54 4.38 21.86 15.96 22.02 16.11-7.94-3.05-17.83-6.72-33.23-7.87a135.1 135.1 0 0 0-19.77 20.38c.77 7.66 2.88 15.68 2.88 15.68-6.28-4.75-11.02-4.61-18 9.45-5.4 12.66-6.93 24.25-4.65 33.18 0 0 4.72 26.8 36.23 40.07-1.3-4.61-1.58-9.91-.93-15.73a87.96 87.96 0 0 1-15.63-9.87c.79-6.61 2.79-13.82 6-21.36 4.42-10.66 4.35-15.14 4.35-15.19.03.07 5.48 12.43 12.95 22.08 4.23-8.84 9.46-16.08 13.67-21.83l-3.77-6.75a143.73 143.73 0 0 1 18.19-18.75c2.05 1.07 4.79 2.47 6.84 3.58 8.68-7.27 19.25-14.05 30.56-18.29-7-11.49-16.02-19.27-16.02-19.27s27.7 2.74 42.02 15.69a25.8 25.8 0 0 1 8.65 2.89ZM28.58 107.52a70.1 70.1 0 0 0-2.74 12.52 55.65 55.65 0 0 1-6.19-8.84 69.17 69.17 0 0 1 2.65-12.1c1.77-5.31 3.35-5.91 5.86-2.23v-.05c2.14 3.07 1.81 6.14.42 10.7ZM61.69 72.2l-.05.05a221.85 221.85 0 0 1-7.77-18.1l.14-.14a194.51 194.51 0 0 1 18.56 6.98 144.44 144.44 0 0 0-10.88 11.22Zm54.84-47.38c-4.42.7-9.02 1.95-13.67 3.72a65.03 65.03 0 0 0-7.81-5.31 66.04 66.04 0 0 1 13.02-3.54c1.53-.19 6.23-.79 10.32 2.42v-.05c2.47 1.91.14 2.37-1.86 2.75Z\" style=\"fill:url(#h)\"/>'\n            )\n        );\n    }\n\n    /// @notice Transfer ownership to a new owner.\n    /// @param newOwner The address to transfer ownership to.\n    function transferOwnership(address newOwner) external onlyOwner {\n        emit OwnershipTransferred(owner, newOwner);\n        owner = newOwner;\n    }\n\n    /// @notice Write data to be accessed by a given file key.\n    /// @param key The key to access the written data.\n    /// @param data The data to be written.\n    function writeFile(uint256 key, string memory data) external onlyOwner {\n        files[key] = SSTORE2.write(bytes(data));\n    }\n\n    /// @notice Read data using a given file key.\n    /// @param key The key to access the stored data.\n    /// @return data The data stored at the given key.\n    function readFile(uint256 key) external view returns (string memory data) {\n        return string(SSTORE2.read(files[key]));\n    }\n\n    /// @notice Create or set a customization preset for renderers to use.\n    /// @param id The ID of the customization preset.\n    /// @param customizationData Data decoded by renderers used to render the SVG according to the preset.\n    function createCustomizationPreset(\n        uint256 id,\n        bytes memory customizationData\n    ) external onlyOwner {\n        customizationPresets[id] = customizationData;\n    }\n\n    /// @notice For crowdfund or party instances to set the customization preset they want to use.\n    /// @param id The ID of the customization preset.\n    function useCustomizationPreset(uint256 id) external {\n        getPresetFor[msg.sender] = id;\n    }\n}\n"
      },
      "contracts/renderers/MetadataRegistry.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport { IGlobals } from \"../globals/IGlobals.sol\";\nimport { LibGlobals } from \"../globals/LibGlobals.sol\";\nimport { IMetadataProvider } from \"./IMetadataProvider.sol\";\nimport { Multicall } from \"../utils/Multicall.sol\";\n\n/// @notice A registry of custom metadata providers for Party Cards.\ncontract MetadataRegistry is Multicall {\n    event ProviderSet(address indexed instance, IMetadataProvider indexed provider);\n    event RegistrarSet(address indexed registrar, address indexed instance, bool canSetData);\n\n    error NotAuthorized(address caller, address instance);\n\n    // The `Globals` contract storing global configuration values. This contract\n    // is immutable and it’s address will never change.\n    IGlobals private immutable _GLOBALS;\n\n    /// @notice Get the metadata provider for a Party instance.\n    mapping(address instance => IMetadataProvider provider) public getProvider;\n\n    /// @notice Whether or not an address is a registar that can set the\n    ///         provider and metadata for another instance. If registrar is set\n    ///         true for `address(1)`, the address is a universal registar and\n    ///         can set data for any instance.\n    /// @dev Registrars' ability to set metadata for another instance must also be\n    ///      supported by the metadata provider used by that instance, indicated by\n    ///      `IMetadataProvider.supportsRegistrars()`.\n    mapping(address registrar => mapping(address instance => bool canSetData)) private _isRegistrar;\n\n    /// @param globals The address of the `Globals` contract.\n    /// @param registrars The addresses of the initial universal registrars.\n    constructor(IGlobals globals, address[] memory registrars) {\n        _GLOBALS = globals;\n\n        // Set the initial universal registrars.\n        for (uint256 i = 0; i < registrars.length; i++) {\n            _isRegistrar[registrars[i]][address(1)] = true;\n        }\n    }\n\n    /// @notice Set the metadata provider for a Party instance.\n    /// @param instance The address of the instance.\n    /// @param provider The address of the metadata provider.\n    function setProvider(address instance, IMetadataProvider provider) external {\n        // Check if the caller is authorized to set the provider for the instance.\n        if (!isRegistrar(msg.sender, instance)) revert NotAuthorized(msg.sender, instance);\n\n        getProvider[instance] = provider;\n\n        emit ProviderSet(instance, provider);\n    }\n\n    /// @notice Set whether or not an address can set metadata for a Party instance.\n    /// @param registrar The address of the possible registrar.\n    /// @param instance The address of the instance the registrar can set\n    ///                 metadata for.\n    /// @param canSetData Whether or not the address can set data for the instance.\n    function setRegistrar(address registrar, address instance, bool canSetData) external {\n        if (\n            msg.sender != instance &&\n            msg.sender != _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET)\n        ) {\n            revert NotAuthorized(msg.sender, instance);\n        }\n\n        _isRegistrar[registrar][instance] = canSetData;\n\n        emit RegistrarSet(registrar, instance, canSetData);\n    }\n\n    /// @notice Get whether or not an address can set metadata for a Party instance.\n    /// @param registrar The address of the possible registrar.\n    /// @param instance The address of the instance the registrar can set\n    ///                 metadata for.\n    /// @return canSetData Whether or not the address can set data for the instance.\n    function isRegistrar(address registrar, address instance) public view returns (bool) {\n        return\n            registrar == instance ||\n            _isRegistrar[registrar][address(1)] ||\n            _isRegistrar[registrar][instance];\n    }\n\n    /// @notice Get the metadata for a Party instance.\n    /// @param instance The address of the instance.\n    /// @param tokenId The ID of the token to get the metadata for.\n    /// @return metadata The encoded metadata.\n    function getMetadata(address instance, uint256 tokenId) external view returns (bytes memory) {\n        IMetadataProvider provider = getProvider[instance];\n\n        return\n            address(provider) != address(0) ? provider.getMetadata(instance, tokenId) : bytes(\"\");\n    }\n}\n"
      },
      "contracts/renderers/IMetadataRegistry1_1.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.20;\n\n// Interface for `MetadataRegistry` contract from v1.1 of the protocol.\ninterface IMetadataRegistry1_1 {\n    function customPartyMetadataByCrowdfund(\n        address crowdfundAddress\n    )\n        external\n        view\n        returns (\n            string memory customName,\n            string memory customDescription,\n            string memory customImage\n        );\n}\n"
      },
      "contracts/party/PartyGovernance.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport { ITokenDistributor } from \"../distribution/ITokenDistributor.sol\";\nimport { ReadOnlyDelegateCall } from \"../utils/ReadOnlyDelegateCall.sol\";\nimport { IERC721 } from \"../tokens/IERC721.sol\";\nimport { IERC20 } from \"../tokens/IERC20.sol\";\nimport { IERC721Receiver } from \"../tokens/IERC721Receiver.sol\";\nimport { ERC1155TokenReceiverBase } from \"../vendor/solmate/ERC1155.sol\";\nimport { LibERC20Compat } from \"../utils/LibERC20Compat.sol\";\nimport { LibRawResult } from \"../utils/LibRawResult.sol\";\nimport { LibSafeCast } from \"../utils/LibSafeCast.sol\";\nimport { IERC4906 } from \"../utils/IERC4906.sol\";\nimport { IGlobals } from \"../globals/IGlobals.sol\";\nimport { LibGlobals } from \"../globals/LibGlobals.sol\";\nimport { IProposalExecutionEngine } from \"../proposals/IProposalExecutionEngine.sol\";\nimport { LibProposal } from \"../proposals/LibProposal.sol\";\nimport { ProposalStorage } from \"../proposals/ProposalStorage.sol\";\nimport { Implementation } from \"../utils/Implementation.sol\";\nimport { Party } from \"./Party.sol\";\n\n/// @notice Base contract for a Party encapsulating all governance functionality.\n/// @dev This contract uses IERC4906 however does not comply with the standard\n///      since it does emit metadata events when distributions are claimed or\n///      when a MetadaProvider changes its URI. This decision was made\n///      intentionally which is why ERC4906 is not included in `supportsInterface`.\nabstract contract PartyGovernance is\n    ProposalStorage,\n    Implementation,\n    IERC4906,\n    ReadOnlyDelegateCall\n{\n    using LibERC20Compat for IERC20;\n    using LibRawResult for bytes;\n    using LibSafeCast for uint256;\n    using LibSafeCast for int192;\n    using LibSafeCast for uint96;\n\n    // States a proposal can be in.\n    enum ProposalStatus {\n        // The proposal does not exist.\n        Invalid,\n        // The proposal has been proposed (via `propose()`), has not been vetoed\n        // by a party host, and is within the voting window. Members can vote on\n        // the proposal and party hosts can veto the proposal.\n        Voting,\n        // The proposal has either exceeded its voting window without reaching\n        // `passThresholdBps` of votes or was vetoed by a party host.\n        Defeated,\n        // The proposal reached at least `passThresholdBps` of votes but is still\n        // waiting for `executionDelay` to pass before it can be executed. Members\n        // can continue to vote on the proposal and party hosts can veto at this time.\n        Passed,\n        // Same as `Passed` but now `executionDelay` has been satisfied. Any member\n        // may execute the proposal via `execute()`, unless `maxExecutableTime`\n        // has arrived.\n        Ready,\n        // The proposal has been executed at least once but has further steps to\n        // complete so it needs to be executed again. No other proposals may be\n        // executed while a proposal is in the `InProgress` state. No voting or\n        // vetoing of the proposal is allowed, however it may be forcibly cancelled\n        // via `cancel()` if the `cancelDelay` has passed since being first executed.\n        InProgress,\n        // The proposal was executed and completed all its steps. No voting or\n        // vetoing can occur and it cannot be cancelled nor executed again.\n        Complete,\n        // The proposal was executed at least once but did not complete before\n        // `cancelDelay` seconds passed since the first execute and was forcibly cancelled.\n        Cancelled\n    }\n\n    struct GovernanceOpts {\n        // Address of initial party hosts.\n        address[] hosts;\n        // How long people can vote on a proposal.\n        uint40 voteDuration;\n        // How long to wait after a proposal passes before it can be\n        // executed.\n        uint40 executionDelay;\n        // Minimum ratio of accept votes to consider a proposal passed,\n        // in bps, where 10,000 == 100%.\n        uint16 passThresholdBps;\n        // Total voting power of governance NFTs.\n        uint96 totalVotingPower;\n        // Fee bps for distributions.\n        uint16 feeBps;\n        // Fee recipient for distributions.\n        address payable feeRecipient;\n    }\n\n    // A snapshot of voting power for a member.\n    struct VotingPowerSnapshot {\n        // The timestamp when the snapshot was taken.\n        uint40 timestamp;\n        // Voting power that was delegated to this user by others.\n        uint96 delegatedVotingPower;\n        // The intrinsic (not delegated from someone else) voting power of this user.\n        uint96 intrinsicVotingPower;\n        // Whether the user was delegated to another at this snapshot.\n        bool isDelegated;\n    }\n\n    // Proposal details chosen by proposer.\n    struct Proposal {\n        // Time beyond which the proposal can no longer be executed.\n        // If the proposal has already been executed, and is still InProgress,\n        // this value is ignored.\n        uint40 maxExecutableTime;\n        // The minimum seconds this proposal can remain in the InProgress status\n        // before it can be cancelled.\n        uint40 cancelDelay;\n        // Encoded proposal data. The first 4 bytes are the proposal type, followed\n        // by encoded proposal args specific to the proposal type. See\n        // ProposalExecutionEngine for details.\n        bytes proposalData;\n    }\n\n    // Accounting and state tracking values for a proposal.\n    struct ProposalStateValues {\n        // When the proposal was proposed.\n        uint40 proposedTime;\n        // When the proposal passed the vote.\n        uint40 passedTime;\n        // When the proposal was first executed.\n        uint40 executedTime;\n        // When the proposal completed.\n        uint40 completedTime;\n        // Number of accept votes.\n        uint96 votes; // -1 == vetoed\n        // Number of total voting power at time proposal created.\n        uint96 totalVotingPower;\n        // Number of hosts at time proposal created\n        uint8 numHosts;\n        // Number of hosts that accepted proposal\n        uint8 numHostsAccepted;\n        // Cached vote duration from proposal creation.\n        uint40 voteDuration;\n        // Cached execution delay from proposal creation.\n        uint40 executionDelay;\n        // Cached pass threshold bps from proposal creation.\n        uint16 passThresholdBps;\n    }\n\n    // Storage states for a proposal.\n    struct ProposalState {\n        // Accounting and state tracking values.\n        ProposalStateValues values;\n        // Hash of the proposal.\n        bytes32 hash;\n        // Whether a member has voted for (accepted) this proposal already.\n        mapping(address => bool) hasVoted;\n    }\n\n    event Proposed(uint256 proposalId, address proposer, Proposal proposal);\n    event ProposalAccepted(uint256 proposalId, address voter, uint256 weight);\n    event EmergencyExecute(address target, bytes data, uint256 amountEth);\n\n    event ProposalPassed(uint256 indexed proposalId);\n    event ProposalVetoed(uint256 indexed proposalId, address host);\n    event ProposalExecuted(uint256 indexed proposalId, address executor, bytes nextProgressData);\n    event ProposalCancelled(uint256 indexed proposalId);\n    event DistributionCreated(\n        ITokenDistributor.TokenType tokenType,\n        address token,\n        uint256 tokenId\n    );\n    event PartyDelegateUpdated(address indexed owner, address indexed delegate);\n    event HostStatusTransferred(address oldHost, address newHost);\n    event EmergencyExecuteDisabled();\n    event PartyVotingSnapshotCreated(\n        address indexed voter,\n        uint40 timestamp,\n        uint96 delegatedVotingPower,\n        uint96 intrinsicVotingPower,\n        bool isDelegated\n    );\n\n    error MismatchedPreciousListLengths();\n    error BadProposalStatusError(ProposalStatus status);\n    error BadProposalHashError(bytes32 proposalHash, bytes32 actualHash);\n    error ExecutionTimeExceededError(uint40 maxExecutableTime, uint40 timestamp);\n    error NotAuthorized();\n    error InvalidDelegateError();\n    error BadPreciousListError();\n    error OnlyWhenEmergencyActionsAllowedError();\n    error OnlyWhenEnabledError();\n    error AlreadyVotedError(address voter);\n    error InvalidNewHostError();\n    error ProposalCannotBeCancelledYetError(uint40 currentTime, uint40 cancelTime);\n    error InvalidBpsError(uint16 bps);\n    error InvalidGovernanceParameter(uint256 value);\n    error DistributionsRequireVoteError();\n    error PartyNotStartedError();\n    error CannotModifyTotalVotingPowerAndAcceptError();\n    error TooManyHosts();\n\n    uint256 private constant UINT40_HIGH_BIT = 1 << 39;\n    uint96 private constant VETO_VALUE = type(uint96).max;\n\n    // The `Globals` contract storing global configuration values. This contract\n    // is immutable and it’s address will never change.\n    IGlobals private immutable _GLOBALS;\n\n    /// @notice Whether the DAO has emergency powers for this party.\n    bool public emergencyExecuteDisabled;\n    /// @notice Distribution fee bps.\n    uint16 public feeBps;\n    /// @notice Distribution fee recipient.\n    address payable public feeRecipient;\n    /// @notice The timestamp of the last time total voting power changed in the party.\n    uint40 public lastTotalVotingPowerChangeTimestamp;\n    /// @notice The hash of the list of precious NFTs guarded by the party.\n    bytes32 public preciousListHash;\n    /// @notice The last proposal ID that was used. 0 means no proposals have been made.\n    uint256 public lastProposalId;\n    /// @notice Whether an address is a party host.\n    mapping(address => bool) public isHost;\n    /// @notice The last person a voter delegated its voting power to.\n    mapping(address => address) public delegationsByVoter;\n    /// @notice Number of hosts for this party\n    uint8 public numHosts;\n    /// @notice ProposalState by proposal ID.\n    mapping(uint256 => ProposalState) private _proposalStateByProposalId;\n    /// @notice Snapshots of voting power per user, each sorted by increasing time.\n    mapping(address => VotingPowerSnapshot[]) private _votingPowerSnapshotsByVoter;\n\n    function _assertHost() internal view {\n        if (!isHost[msg.sender]) {\n            revert NotAuthorized();\n        }\n    }\n\n    function _assertActiveMember() internal view {\n        VotingPowerSnapshot memory snap = _getLastVotingPowerSnapshotForVoter(msg.sender);\n        // Must have either delegated voting power or intrinsic voting power.\n        if (snap.intrinsicVotingPower == 0 && snap.delegatedVotingPower == 0) {\n            revert NotAuthorized();\n        }\n    }\n\n    // Only the party DAO multisig can call.\n    modifier onlyPartyDao() {\n        {\n            address partyDao = _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);\n            if (msg.sender != partyDao) {\n                revert NotAuthorized();\n            }\n        }\n        _;\n    }\n\n    // Only the party DAO multisig or a party host can call.\n    modifier onlyPartyDaoOrHost() {\n        address partyDao = _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);\n        if (msg.sender != partyDao && !isHost[msg.sender]) {\n            revert NotAuthorized();\n        }\n        _;\n    }\n\n    // Only if `emergencyExecuteDisabled` is not true.\n    modifier onlyWhenEmergencyExecuteAllowed() {\n        if (emergencyExecuteDisabled) {\n            revert OnlyWhenEmergencyActionsAllowedError();\n        }\n        _;\n    }\n\n    function _assertNotGloballyDisabled() internal view {\n        if (_GLOBALS.getBool(LibGlobals.GLOBAL_DISABLE_PARTY_ACTIONS)) {\n            revert OnlyWhenEnabledError();\n        }\n    }\n\n    // Set the `Globals` contract.\n    constructor(IGlobals globals) {\n        _GLOBALS = globals;\n    }\n\n    // Initialize storage for proxy contracts and initialize the proposal execution engine.\n    function _initialize(\n        GovernanceOpts memory govOpts,\n        ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds\n    ) internal virtual {\n        // Check BPS are valid.\n        if (govOpts.feeBps > 1e4) {\n            revert InvalidBpsError(govOpts.feeBps);\n        }\n        if (govOpts.voteDuration < 1 hours) {\n            revert InvalidGovernanceParameter(govOpts.voteDuration);\n        }\n        if (govOpts.passThresholdBps == 0 || govOpts.passThresholdBps > 1e4) {\n            revert InvalidBpsError(govOpts.passThresholdBps);\n        }\n        if (govOpts.executionDelay == 0 || govOpts.executionDelay > 30 days) {\n            revert InvalidGovernanceParameter(govOpts.executionDelay);\n        }\n        // Initialize the proposal execution engine.\n        _initProposalImpl(\n            IProposalExecutionEngine(_GLOBALS.getAddress(LibGlobals.GLOBAL_PROPOSAL_ENGINE_IMPL)),\n            abi.encode(proposalEngineOpts)\n        );\n        // Set the governance parameters.\n        _getSharedProposalStorage().governanceValues = GovernanceValues({\n            voteDuration: govOpts.voteDuration,\n            executionDelay: govOpts.executionDelay,\n            passThresholdBps: govOpts.passThresholdBps,\n            totalVotingPower: govOpts.totalVotingPower\n        });\n        numHosts = uint8(govOpts.hosts.length);\n        // Set fees.\n        feeBps = govOpts.feeBps;\n        feeRecipient = govOpts.feeRecipient;\n        // Set the precious list.\n        _setPreciousList(preciousTokens, preciousTokenIds);\n        // Set the party hosts.\n        if (govOpts.hosts.length > type(uint8).max) {\n            revert TooManyHosts();\n        }\n        for (uint256 i = 0; i < govOpts.hosts.length; ++i) {\n            isHost[govOpts.hosts[i]] = true;\n        }\n    }\n\n    /// @dev Forward all unknown read-only calls to the proposal execution engine.\n    ///      Initial use case is to facilitate eip-1271 signatures.\n    fallback() external {\n        bytes4 functionSelector = bytes4(msg.data[0:4]);\n        if (\n            functionSelector == ERC1155TokenReceiverBase.onERC1155BatchReceived.selector ||\n            functionSelector == ERC1155TokenReceiverBase.onERC1155Received.selector ||\n            functionSelector == IERC721Receiver.onERC721Received.selector\n        ) {\n            assembly {\n                let freeMem := mload(0x40)\n                mstore(freeMem, functionSelector)\n                mstore(0x40, add(freeMem, 0x20))\n                return(freeMem, 0x20)\n            }\n        }\n        _readOnlyDelegateCall(address(_getSharedProposalStorage().engineImpl), msg.data);\n    }\n\n    /// @notice Query if a contract implements an interface.\n    /// @param interfaceId The interface identifier, as specified in ERC-165\n    /// @return `true` if the contract implements `interfaceId` and\n    ///         `interfaceId` is not 0xffffffff, `false` otherwise\n    function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {\n        return\n            interfaceId == type(IERC721Receiver).interfaceId ||\n            interfaceId == type(ERC1155TokenReceiverBase).interfaceId;\n    }\n\n    /// @notice Get the current `ProposalExecutionEngine` instance.\n    function getProposalExecutionEngine() external view returns (IProposalExecutionEngine) {\n        return _getSharedProposalStorage().engineImpl;\n    }\n\n    /// @notice Get the current `ProposalEngineOpts` options.\n    function getProposalEngineOpts() external view returns (ProposalEngineOpts memory) {\n        return _getSharedProposalStorage().opts;\n    }\n\n    /// @notice Get the total voting power of `voter` at a snapshot `snapIndex`, with checks to\n    ///         make sure it is the latest voting snapshot =< `timestamp`.\n    /// @param voter The address of the voter.\n    /// @param timestamp The timestamp to get the voting power at.\n    /// @param snapIndex The index of the snapshot to get the voting power at.\n    /// @return votingPower The total voting power of `voter` at `timestamp`.\n    function getVotingPowerAt(\n        address voter,\n        uint40 timestamp,\n        uint256 snapIndex\n    ) public view returns (uint96 votingPower) {\n        VotingPowerSnapshot memory snap = _getVotingPowerSnapshotAt(voter, timestamp, snapIndex);\n        return (snap.isDelegated ? 0 : snap.intrinsicVotingPower) + snap.delegatedVotingPower;\n    }\n\n    /// @notice Get the state of a proposal.\n    /// @param proposalId The ID of the proposal.\n    /// @return status The status of the proposal.\n    /// @return values The state of the proposal.\n    function getProposalStateInfo(\n        uint256 proposalId\n    ) external view returns (ProposalStatus status, ProposalStateValues memory values) {\n        values = _proposalStateByProposalId[proposalId].values;\n        status = _getProposalStatus(values);\n    }\n\n    /// @notice Retrieve fixed governance parameters.\n    /// @return gv The governance parameters of this party.\n    function getGovernanceValues() external view returns (GovernanceValues memory) {\n        return _getSharedProposalStorage().governanceValues;\n    }\n\n    /// @notice Get the hash of a proposal.\n    /// @dev Proposal details are not stored on-chain so the hash is used to enforce\n    ///      consistency between calls.\n    /// @param proposal The proposal to hash.\n    /// @return proposalHash The hash of the proposal.\n    function getProposalHash(Proposal memory proposal) public pure returns (bytes32 proposalHash) {\n        // Hash the proposal in-place. Equivalent to:\n        // keccak256(abi.encode(\n        //   proposal.maxExecutableTime,\n        //   proposal.cancelDelay,\n        //   keccak256(proposal.proposalData)\n        // ))\n        bytes32 dataHash = keccak256(proposal.proposalData);\n        assembly {\n            // Overwrite the data field with the hash of its contents and then\n            // hash the struct.\n            let dataPos := add(proposal, 0x40)\n            let t := mload(dataPos)\n            mstore(dataPos, dataHash)\n            proposalHash := keccak256(proposal, 0x60)\n            // Restore the data field.\n            mstore(dataPos, t)\n        }\n    }\n\n    /// @notice Get the index of the most recent voting power snapshot <= `timestamp`.\n    /// @param voter The address of the voter.\n    /// @param timestamp The timestamp to get the snapshot index at.\n    /// @return index The index of the snapshot.\n    function findVotingPowerSnapshotIndex(\n        address voter,\n        uint40 timestamp\n    ) public view returns (uint256 index) {\n        VotingPowerSnapshot[] storage snaps = _votingPowerSnapshotsByVoter[voter];\n\n        // Derived from Open Zeppelin binary search\n        // ref: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Checkpoints.sol#L39\n        uint256 high = snaps.length;\n        uint256 low = 0;\n        while (low < high) {\n            uint256 mid = (low + high) / 2;\n            if (snaps[mid].timestamp > timestamp) {\n                // Entry is too recent.\n                high = mid;\n            } else {\n                // Entry is older. This is our best guess for now.\n                low = mid + 1;\n            }\n        }\n\n        // Return `type(uint256).max` if no valid voting snapshots found.\n        return high == 0 ? type(uint256).max : high - 1;\n    }\n\n    /// @notice Pledge your intrinsic voting power to a new delegate, removing it from\n    ///         the old one (if any).\n    /// @param delegate The address to delegating voting power to.\n    function delegateVotingPower(address delegate) external {\n        _adjustVotingPower(msg.sender, 0, delegate);\n    }\n\n    /// @notice Transfer party host status to another.\n    /// @param newPartyHost The address of the new host.\n    function abdicateHost(address newPartyHost) external {\n        _assertHost();\n        // 0 is a special case burn address.\n        if (newPartyHost != address(0)) {\n            // Can only abdicate host\n            revert InvalidNewHostError();\n        } else {\n            // Burned the host status\n            --numHosts;\n        }\n        isHost[msg.sender] = false;\n        emit HostStatusTransferred(msg.sender, newPartyHost);\n    }\n\n    /// @notice Create a token distribution by moving the party's entire balance\n    ///         to the `TokenDistributor` contract and immediately creating a\n    ///         distribution governed by this party.\n    /// @dev The `feeBps` and `feeRecipient` this party was created with will be\n    ///      propagated to the distribution. Party members are entitled to a\n    ///      share of the distribution's tokens proportionate to their relative\n    ///      voting power in this party (less the fee).\n    /// @dev Allow this to be called by the party itself for `FractionalizeProposal`.\n    /// @param tokenType The type of token to distribute.\n    /// @param token The address of the token to distribute.\n    /// @param tokenId The ID of the token to distribute. Currently unused but\n    ///                may be used in the future to support other distribution types.\n    /// @return distInfo The information about the created distribution.\n    function distribute(\n        uint256 amount,\n        ITokenDistributor.TokenType tokenType,\n        address token,\n        uint256 tokenId\n    ) external returns (ITokenDistributor.DistributionInfo memory distInfo) {\n        _assertNotGloballyDisabled();\n        // Ignore if the party is calling functions on itself, like with\n        // `FractionalizeProposal` and `DistributionProposal`.\n        if (msg.sender != address(this)) {\n            // Must not require a vote to create a distribution, otherwise\n            // distributions can only be created through a distribution\n            // proposal.\n            if (\n                _getSharedProposalStorage().opts.distributionsConfig !=\n                DistributionsConfig.AllowedWithoutVote\n            ) {\n                revert DistributionsRequireVoteError();\n            }\n            // Must be an active member.\n            VotingPowerSnapshot memory snap = _getLastVotingPowerSnapshotForVoter(msg.sender);\n            if (snap.intrinsicVotingPower == 0 && snap.delegatedVotingPower == 0) {\n                revert NotAuthorized();\n            }\n        }\n        // Prevent creating a distribution if the party has not started.\n        if (_getSharedProposalStorage().governanceValues.totalVotingPower == 0) {\n            revert PartyNotStartedError();\n        }\n        // Get the address of the token distributor.\n        ITokenDistributor distributor = ITokenDistributor(\n            _GLOBALS.getAddress(LibGlobals.GLOBAL_TOKEN_DISTRIBUTOR)\n        );\n        emit DistributionCreated(tokenType, token, tokenId);\n        _emitMetadataUpdateEvent();\n\n        // Create a native token distribution.\n        address payable feeRecipient_ = feeRecipient;\n        uint16 feeBps_ = feeBps;\n        if (tokenType == ITokenDistributor.TokenType.Native) {\n            return\n                distributor.createNativeDistribution{ value: amount }(\n                    Party(payable(address(this))),\n                    feeRecipient_,\n                    feeBps_\n                );\n        }\n        // Otherwise must be an ERC20 token distribution.\n        assert(tokenType == ITokenDistributor.TokenType.Erc20);\n        IERC20(token).compatTransfer(address(distributor), amount);\n        return\n            distributor.createErc20Distribution(\n                IERC20(token),\n                Party(payable(address(this))),\n                feeRecipient_,\n                feeBps_\n            );\n    }\n\n    /// @notice Make a proposal for members to vote on and cast a vote to accept it\n    ///         as well.\n    /// @dev Only an active member (has voting power) can call this.\n    ///      Afterwards, members can vote to support it with `accept()` or a party\n    ///      host can unilaterally reject the proposal with `veto()`.\n    /// @param proposal The details of the proposal.\n    /// @param latestSnapIndex The index of the caller's most recent voting power\n    ///                        snapshot before the proposal was created. Should\n    ///                        be retrieved off-chain and passed in.\n    function propose(\n        Proposal memory proposal,\n        uint256 latestSnapIndex\n    ) external returns (uint256 proposalId) {\n        _assertActiveMember();\n        proposalId = ++lastProposalId;\n\n        ProposalStorage.GovernanceValues memory gv = _getSharedProposalStorage().governanceValues;\n\n        // Store the time the proposal was created and the proposal hash.\n        (\n            _proposalStateByProposalId[proposalId].values,\n            _proposalStateByProposalId[proposalId].hash\n        ) = (\n            ProposalStateValues({\n                proposedTime: uint40(block.timestamp),\n                passedTime: 0,\n                executedTime: 0,\n                completedTime: 0,\n                votes: 0,\n                totalVotingPower: gv.totalVotingPower,\n                numHosts: numHosts,\n                numHostsAccepted: 0,\n                voteDuration: gv.voteDuration,\n                executionDelay: gv.executionDelay,\n                passThresholdBps: gv.passThresholdBps\n            }),\n            getProposalHash(proposal)\n        );\n        emit Proposed(proposalId, msg.sender, proposal);\n        accept(proposalId, latestSnapIndex);\n        _emitMetadataUpdateEvent();\n    }\n\n    /// @notice Vote to support a proposed proposal.\n    /// @dev The voting power cast will be the effective voting power of the caller\n    ///      just before `propose()` was called (see `getVotingPowerAt()`).\n    ///      If the proposal reaches `passThresholdBps` acceptance ratio then the\n    ///      proposal will be in the `Passed` state and will be executable after\n    ///      the `executionDelay` has passed, putting it in the `Ready` state.\n    /// @param proposalId The ID of the proposal to accept.\n    /// @param snapIndex The index of the caller's last voting power snapshot\n    ///                  before the proposal was created. Should be retrieved\n    ///                  off-chain and passed in.\n    /// @return totalVotes The total votes cast on the proposal.\n    function accept(uint256 proposalId, uint256 snapIndex) public returns (uint256 totalVotes) {\n        // Get the information about the proposal.\n        ProposalState storage info = _proposalStateByProposalId[proposalId];\n        ProposalStateValues memory values = info.values;\n\n        // Can only vote in certain proposal statuses.\n        {\n            ProposalStatus status = _getProposalStatus(values);\n            // Allow voting even if the proposal is passed/ready so it can\n            // potentially reach 100% consensus, which unlocks special\n            // behaviors for certain proposal types.\n            if (\n                status != ProposalStatus.Voting &&\n                status != ProposalStatus.Passed &&\n                status != ProposalStatus.Ready\n            ) {\n                revert BadProposalStatusError(status);\n            }\n        }\n\n        // Prevent voting in the same block as the last total voting power\n        // change. This is to prevent an exploit where a member can, for\n        // example, rage quit to reduce the total voting power of the party,\n        // then propose and vote in the same block since `getVotingPowerAt()`\n        // uses `values.proposedTime - 1`. This would allow them to use the\n        // voting power snapshot just before their card was burned to vote,\n        // potentially passing a proposal that would have otherwise not passed.\n        if (lastTotalVotingPowerChangeTimestamp == block.timestamp) {\n            revert CannotModifyTotalVotingPowerAndAcceptError();\n        }\n\n        // Cannot vote twice.\n        if (info.hasVoted[msg.sender]) {\n            revert AlreadyVotedError(msg.sender);\n        }\n        // Mark the caller as having voted.\n        info.hasVoted[msg.sender] = true;\n\n        // Increase the total votes that have been cast on this proposal.\n        uint96 votingPower = getVotingPowerAt(msg.sender, values.proposedTime - 1, snapIndex);\n        values.votes += votingPower;\n        if (isHost[msg.sender]) {\n            ++values.numHostsAccepted;\n        }\n        info.values = values;\n        emit ProposalAccepted(proposalId, msg.sender, votingPower);\n\n        // Update the proposal status if it has reached the pass threshold.\n        if (\n            values.passedTime == 0 &&\n            (uint256(values.votes) * 1e4) / uint256(values.totalVotingPower) >=\n            uint256(values.passThresholdBps)\n        ) {\n            info.values.passedTime = uint40(block.timestamp);\n            emit ProposalPassed(proposalId);\n            _emitMetadataUpdateEvent();\n        }\n        return values.votes;\n    }\n\n    /// @notice As a party host, veto a proposal, unilaterally rejecting it.\n    /// @dev The proposal will never be executable and cannot be voted on anymore.\n    ///      A proposal that has been already executed at least once (in the `InProgress` status)\n    ///      cannot be vetoed.\n    /// @param proposalId The ID of the proposal to veto.\n    function veto(uint256 proposalId) external {\n        _assertHost();\n        // Setting `votes` to -1 indicates a veto.\n        ProposalState storage info = _proposalStateByProposalId[proposalId];\n        ProposalStateValues memory values = info.values;\n\n        {\n            ProposalStatus status = _getProposalStatus(values);\n            // Proposal must be in one of the following states.\n            if (\n                status != ProposalStatus.Voting &&\n                status != ProposalStatus.Passed &&\n                status != ProposalStatus.Ready\n            ) {\n                revert BadProposalStatusError(status);\n            }\n        }\n\n        // -1 indicates veto.\n        info.values.votes = VETO_VALUE;\n        emit ProposalVetoed(proposalId, msg.sender);\n        _emitMetadataUpdateEvent();\n    }\n\n    /// @notice Executes a proposal that has passed governance.\n    /// @dev The proposal must be in the `Ready` or `InProgress` status.\n    ///      A `ProposalExecuted` event will be emitted with a non-empty `nextProgressData`\n    ///      if the proposal has extra steps (must be executed again) to carry out,\n    ///      in which case `nextProgressData` should be passed into the next `execute()` call.\n    ///      The `ProposalExecutionEngine` enforces that only one `InProgress` proposal\n    ///      is active at a time, so that proposal must be completed or cancelled via `cancel()`\n    ///      in order to execute a different proposal.\n    ///      `extraData` is optional, off-chain data a proposal might need to execute a step.\n    /// @param proposalId The ID of the proposal to execute.\n    /// @param proposal The details of the proposal.\n    /// @param preciousTokens The tokens that the party considers precious.\n    /// @param preciousTokenIds The token IDs associated with each precious token.\n    /// @param progressData The data returned from the last `execute()` call, if any.\n    /// @param extraData Off-chain data a proposal might need to execute a step.\n    function execute(\n        uint256 proposalId,\n        Proposal memory proposal,\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds,\n        bytes calldata progressData,\n        bytes calldata extraData\n    ) external payable {\n        _assertNotGloballyDisabled();\n        _assertActiveMember();\n        // Get information about the proposal.\n        ProposalState storage proposalState = _proposalStateByProposalId[proposalId];\n        // Proposal details must remain the same from `propose()`.\n        _validateProposalHash(proposal, proposalState.hash);\n        ProposalStateValues memory values = proposalState.values;\n        ProposalStatus status = _getProposalStatus(values);\n        // The proposal must be executable or have already been executed but still\n        // has more steps to go.\n        if (status != ProposalStatus.Ready && status != ProposalStatus.InProgress) {\n            revert BadProposalStatusError(status);\n        }\n        if (status == ProposalStatus.Ready) {\n            // If the proposal has not been executed yet, make sure it hasn't\n            // expired. Note that proposals that have been executed\n            // (but still have more steps) ignore `maxExecutableTime`.\n            if (proposal.maxExecutableTime < block.timestamp) {\n                revert ExecutionTimeExceededError(\n                    proposal.maxExecutableTime,\n                    uint40(block.timestamp)\n                );\n            }\n            proposalState.values.executedTime = uint40(block.timestamp);\n        }\n        // Check that the precious list is valid.\n        if (!_isPreciousListCorrect(preciousTokens, preciousTokenIds)) {\n            revert BadPreciousListError();\n        }\n        // Preemptively set the proposal to completed to avoid it being executed\n        // again in a deeper call.\n        proposalState.values.completedTime = uint40(block.timestamp);\n        // Execute the proposal.\n        bool completed = _executeProposal(\n            proposalId,\n            proposal,\n            preciousTokens,\n            preciousTokenIds,\n            _getProposalFlags(values),\n            progressData,\n            extraData\n        );\n        if (!completed) {\n            // Proposal did not complete.\n            proposalState.values.completedTime = 0;\n        }\n    }\n\n    /// @notice Cancel a (probably stuck) InProgress proposal.\n    /// @dev `proposal.cancelDelay` seconds must have passed since it was first\n    ///      executed for this to be valid. The currently active proposal will\n    ///      simply be yeeted out of existence so another proposal can execute.\n    ///      This is intended to be a last resort and can leave the party in a\n    ///      broken state. Whenever possible, active proposals should be\n    ///      allowed to complete their lifecycle.\n    /// @param proposalId The ID of the proposal to cancel.\n    /// @param proposal The details of the proposal to cancel.\n    function cancel(uint256 proposalId, Proposal calldata proposal) external {\n        _assertActiveMember();\n        // Get information about the proposal.\n        ProposalState storage proposalState = _proposalStateByProposalId[proposalId];\n        // Proposal details must remain the same from `propose()`.\n        _validateProposalHash(proposal, proposalState.hash);\n        ProposalStateValues memory values = proposalState.values;\n        {\n            // Must be `InProgress`.\n            ProposalStatus status = _getProposalStatus(values);\n            if (status != ProposalStatus.InProgress) {\n                revert BadProposalStatusError(status);\n            }\n        }\n        {\n            // Limit the `cancelDelay` to the global max and min cancel delay\n            // to mitigate parties accidentally getting stuck forever by setting an\n            // unrealistic `cancelDelay` or being reckless with too low a\n            // cancel delay.\n            uint256 cancelDelay = proposal.cancelDelay;\n            uint256 globalMaxCancelDelay = _GLOBALS.getUint256(\n                LibGlobals.GLOBAL_PROPOSAL_MAX_CANCEL_DURATION\n            );\n            uint256 globalMinCancelDelay = _GLOBALS.getUint256(\n                LibGlobals.GLOBAL_PROPOSAL_MIN_CANCEL_DURATION\n            );\n            if (globalMaxCancelDelay != 0) {\n                // Only if we have one set.\n                if (cancelDelay > globalMaxCancelDelay) {\n                    cancelDelay = globalMaxCancelDelay;\n                }\n            }\n            if (globalMinCancelDelay != 0) {\n                // Only if we have one set.\n                if (cancelDelay < globalMinCancelDelay) {\n                    cancelDelay = globalMinCancelDelay;\n                }\n            }\n            uint256 cancelTime = values.executedTime + cancelDelay;\n            // Must not be too early.\n            if (block.timestamp < cancelTime) {\n                revert ProposalCannotBeCancelledYetError(\n                    uint40(block.timestamp),\n                    uint40(cancelTime)\n                );\n            }\n        }\n        // Mark the proposal as cancelled by setting the completed time to the current\n        // time with the high bit set.\n        proposalState.values.completedTime = uint40(block.timestamp | UINT40_HIGH_BIT);\n        {\n            // Delegatecall into the proposal engine impl to perform the cancel.\n            (bool success, bytes memory resultData) = (\n                address(_getSharedProposalStorage().engineImpl)\n            ).delegatecall(abi.encodeCall(IProposalExecutionEngine.cancelProposal, (proposalId)));\n            if (!success) {\n                resultData.rawRevert();\n            }\n        }\n        emit ProposalCancelled(proposalId);\n        _emitMetadataUpdateEvent();\n    }\n\n    /// @notice As the DAO, execute an arbitrary function call from this contract.\n    /// @dev Emergency actions must not be revoked for this to work.\n    /// @param targetAddress The contract to call.\n    /// @param targetCallData The data to pass to the contract.\n    /// @param amountEth The amount of ETH to send to the contract.\n    function emergencyExecute(\n        address targetAddress,\n        bytes calldata targetCallData,\n        uint256 amountEth\n    ) external payable onlyPartyDao onlyWhenEmergencyExecuteAllowed {\n        (bool success, bytes memory res) = targetAddress.call{ value: amountEth }(targetCallData);\n        if (!success) {\n            res.rawRevert();\n        }\n        emit EmergencyExecute(targetAddress, targetCallData, amountEth);\n    }\n\n    /// @notice Revoke the DAO's ability to call emergencyExecute().\n    /// @dev Either the DAO or the party host can call this.\n    function disableEmergencyExecute() external onlyPartyDaoOrHost {\n        emergencyExecuteDisabled = true;\n        emit EmergencyExecuteDisabled();\n    }\n\n    function _executeProposal(\n        uint256 proposalId,\n        Proposal memory proposal,\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds,\n        uint256 flags,\n        bytes memory progressData,\n        bytes memory extraData\n    ) private returns (bool completed) {\n        // Setup the arguments for the proposal execution engine.\n        IProposalExecutionEngine.ExecuteProposalParams\n            memory executeParams = IProposalExecutionEngine.ExecuteProposalParams({\n                proposalId: proposalId,\n                proposalData: proposal.proposalData,\n                progressData: progressData,\n                extraData: extraData,\n                preciousTokens: preciousTokens,\n                preciousTokenIds: preciousTokenIds,\n                flags: flags\n            });\n        // Get the progress data returned after the proposal is executed.\n        bytes memory nextProgressData;\n        {\n            // Execute the proposal.\n            (bool success, bytes memory resultData) = address(\n                _getSharedProposalStorage().engineImpl\n            ).delegatecall(\n                    abi.encodeCall(IProposalExecutionEngine.executeProposal, (executeParams))\n                );\n            if (!success) {\n                resultData.rawRevert();\n            }\n            nextProgressData = abi.decode(resultData, (bytes));\n        }\n        emit ProposalExecuted(proposalId, msg.sender, nextProgressData);\n        _emitMetadataUpdateEvent();\n        // If the returned progress data is empty, then the proposal completed\n        // and it should not be executed again.\n        return nextProgressData.length == 0;\n    }\n\n    // Get the most recent voting power snapshot <= timestamp using `hintindex` as a \"hint\".\n    function _getVotingPowerSnapshotAt(\n        address voter,\n        uint40 timestamp,\n        uint256 hintIndex\n    ) internal view returns (VotingPowerSnapshot memory snap) {\n        VotingPowerSnapshot[] storage snaps = _votingPowerSnapshotsByVoter[voter];\n        uint256 snapsLength = snaps.length;\n        if (snapsLength != 0) {\n            if (\n                // Hint is within bounds.\n                hintIndex < snapsLength &&\n                // Snapshot is not too recent.\n                snaps[hintIndex].timestamp <= timestamp &&\n                // Snapshot is not too old.\n                (hintIndex == snapsLength - 1 || snaps[hintIndex + 1].timestamp > timestamp)\n            ) {\n                return snaps[hintIndex];\n            }\n\n            // Hint was wrong, fallback to binary search to find snapshot.\n            hintIndex = findVotingPowerSnapshotIndex(voter, timestamp);\n            // Check that snapshot was found.\n            if (hintIndex != type(uint256).max) {\n                return snaps[hintIndex];\n            }\n        }\n\n        // No snapshot found.\n        return snap;\n    }\n\n    // Transfers some voting power of `from` to `to`. The total voting power of\n    // their respective delegates will be updated as well.\n    function _transferVotingPower(address from, address to, uint256 power) internal {\n        int192 powerI192 = power.safeCastUint256ToInt192();\n        _adjustVotingPower(from, -powerI192, address(0));\n        _adjustVotingPower(to, powerI192, address(0));\n    }\n\n    // Increase `voter`'s intrinsic voting power and update their delegate if delegate is nonzero.\n    function _adjustVotingPower(address voter, int192 votingPower, address delegate) internal {\n        VotingPowerSnapshot memory oldSnap = _getLastVotingPowerSnapshotForVoter(voter);\n        address oldDelegate = delegationsByVoter[voter];\n        // If `oldDelegate` is zero and `voter` never delegated, then have\n        // `voter` delegate to themself.\n        oldDelegate = oldDelegate == address(0) ? voter : oldDelegate;\n        // If the new `delegate` is zero, use the current (old) delegate.\n        delegate = delegate == address(0) ? oldDelegate : delegate;\n\n        VotingPowerSnapshot memory newSnap = VotingPowerSnapshot({\n            timestamp: uint40(block.timestamp),\n            delegatedVotingPower: oldSnap.delegatedVotingPower,\n            intrinsicVotingPower: (oldSnap.intrinsicVotingPower.safeCastUint96ToInt192() +\n                votingPower).safeCastInt192ToUint96(),\n            isDelegated: delegate != voter\n        });\n        _insertVotingPowerSnapshot(voter, newSnap);\n        delegationsByVoter[voter] = delegate;\n\n        // This event is emitted even if the delegate did not change.\n        emit PartyDelegateUpdated(voter, delegate);\n\n        // Handle rebalancing delegates.\n        _rebalanceDelegates(voter, oldDelegate, delegate, oldSnap, newSnap);\n    }\n\n    // Update the delegated voting power of the old and new delegates delegated to\n    // by `voter` based on the snapshot change.\n    function _rebalanceDelegates(\n        address voter,\n        address oldDelegate,\n        address newDelegate,\n        VotingPowerSnapshot memory oldSnap,\n        VotingPowerSnapshot memory newSnap\n    ) private {\n        if (newDelegate == address(0) || oldDelegate == address(0)) {\n            revert InvalidDelegateError();\n        }\n        if (oldDelegate != voter && oldDelegate != newDelegate) {\n            // Remove past voting power from old delegate.\n            VotingPowerSnapshot memory oldDelegateSnap = _getLastVotingPowerSnapshotForVoter(\n                oldDelegate\n            );\n            VotingPowerSnapshot memory updatedOldDelegateSnap = VotingPowerSnapshot({\n                timestamp: uint40(block.timestamp),\n                delegatedVotingPower: oldDelegateSnap.delegatedVotingPower -\n                    oldSnap.intrinsicVotingPower,\n                intrinsicVotingPower: oldDelegateSnap.intrinsicVotingPower,\n                isDelegated: oldDelegateSnap.isDelegated\n            });\n            _insertVotingPowerSnapshot(oldDelegate, updatedOldDelegateSnap);\n        }\n        if (newDelegate != voter) {\n            // Not delegating to self.\n            // Add new voting power to new delegate.\n            VotingPowerSnapshot memory newDelegateSnap = _getLastVotingPowerSnapshotForVoter(\n                newDelegate\n            );\n            uint96 newDelegateDelegatedVotingPower = newDelegateSnap.delegatedVotingPower +\n                newSnap.intrinsicVotingPower;\n            if (newDelegate == oldDelegate) {\n                // If the old and new delegate are the same, subtract the old\n                // intrinsic voting power of the voter, or else we will double\n                // count a portion of it.\n                newDelegateDelegatedVotingPower -= oldSnap.intrinsicVotingPower;\n            }\n            VotingPowerSnapshot memory updatedNewDelegateSnap = VotingPowerSnapshot({\n                timestamp: uint40(block.timestamp),\n                delegatedVotingPower: newDelegateDelegatedVotingPower,\n                intrinsicVotingPower: newDelegateSnap.intrinsicVotingPower,\n                isDelegated: newDelegateSnap.isDelegated\n            });\n            _insertVotingPowerSnapshot(newDelegate, updatedNewDelegateSnap);\n        }\n    }\n\n    // Append a new voting power snapshot, overwriting the last one if possible.\n    function _insertVotingPowerSnapshot(address voter, VotingPowerSnapshot memory snap) private {\n        emit PartyVotingSnapshotCreated(\n            voter,\n            snap.timestamp,\n            snap.delegatedVotingPower,\n            snap.intrinsicVotingPower,\n            snap.isDelegated\n        );\n\n        VotingPowerSnapshot[] storage voterSnaps = _votingPowerSnapshotsByVoter[voter];\n        uint256 n = voterSnaps.length;\n        // If same timestamp as last entry, overwrite the last snapshot, otherwise append.\n        if (n != 0) {\n            VotingPowerSnapshot memory lastSnap = voterSnaps[n - 1];\n            if (lastSnap.timestamp == snap.timestamp) {\n                voterSnaps[n - 1] = snap;\n                return;\n            }\n        }\n        voterSnaps.push(snap);\n    }\n\n    function _getLastVotingPowerSnapshotForVoter(\n        address voter\n    ) private view returns (VotingPowerSnapshot memory snap) {\n        VotingPowerSnapshot[] storage voterSnaps = _votingPowerSnapshotsByVoter[voter];\n        uint256 n = voterSnaps.length;\n        if (n != 0) {\n            snap = voterSnaps[n - 1];\n        }\n    }\n\n    function _getProposalFlags(ProposalStateValues memory pv) private pure returns (uint256) {\n        uint256 flags = 0;\n        if (_isUnanimousVotes(pv.votes, pv.totalVotingPower)) {\n            flags = flags | LibProposal.PROPOSAL_FLAG_UNANIMOUS;\n        }\n        if (_hostsAccepted(pv.numHosts, pv.numHostsAccepted)) {\n            flags = flags | LibProposal.PROPOSAL_FLAG_HOSTS_ACCEPT;\n        }\n        return flags;\n    }\n\n    function _getProposalStatus(\n        ProposalStateValues memory pv\n    ) private view returns (ProposalStatus status) {\n        // Never proposed.\n        if (pv.proposedTime == 0) {\n            return ProposalStatus.Invalid;\n        }\n        // Executed at least once.\n        if (pv.executedTime != 0) {\n            if (pv.completedTime == 0) {\n                return ProposalStatus.InProgress;\n            }\n            // completedTime high bit will be set if cancelled.\n            if (pv.completedTime & UINT40_HIGH_BIT == UINT40_HIGH_BIT) {\n                return ProposalStatus.Cancelled;\n            }\n            return ProposalStatus.Complete;\n        }\n        // Vetoed.\n        if (pv.votes == type(uint96).max) {\n            return ProposalStatus.Defeated;\n        }\n        uint40 t = uint40(block.timestamp);\n        if (pv.passedTime != 0) {\n            // Ready.\n            if (pv.passedTime + pv.executionDelay <= t) {\n                return ProposalStatus.Ready;\n            }\n            // If unanimous, we skip the execution delay.\n            if (_isUnanimousVotes(pv.votes, pv.totalVotingPower)) {\n                return ProposalStatus.Ready;\n            }\n            // If all hosts voted, skip execution delay\n            if (_hostsAccepted(pv.numHosts, pv.numHostsAccepted)) {\n                return ProposalStatus.Ready;\n            }\n            // Passed.\n            return ProposalStatus.Passed;\n        }\n        // Voting window expired.\n        if (pv.proposedTime + pv.voteDuration <= t) {\n            return ProposalStatus.Defeated;\n        }\n        return ProposalStatus.Voting;\n    }\n\n    function _isUnanimousVotes(\n        uint96 totalVotes,\n        uint96 totalVotingPower\n    ) private pure returns (bool) {\n        uint256 acceptanceRatio = (uint256(totalVotes) * 1e4) / totalVotingPower;\n        // If >= 99.99% acceptance, consider it unanimous.\n        // The minting formula for voting power is a bit lossy, so we check\n        // for slightly less than 100%.\n        return acceptanceRatio >= 0.9999e4;\n    }\n\n    function _hostsAccepted(\n        uint8 snapshotNumHosts,\n        uint8 numHostsAccepted\n    ) private pure returns (bool) {\n        return snapshotNumHosts > 0 && snapshotNumHosts == numHostsAccepted;\n    }\n\n    function _setPreciousList(\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds\n    ) private {\n        if (preciousTokens.length != preciousTokenIds.length) {\n            revert MismatchedPreciousListLengths();\n        }\n        preciousListHash = _hashPreciousList(preciousTokens, preciousTokenIds);\n    }\n\n    function _isPreciousListCorrect(\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds\n    ) private view returns (bool) {\n        return preciousListHash == _hashPreciousList(preciousTokens, preciousTokenIds);\n    }\n\n    function _hashPreciousList(\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds\n    ) internal pure returns (bytes32 h) {\n        assembly {\n            mstore(0x00, keccak256(add(preciousTokens, 0x20), mul(mload(preciousTokens), 0x20)))\n            mstore(0x20, keccak256(add(preciousTokenIds, 0x20), mul(mload(preciousTokenIds), 0x20)))\n            h := keccak256(0x00, 0x40)\n        }\n    }\n\n    function _emitMetadataUpdateEvent() internal {\n        emit BatchMetadataUpdate(0, type(uint256).max);\n    }\n\n    // Assert that the hash of a proposal matches expectedHash.\n    function _validateProposalHash(Proposal memory proposal, bytes32 expectedHash) private pure {\n        bytes32 actualHash = getProposalHash(proposal);\n        if (expectedHash != actualHash) {\n            revert BadProposalHashError(actualHash, expectedHash);\n        }\n    }\n}\n"
      },
      "contracts/party/PartyGovernanceNFT.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../utils/LibSafeCast.sol\";\nimport \"../utils/LibAddress.sol\";\nimport \"openzeppelin/contracts/interfaces/IERC2981.sol\";\nimport \"../globals/IGlobals.sol\";\nimport \"../tokens/IERC721.sol\";\nimport \"../vendor/solmate/ERC721.sol\";\nimport \"./PartyGovernance.sol\";\nimport \"../renderers/RendererStorage.sol\";\n\n/// @notice ERC721 functionality built on top of `PartyGovernance`.\nabstract contract PartyGovernanceNFT is PartyGovernance, ERC721, IERC2981 {\n    using LibSafeCast for uint256;\n    using LibSafeCast for uint96;\n    using LibERC20Compat for IERC20;\n    using LibAddress for address payable;\n\n    error FixedRageQuitTimestampError(uint40 rageQuitTimestamp);\n    error CannotRageQuitError(uint40 rageQuitTimestamp);\n    error CannotDisableRageQuitAfterInitializationError();\n    error CannotEnableRageQuitIfNotDistributionsRequireVoteError();\n    error InvalidTokenOrderError();\n    error BelowMinWithdrawAmountError(uint256 amount, uint256 minAmount);\n    error NothingToBurnError();\n\n    event AuthorityAdded(address indexed authority);\n    event AuthorityRemoved(address indexed authority);\n    event RageQuitSet(uint40 oldRageQuitTimestamp, uint40 newRageQuitTimestamp);\n    event Burn(address caller, uint256 tokenId, uint256 votingPower);\n    event RageQuit(address caller, uint256[] tokenIds, IERC20[] withdrawTokens, address receiver);\n    event PartyCardIntrinsicVotingPowerSet(uint256 indexed tokenId, uint256 intrinsicVotingPower);\n\n    uint40 private constant ENABLE_RAGEQUIT_PERMANENTLY = 0x6b5b567bfe; // uint40(uint256(keccak256(\"ENABLE_RAGEQUIT_PERMANENTLY\")))\n    uint40 private constant DISABLE_RAGEQUIT_PERMANENTLY = 0xab2cb21860; // uint40(uint256(keccak256(\"DISABLE_RAGEQUIT_PERMANENTLY\")))\n\n    // Token address used to indicate ETH.\n    address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n    // The `Globals` contract storing global configuration values. This contract\n    // is immutable and its address will never change.\n    IGlobals private immutable _GLOBALS;\n\n    /// @notice The number of tokens that have been minted.\n    uint96 public tokenCount;\n    /// @notice The total minted voting power.\n    ///         Capped to `_governanceValues.totalVotingPower` unless minting\n    ///         party cards for initial crowdfund.\n    uint96 public mintedVotingPower;\n    /// @notice The timestamp until which ragequit is enabled. Can be set to the\n    ///         `ENABLE_RAGEQUIT_PERMANENTLY`/`DISABLE_RAGEQUIT_PERMANENTLY`\n    ///         values to enable/disable ragequit permanently.\n    ///         `DISABLE_RAGEQUIT_PERMANENTLY` can only be set during\n    ///         initialization.\n    uint40 public rageQuitTimestamp;\n    /// @notice The voting power of `tokenId`.\n    mapping(uint256 => uint256) public votingPowerByTokenId;\n    /// @notice Address with authority to mint cards and update voting power for the party.\n    mapping(address => bool) public isAuthority;\n\n    function _assertAuthority() internal view {\n        if (!isAuthority[msg.sender]) {\n            revert NotAuthorized();\n        }\n    }\n\n    modifier onlySelf() {\n        if (msg.sender != address(this)) {\n            revert NotAuthorized();\n        }\n        _;\n    }\n\n    // Set the `Globals` contract. The name or symbol of ERC721 does not matter;\n    // it will be set in `_initialize()`.\n    constructor(IGlobals globals) payable PartyGovernance(globals) ERC721(\"\", \"\") {\n        _GLOBALS = globals;\n    }\n\n    // Initialize storage for proxy contracts.\n    function _initialize(\n        string memory name_,\n        string memory symbol_,\n        uint256 customizationPresetId,\n        PartyGovernance.GovernanceOpts memory governanceOpts,\n        ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds,\n        address[] memory authorities,\n        uint40 rageQuitTimestamp_\n    ) internal {\n        PartyGovernance._initialize(\n            governanceOpts,\n            proposalEngineOpts,\n            preciousTokens,\n            preciousTokenIds\n        );\n        name = name_;\n        symbol = symbol_;\n        if (rageQuitTimestamp_ != 0) {\n            if (proposalEngineOpts.distributionsConfig == DistributionsConfig.AllowedWithoutVote) {\n                revert CannotEnableRageQuitIfNotDistributionsRequireVoteError();\n            }\n\n            rageQuitTimestamp = rageQuitTimestamp_;\n        }\n        unchecked {\n            for (uint256 i; i < authorities.length; ++i) {\n                isAuthority[authorities[i]] = true;\n            }\n        }\n        if (customizationPresetId != 0) {\n            RendererStorage(_GLOBALS.getAddress(LibGlobals.GLOBAL_RENDERER_STORAGE))\n                .useCustomizationPreset(customizationPresetId);\n        }\n    }\n\n    /// @inheritdoc EIP165\n    function supportsInterface(\n        bytes4 interfaceId\n    ) public pure override(PartyGovernance, ERC721, IERC165) returns (bool) {\n        return\n            PartyGovernance.supportsInterface(interfaceId) ||\n            ERC721.supportsInterface(interfaceId) ||\n            interfaceId == type(IERC2981).interfaceId;\n    }\n\n    /// @inheritdoc ERC721\n    function tokenURI(uint256) public view override returns (string memory) {\n        _delegateToRenderer();\n        return \"\"; // Just to make the compiler happy.\n    }\n\n    /// @notice Returns a URI for the storefront-level metadata for your contract.\n    function contractURI() external view returns (string memory) {\n        _delegateToRenderer();\n        return \"\"; // Just to make the compiler happy.\n    }\n\n    /// @notice Called with the sale price to determine how much royalty\n    //          is owed and to whom.\n    function royaltyInfo(uint256, uint256) external view returns (address, uint256) {\n        _delegateToRenderer();\n        return (address(0), 0); // Just to make the compiler happy.\n    }\n\n    /// @notice Return the distribution share amount of a token. Included as an alias\n    ///         for `votePowerByTokenId` for backwards compatibility with old\n    ///         `TokenDistributor` implementations.\n    /// @param tokenId The token ID to query.\n    /// @return share The distribution shares of `tokenId`.\n    function getDistributionShareOf(uint256 tokenId) external view returns (uint256) {\n        return votingPowerByTokenId[tokenId];\n    }\n\n    /// @notice Return the voting power share of a token. Denominated\n    ///         fractions of 1e18. I.e., 1e18 = 100%.\n    /// @param tokenId The token ID to query.\n    /// @return share The voting power percentage of `tokenId`.\n    function getVotingPowerShareOf(uint256 tokenId) public view returns (uint256) {\n        uint256 totalVotingPower = _getSharedProposalStorage().governanceValues.totalVotingPower;\n        return\n            totalVotingPower == 0 ? 0 : (votingPowerByTokenId[tokenId] * 1e18) / totalVotingPower;\n    }\n\n    /// @notice Mint a governance NFT for `owner` with `votingPower` and\n    ///         immediately delegate voting power to `delegate.` Only callable\n    ///         by an authority.\n    /// @param owner The owner of the NFT.\n    /// @param votingPower The voting power of the NFT.\n    /// @param delegate The address to delegate voting power to.\n    function mint(\n        address owner,\n        uint256 votingPower,\n        address delegate\n    ) external returns (uint256 tokenId) {\n        _assertAuthority();\n        uint96 mintedVotingPower_ = mintedVotingPower;\n        uint96 totalVotingPower = _getSharedProposalStorage().governanceValues.totalVotingPower;\n\n        // Cap voting power to remaining unminted voting power supply.\n        uint96 votingPower_ = votingPower.safeCastUint256ToUint96();\n        // Allow minting past total voting power if minting party cards for\n        // initial crowdfund when there is no total voting power.\n        if (totalVotingPower != 0 && totalVotingPower - mintedVotingPower_ < votingPower_) {\n            unchecked {\n                votingPower_ = totalVotingPower - mintedVotingPower_;\n            }\n        }\n\n        // Update state.\n        unchecked {\n            tokenId = ++tokenCount;\n        }\n        mintedVotingPower += votingPower_;\n        votingPowerByTokenId[tokenId] = votingPower_;\n\n        emit PartyCardIntrinsicVotingPowerSet(tokenId, votingPower_);\n\n        // Use delegate from party over the one set during crowdfund.\n        address delegate_ = delegationsByVoter[owner];\n        if (delegate_ != address(0)) {\n            delegate = delegate_;\n        }\n\n        _adjustVotingPower(owner, votingPower_.safeCastUint96ToInt192(), delegate);\n        _safeMint(owner, tokenId);\n    }\n\n    /// @notice Add voting power to an existing NFT. Only callable by an\n    ///         authority.\n    /// @param tokenId The ID of the NFT to add voting power to.\n    /// @param votingPower The amount of voting power to add.\n    function increaseVotingPower(uint256 tokenId, uint96 votingPower) external {\n        _assertAuthority();\n        uint96 mintedVotingPower_ = mintedVotingPower;\n        uint96 totalVotingPower = _getSharedProposalStorage().governanceValues.totalVotingPower;\n\n        // Cap voting power to remaining unminted voting power supply. Allow\n        // minting past total voting power if minting party cards for initial\n        // crowdfund when there is no total voting power.\n        if (totalVotingPower != 0 && totalVotingPower - mintedVotingPower_ < votingPower) {\n            unchecked {\n                votingPower = totalVotingPower - mintedVotingPower_;\n            }\n        }\n\n        // Update state.\n        mintedVotingPower += votingPower;\n        uint256 newIntrinsicVotingPower = votingPowerByTokenId[tokenId] + votingPower;\n        votingPowerByTokenId[tokenId] = newIntrinsicVotingPower;\n\n        emit PartyCardIntrinsicVotingPowerSet(tokenId, newIntrinsicVotingPower);\n\n        _adjustVotingPower(ownerOf(tokenId), votingPower.safeCastUint96ToInt192(), address(0));\n\n        // Notify third-party platforms that the party NFT metadata has updated.\n        emit MetadataUpdate(tokenId);\n    }\n\n    /// @notice Remove voting power from an existing NFT. Only callable by an\n    ///         authority.\n    /// @param tokenId The ID of the NFT to remove voting power from.\n    /// @param votingPower The amount of voting power to remove.\n    function decreaseVotingPower(uint256 tokenId, uint96 votingPower) external {\n        _assertAuthority();\n        mintedVotingPower -= votingPower;\n        votingPowerByTokenId[tokenId] -= votingPower;\n\n        _adjustVotingPower(ownerOf(tokenId), -votingPower.safeCastUint96ToInt192(), address(0));\n\n        // Notify third-party platforms that the party NFT metadata has updated.\n        emit MetadataUpdate(tokenId);\n    }\n\n    /// @notice Increase the total voting power of the party. Only callable by\n    ///         an authority.\n    /// @param votingPower The new total voting power to add.\n    function increaseTotalVotingPower(uint96 votingPower) external {\n        _assertAuthority();\n        _getSharedProposalStorage().governanceValues.totalVotingPower += votingPower;\n        lastTotalVotingPowerChangeTimestamp == uint40(block.timestamp);\n\n        // Notify third-party platforms that the party NFT metadata has updated\n        // for all tokens.\n        emit BatchMetadataUpdate(0, type(uint256).max);\n    }\n\n    /// @notice Decrease the total voting power of the party. Only callable by\n    ///         an authority.\n    /// @param votingPower The new total voting power to add.\n    function decreaseTotalVotingPower(uint96 votingPower) external {\n        _assertAuthority();\n        _getSharedProposalStorage().governanceValues.totalVotingPower -= votingPower;\n        lastTotalVotingPowerChangeTimestamp == uint40(block.timestamp);\n\n        // Notify third-party platforms that the party NFT metadata has updated\n        // for all tokens.\n        emit BatchMetadataUpdate(0, type(uint256).max);\n    }\n\n    /// @notice Burn governance NFTs and remove their voting power.\n    /// @param tokenIds The IDs of the governance NFTs to burn.\n    function burn(uint256[] memory tokenIds) public {\n        _assertAuthority();\n        _burnAndUpdateVotingPower(tokenIds, false);\n    }\n\n    function _burnAndUpdateVotingPower(\n        uint256[] memory tokenIds,\n        bool checkIfAuthorizedToBurn\n    ) private returns (uint96 totalVotingPowerBurned) {\n        for (uint256 i; i < tokenIds.length; ++i) {\n            uint256 tokenId = tokenIds[i];\n            address owner = ownerOf(tokenId);\n\n            // Check if caller is authorized to burn the token.\n            if (checkIfAuthorizedToBurn) {\n                if (\n                    msg.sender != owner &&\n                    getApproved[tokenId] != msg.sender &&\n                    !isApprovedForAll[owner][msg.sender]\n                ) {\n                    revert NotAuthorized();\n                }\n            }\n\n            // Must be retrieved before updating voting power for token to be burned.\n            uint96 votingPower = votingPowerByTokenId[tokenId].safeCastUint256ToUint96();\n\n            totalVotingPowerBurned += votingPower;\n\n            // Update voting power for token to be burned.\n            delete votingPowerByTokenId[tokenId];\n            emit PartyCardIntrinsicVotingPowerSet(tokenId, 0);\n            _adjustVotingPower(owner, -votingPower.safeCastUint96ToInt192(), address(0));\n\n            // Burn token.\n            _burn(tokenId);\n\n            emit Burn(msg.sender, tokenId, votingPower);\n        }\n\n        // Update minted voting power.\n        mintedVotingPower -= totalVotingPowerBurned;\n\n        emit BatchMetadataUpdate(0, type(uint256).max);\n    }\n\n    /// @notice Burn governance NFT and remove its voting power.\n    /// @param tokenId The ID of the governance NFTs to burn.\n    function burn(uint256 tokenId) external {\n        uint256[] memory tokenIds = new uint256[](1);\n        tokenIds[0] = tokenId;\n        burn(tokenIds);\n    }\n\n    /// @notice Set the timestamp until which ragequit is enabled.\n    /// @param newRageQuitTimestamp The new ragequit timestamp.\n    function setRageQuit(uint40 newRageQuitTimestamp) external {\n        _assertHost();\n        // Prevent disabling ragequit after initialization.\n        if (newRageQuitTimestamp == DISABLE_RAGEQUIT_PERMANENTLY) {\n            revert CannotDisableRageQuitAfterInitializationError();\n        }\n\n        // Prevent enabling ragequit if distributions can be created without a vote.\n        if (\n            _getSharedProposalStorage().opts.distributionsConfig ==\n            DistributionsConfig.AllowedWithoutVote\n        ) revert CannotEnableRageQuitIfNotDistributionsRequireVoteError();\n\n        uint40 oldRageQuitTimestamp = rageQuitTimestamp;\n\n        // Prevent setting timestamp if it is permanently enabled/disabled.\n        if (\n            oldRageQuitTimestamp == ENABLE_RAGEQUIT_PERMANENTLY ||\n            oldRageQuitTimestamp == DISABLE_RAGEQUIT_PERMANENTLY\n        ) {\n            revert FixedRageQuitTimestampError(oldRageQuitTimestamp);\n        }\n\n        rageQuitTimestamp = newRageQuitTimestamp;\n\n        emit RageQuitSet(oldRageQuitTimestamp, newRageQuitTimestamp);\n    }\n\n    /// @notice Burn a governance NFT and withdraw a fair share of fungible tokens from the party.\n    /// @param tokenIds The IDs of the governance NFTs to burn.\n    /// @param withdrawTokens The fungible tokens to withdraw. Specify the\n    ///                       `ETH_ADDRESS` value to withdraw ETH.\n    /// @param minWithdrawAmounts The minimum amount of to withdraw for each token.\n    /// @param receiver The address to receive the withdrawn tokens.\n    function rageQuit(\n        uint256[] calldata tokenIds,\n        IERC20[] calldata withdrawTokens,\n        uint256[] calldata minWithdrawAmounts,\n        address receiver\n    ) external {\n        if (tokenIds.length == 0) revert NothingToBurnError();\n\n        // Check if called by an authority.\n        bool isAuthority_ = isAuthority[msg.sender];\n\n        // Check if ragequit is allowed.\n        uint40 currentRageQuitTimestamp = rageQuitTimestamp;\n        if (!isAuthority_) {\n            if (currentRageQuitTimestamp != ENABLE_RAGEQUIT_PERMANENTLY) {\n                if (\n                    currentRageQuitTimestamp == DISABLE_RAGEQUIT_PERMANENTLY ||\n                    currentRageQuitTimestamp < block.timestamp\n                ) {\n                    revert CannotRageQuitError(currentRageQuitTimestamp);\n                }\n            }\n        }\n\n        // Used as a reentrancy guard. Will be updated back after ragequit.\n        rageQuitTimestamp = DISABLE_RAGEQUIT_PERMANENTLY;\n\n        lastTotalVotingPowerChangeTimestamp = uint40(block.timestamp);\n\n        // Sum up total amount of each token to withdraw.\n        uint256[] memory withdrawAmounts = new uint256[](withdrawTokens.length);\n        {\n            IERC20 prevToken;\n            for (uint256 i; i < withdrawTokens.length; ++i) {\n                // Check if order of tokens to transfer is valid.\n                // Prevent null and duplicate transfers.\n                if (prevToken >= withdrawTokens[i]) revert InvalidTokenOrderError();\n\n                prevToken = withdrawTokens[i];\n\n                // Check token's balance.\n                uint256 balance = address(withdrawTokens[i]) == ETH_ADDRESS\n                    ? address(this).balance\n                    : withdrawTokens[i].balanceOf(address(this));\n\n                // Add fair share of tokens from the party to total.\n                for (uint256 j; j < tokenIds.length; ++j) {\n                    // Must be retrieved before burning the token.\n                    withdrawAmounts[i] += (balance * getVotingPowerShareOf(tokenIds[j])) / 1e18;\n                }\n            }\n        }\n        {\n            // Burn caller's party cards. This will revert if caller is not the\n            // the owner or approved for any of the card they are attempting to\n            // burn, not an authority, or if there are duplicate token IDs.\n            uint96 totalVotingPowerBurned = _burnAndUpdateVotingPower(tokenIds, !isAuthority_);\n\n            // Update total voting power of party.\n            _getSharedProposalStorage().governanceValues.totalVotingPower -= totalVotingPowerBurned;\n        }\n        {\n            uint16 feeBps_ = feeBps;\n            for (uint256 i; i < withdrawTokens.length; ++i) {\n                IERC20 token = withdrawTokens[i];\n                uint256 amount = withdrawAmounts[i];\n\n                // Take fee from amount.\n                uint256 fee = (amount * feeBps_) / 1e4;\n\n                if (fee > 0) {\n                    amount -= fee;\n\n                    // Transfer fee to fee recipient.\n                    if (address(token) == ETH_ADDRESS) {\n                        payable(feeRecipient).transferEth(fee);\n                    } else {\n                        token.compatTransfer(feeRecipient, fee);\n                    }\n                }\n\n                // Check amount is at least minimum.\n                uint256 minAmount = minWithdrawAmounts[i];\n                if (amount < minAmount) {\n                    revert BelowMinWithdrawAmountError(amount, minAmount);\n                }\n\n                if (amount > 0) {\n                    // Transfer token from party to recipient.\n                    if (address(token) == ETH_ADDRESS) {\n                        payable(receiver).transferEth(amount);\n                    } else {\n                        token.compatTransfer(receiver, amount);\n                    }\n                }\n            }\n        }\n\n        // Update ragequit timestamp back to before.\n        rageQuitTimestamp = currentRageQuitTimestamp;\n\n        emit RageQuit(msg.sender, tokenIds, withdrawTokens, receiver);\n    }\n\n    /// @inheritdoc ERC721\n    function transferFrom(address owner, address to, uint256 tokenId) public override {\n        // Transfer voting along with token.\n        _transferVotingPower(owner, to, votingPowerByTokenId[tokenId]);\n        super.transferFrom(owner, to, tokenId);\n    }\n\n    /// @inheritdoc ERC721\n    function safeTransferFrom(address owner, address to, uint256 tokenId) public override {\n        // super.safeTransferFrom() will call transferFrom() first which will\n        // transfer voting power.\n        super.safeTransferFrom(owner, to, tokenId);\n    }\n\n    /// @inheritdoc ERC721\n    function safeTransferFrom(\n        address owner,\n        address to,\n        uint256 tokenId,\n        bytes calldata data\n    ) public override {\n        // super.safeTransferFrom() will call transferFrom() first which will\n        // transfer voting power.\n        super.safeTransferFrom(owner, to, tokenId, data);\n    }\n\n    /// @notice Add a new authority.\n    /// @dev Used in `AddAuthorityProposal`. Only the party itself can add\n    ///      authorities to prevent it from being used anywhere else.\n    function addAuthority(address authority) external onlySelf {\n        isAuthority[authority] = true;\n\n        emit AuthorityAdded(authority);\n    }\n\n    /// @notice Relinquish the authority role.\n    function abdicateAuthority() external {\n        _assertAuthority();\n        delete isAuthority[msg.sender];\n\n        emit AuthorityRemoved(msg.sender);\n    }\n\n    function _delegateToRenderer() private view {\n        _readOnlyDelegateCall(\n            // Instance of IERC721Renderer.\n            _GLOBALS.getAddress(LibGlobals.GLOBAL_GOVERNANCE_NFT_RENDER_IMPL),\n            msg.data\n        );\n        assert(false); // Will not be reached.\n    }\n}\n"
      },
      "contracts/distribution/TokenDistributor.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"../globals/IGlobals.sol\";\nimport \"../globals/LibGlobals.sol\";\nimport \"../tokens/IERC20.sol\";\nimport \"../utils/LibAddress.sol\";\nimport \"../utils/LibERC20Compat.sol\";\nimport \"../utils/LibRawResult.sol\";\nimport \"../utils/LibSafeCast.sol\";\n\nimport \"./ITokenDistributor.sol\";\n\n/// @notice Creates token distributions for parties.\ncontract TokenDistributor is ITokenDistributor, ReentrancyGuard {\n    using LibAddress for address payable;\n    using LibERC20Compat for IERC20;\n    using LibRawResult for bytes;\n    using LibSafeCast for uint256;\n\n    struct DistributionState {\n        // The hash of the `DistributionInfo`.\n        bytes32 distributionHash;\n        // The remaining member supply.\n        uint128 remainingMemberSupply;\n        // Whether the distribution's feeRecipient has claimed its fee.\n        bool wasFeeClaimed;\n        // Whether a governance token has claimed its distribution share.\n        mapping(uint256 => bool) hasPartyTokenClaimed;\n        // Last token ID at time distribution was created that can claim.\n        // Stored here instead of in `DistributionInfo` to avoid versioning\n        // conflicts on the frontend with older parties.\n        uint96 maxTokenId;\n    }\n\n    // Arguments for `_createDistribution()`.\n    struct CreateDistributionArgs {\n        Party party;\n        TokenType tokenType;\n        address token;\n        uint256 currentTokenBalance;\n        address payable feeRecipient;\n        uint16 feeBps;\n    }\n\n    event EmergencyExecute(address target, bytes data);\n\n    error OnlyPartyDaoError(address notDao, address partyDao);\n    error InvalidDistributionInfoError(DistributionInfo info);\n    error DistributionAlreadyClaimedByPartyTokenError(uint256 distributionId, uint256 partyTokenId);\n    error DistributionFeeAlreadyClaimedError(uint256 distributionId);\n    error MustOwnTokenError(address sender, address expectedOwner, uint256 partyTokenId);\n    error TokenIdAboveMaxError(uint256 partyTokenId, uint256 maxTokenId);\n    error EmergencyActionsNotAllowedError();\n    error InvalidDistributionSupplyError(uint128 supply);\n    error OnlyFeeRecipientError(address caller, address feeRecipient);\n    error InvalidFeeBpsError(uint16 feeBps);\n\n    // Token address used to indicate a native distribution (i.e. distribution of ETH).\n    address private constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n    /// @notice The `Globals` contract storing global configuration values. This contract\n    ///         is immutable and it’s address will never change.\n    IGlobals public immutable GLOBALS;\n    /// @notice Timestamp when the DAO is no longer allowed to call emergency functions.\n    uint40 public immutable EMERGENCY_DISABLED_TIMESTAMP;\n\n    /// @notice Last distribution ID for a party.\n    mapping(Party => uint256) public lastDistributionIdPerParty;\n    /// Last known balance of a token, identified by an ID derived from the token.\n    /// Gets lazily updated when creating and claiming a distribution (transfers).\n    /// Allows one to simply transfer and call `createDistribution()` without\n    /// fussing with allowances.\n    mapping(bytes32 => uint256) private _storedBalances;\n    // tokenDistributorParty => distributionId => DistributionState\n    mapping(Party => mapping(uint256 => DistributionState)) private _distributionStates;\n\n    // msg.sender == DAO\n    modifier onlyPartyDao() {\n        {\n            address partyDao = GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);\n            if (msg.sender != partyDao) {\n                revert OnlyPartyDaoError(msg.sender, partyDao);\n            }\n        }\n        _;\n    }\n\n    // emergencyActionsDisabled == false\n    modifier onlyIfEmergencyActionsAllowed() {\n        if (block.timestamp > EMERGENCY_DISABLED_TIMESTAMP) {\n            revert EmergencyActionsNotAllowedError();\n        }\n        _;\n    }\n\n    // Set the `Globals` contract.\n    constructor(IGlobals globals, uint40 emergencyDisabledTimestamp) {\n        GLOBALS = globals;\n        EMERGENCY_DISABLED_TIMESTAMP = emergencyDisabledTimestamp;\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function createNativeDistribution(\n        Party party,\n        address payable feeRecipient,\n        uint16 feeBps\n    ) external payable nonReentrant returns (DistributionInfo memory info) {\n        info = _createDistribution(\n            CreateDistributionArgs({\n                party: party,\n                tokenType: TokenType.Native,\n                token: NATIVE_TOKEN_ADDRESS,\n                currentTokenBalance: address(this).balance,\n                feeRecipient: feeRecipient,\n                feeBps: feeBps\n            })\n        );\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function createErc20Distribution(\n        IERC20 token,\n        Party party,\n        address payable feeRecipient,\n        uint16 feeBps\n    ) external nonReentrant returns (DistributionInfo memory info) {\n        info = _createDistribution(\n            CreateDistributionArgs({\n                party: party,\n                tokenType: TokenType.Erc20,\n                token: address(token),\n                currentTokenBalance: token.balanceOf(address(this)),\n                feeRecipient: feeRecipient,\n                feeBps: feeBps\n            })\n        );\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function claim(\n        DistributionInfo calldata info,\n        uint256 partyTokenId\n    ) public nonReentrant returns (uint128 amountClaimed) {\n        // Caller must own the party token.\n        {\n            address ownerOfPartyToken = info.party.ownerOf(partyTokenId);\n            if (msg.sender != ownerOfPartyToken) {\n                revert MustOwnTokenError(msg.sender, ownerOfPartyToken, partyTokenId);\n            }\n        }\n        // DistributionInfo must be correct for this distribution ID.\n        DistributionState storage state = _distributionStates[info.party][info.distributionId];\n        if (state.distributionHash != _getDistributionHash(info)) {\n            revert InvalidDistributionInfoError(info);\n        }\n        // The partyTokenId must not have claimed its distribution yet.\n        if (state.hasPartyTokenClaimed[partyTokenId]) {\n            revert DistributionAlreadyClaimedByPartyTokenError(info.distributionId, partyTokenId);\n        }\n        // Mark the partyTokenId as having claimed their distribution.\n        state.hasPartyTokenClaimed[partyTokenId] = true;\n\n        // Compute amount owed to partyTokenId.\n        amountClaimed = getClaimAmount(info, partyTokenId);\n\n        // Cap at the remaining member supply. Otherwise a malicious\n        // party could drain more than the distribution supply.\n        uint128 remainingMemberSupply = state.remainingMemberSupply;\n        amountClaimed = amountClaimed > remainingMemberSupply\n            ? remainingMemberSupply\n            : amountClaimed;\n        state.remainingMemberSupply = remainingMemberSupply - amountClaimed;\n\n        // Transfer tokens owed.\n        _transfer(info.tokenType, info.token, payable(msg.sender), amountClaimed);\n        emit DistributionClaimedByPartyToken(\n            info.party,\n            partyTokenId,\n            msg.sender,\n            info.tokenType,\n            info.token,\n            amountClaimed\n        );\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function claimFee(\n        DistributionInfo calldata info,\n        address payable recipient\n    ) public nonReentrant {\n        // DistributionInfo must be correct for this distribution ID.\n        DistributionState storage state = _distributionStates[info.party][info.distributionId];\n        if (state.distributionHash != _getDistributionHash(info)) {\n            revert InvalidDistributionInfoError(info);\n        }\n        // Caller must be the fee recipient.\n        if (info.feeRecipient != msg.sender) {\n            revert OnlyFeeRecipientError(msg.sender, info.feeRecipient);\n        }\n        // Must not have claimed the fee yet.\n        if (state.wasFeeClaimed) {\n            revert DistributionFeeAlreadyClaimedError(info.distributionId);\n        }\n        // Mark the fee as claimed.\n        state.wasFeeClaimed = true;\n        // Transfer the tokens owed.\n        _transfer(info.tokenType, info.token, recipient, info.fee);\n        emit DistributionFeeClaimed(\n            info.party,\n            info.feeRecipient,\n            info.tokenType,\n            info.token,\n            info.fee\n        );\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function batchClaim(\n        DistributionInfo[] calldata infos,\n        uint256[] calldata partyTokenIds\n    ) external returns (uint128[] memory amountsClaimed) {\n        amountsClaimed = new uint128[](infos.length);\n        for (uint256 i = 0; i < infos.length; ++i) {\n            amountsClaimed[i] = claim(infos[i], partyTokenIds[i]);\n        }\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function batchClaimFee(\n        DistributionInfo[] calldata infos,\n        address payable[] calldata recipients\n    ) external {\n        for (uint256 i = 0; i < infos.length; ++i) {\n            claimFee(infos[i], recipients[i]);\n        }\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function getClaimAmount(\n        DistributionInfo calldata info,\n        uint256 partyTokenId\n    ) public view returns (uint128) {\n        Party party = info.party;\n\n        // Token ID must not be greater than the max token ID. If totalVotingPower hasn't increased we allow the claim.\n        uint96 maxTokenId = _distributionStates[party][info.distributionId].maxTokenId;\n        if (\n            partyTokenId > maxTokenId &&\n            info.party.getGovernanceValues().totalVotingPower > info.totalShares\n        ) {\n            revert TokenIdAboveMaxError(partyTokenId, maxTokenId);\n        }\n\n        // Check which method to use for calculating claim amount based on\n        // version of Party contract.\n        (bool success, bytes memory response) = address(party).staticcall(\n            abi.encodeCall(party.VERSION_ID, ())\n        );\n\n        // Check the version ID.\n        if (success && abi.decode(response, (uint16)) >= 1) {\n            uint256 shareOfSupply = ((info.party.getDistributionShareOf(partyTokenId)) * 1e18) /\n                info.totalShares;\n\n            return\n                // We round up here to prevent dust amounts getting trapped in this contract.\n                ((shareOfSupply * info.memberSupply + (1e18 - 1)) / 1e18)\n                    .safeCastUint256ToUint128();\n        } else {\n            // Use method of calculating claim amount for backwards\n            // compatibility with older parties where getDistributionShareOf()\n            // returned the fraction of the memberSupply partyTokenId is\n            // entitled to, scaled by 1e18.\n            uint256 shareOfSupply = party.getDistributionShareOf(partyTokenId);\n\n            return\n                // We round up here to prevent dust amounts getting trapped in this contract.\n                ((shareOfSupply * info.memberSupply + (1e18 - 1)) / 1e18)\n                    .safeCastUint256ToUint128();\n        }\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function wasFeeClaimed(Party party, uint256 distributionId) external view returns (bool) {\n        return _distributionStates[party][distributionId].wasFeeClaimed;\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function hasPartyTokenIdClaimed(\n        Party party,\n        uint256 partyTokenId,\n        uint256 distributionId\n    ) external view returns (bool) {\n        return _distributionStates[party][distributionId].hasPartyTokenClaimed[partyTokenId];\n    }\n\n    /// @inheritdoc ITokenDistributor\n    function getRemainingMemberSupply(\n        Party party,\n        uint256 distributionId\n    ) external view returns (uint128) {\n        return _distributionStates[party][distributionId].remainingMemberSupply;\n    }\n\n    /// @notice As the DAO, execute an arbitrary delegatecall from this contract.\n    /// @dev Emergency actions must not be revoked for this to work.\n    /// @param targetAddress The contract to delegatecall into.\n    /// @param targetCallData The data to pass to the call.\n    function emergencyExecute(\n        address targetAddress,\n        bytes calldata targetCallData\n    ) external onlyPartyDao onlyIfEmergencyActionsAllowed {\n        (bool success, bytes memory res) = targetAddress.delegatecall(targetCallData);\n        if (!success) {\n            res.rawRevert();\n        }\n        emit EmergencyExecute(targetAddress, targetCallData);\n    }\n\n    function _createDistribution(\n        CreateDistributionArgs memory args\n    ) private returns (DistributionInfo memory info) {\n        if (args.feeBps > 1e4) {\n            revert InvalidFeeBpsError(args.feeBps);\n        }\n        uint128 supply;\n        {\n            bytes32 balanceId = _getBalanceId(args.tokenType, args.token);\n            supply = (args.currentTokenBalance - _storedBalances[balanceId])\n                .safeCastUint256ToUint128();\n            // Supply must be nonzero.\n            if (supply == 0) {\n                revert InvalidDistributionSupplyError(supply);\n            }\n            // Update stored balance.\n            _storedBalances[balanceId] = args.currentTokenBalance;\n        }\n\n        // Create a distribution.\n        uint128 fee = (supply * args.feeBps) / 1e4;\n        uint128 memberSupply = supply - fee;\n\n        info = DistributionInfo({\n            tokenType: args.tokenType,\n            distributionId: ++lastDistributionIdPerParty[args.party],\n            token: args.token,\n            party: args.party,\n            memberSupply: memberSupply,\n            feeRecipient: args.feeRecipient,\n            fee: fee,\n            totalShares: args.party.getGovernanceValues().totalVotingPower\n        });\n\n        DistributionState storage state = _distributionStates[args.party][info.distributionId];\n        state.distributionHash = _getDistributionHash(info);\n        state.remainingMemberSupply = memberSupply;\n        state.maxTokenId = args.party.tokenCount();\n\n        emit DistributionCreated(args.party, info);\n    }\n\n    function _transfer(\n        TokenType tokenType,\n        address token,\n        address payable recipient,\n        uint256 amount\n    ) private {\n        bytes32 balanceId = _getBalanceId(tokenType, token);\n        _storedBalances[balanceId] -= amount;\n        if (tokenType == TokenType.Native) {\n            recipient.transferEth(amount);\n        } else {\n            assert(tokenType == TokenType.Erc20);\n            IERC20(token).compatTransfer(recipient, amount);\n        }\n    }\n\n    function _getDistributionHash(\n        DistributionInfo memory info\n    ) internal pure returns (bytes32 hash) {\n        assembly {\n            hash := keccak256(info, 0x100)\n        }\n    }\n\n    function _getBalanceId(\n        TokenType tokenType,\n        address token\n    ) private pure returns (bytes32 balanceId) {\n        if (tokenType == TokenType.Native) {\n            return bytes32(uint256(uint160(NATIVE_TOKEN_ADDRESS)));\n        }\n        assert(tokenType == TokenType.Erc20);\n        return bytes32(uint256(uint160(token)));\n    }\n}\n"
      },
      "contracts/globals/IGlobals.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../utils/Implementation.sol\";\n\n// Single registry of global values controlled by multisig.\n// See `LibGlobals` for all valid keys.\ninterface IGlobals {\n    function multiSig() external view returns (address);\n\n    function getBytes32(uint256 key) external view returns (bytes32);\n\n    function getUint256(uint256 key) external view returns (uint256);\n\n    function getBool(uint256 key) external view returns (bool);\n\n    function getAddress(uint256 key) external view returns (address);\n\n    function getImplementation(uint256 key) external view returns (Implementation);\n\n    function getIncludesBytes32(uint256 key, bytes32 value) external view returns (bool);\n\n    function getIncludesUint256(uint256 key, uint256 value) external view returns (bool);\n\n    function getIncludesAddress(uint256 key, address value) external view returns (bool);\n\n    function setBytes32(uint256 key, bytes32 value) external;\n\n    function setUint256(uint256 key, uint256 value) external;\n\n    function setBool(uint256 key, bool value) external;\n\n    function setAddress(uint256 key, address value) external;\n\n    function setIncludesBytes32(uint256 key, bytes32 value, bool isIncluded) external;\n\n    function setIncludesUint256(uint256 key, uint256 value, bool isIncluded) external;\n\n    function setIncludesAddress(uint256 key, address value, bool isIncluded) external;\n}\n"
      },
      "contracts/globals/LibGlobals.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\n// Valid keys in `IGlobals`. Append-only.\nlibrary LibGlobals {\n    // The Globals commented out below were depreciated in 1.2; factories\n    // can now choose the implementation address to deploy and no longer\n    // deploy the latest implementation. They will no longer be updated\n    // in future releases.\n    //\n    // See https://github.com/PartyDAO/party-migrations for\n    // implementation addresses by release.\n\n    uint256 internal constant GLOBAL_PARTY_IMPL = 1;\n    uint256 internal constant GLOBAL_PROPOSAL_ENGINE_IMPL = 2;\n    uint256 internal constant GLOBAL_PARTY_FACTORY = 3;\n    uint256 internal constant GLOBAL_GOVERNANCE_NFT_RENDER_IMPL = 4;\n    uint256 internal constant GLOBAL_CF_NFT_RENDER_IMPL = 5;\n    uint256 internal constant GLOBAL_OS_ZORA_AUCTION_TIMEOUT = 6;\n    uint256 internal constant GLOBAL_OS_ZORA_AUCTION_DURATION = 7;\n    // uint256 internal constant GLOBAL_AUCTION_CF_IMPL = 8;\n    // uint256 internal constant GLOBAL_BUY_CF_IMPL = 9;\n    // uint256 internal constant GLOBAL_COLLECTION_BUY_CF_IMPL = 10;\n    uint256 internal constant GLOBAL_DAO_WALLET = 11;\n    uint256 internal constant GLOBAL_TOKEN_DISTRIBUTOR = 12;\n    uint256 internal constant GLOBAL_OPENSEA_CONDUIT_KEY = 13;\n    uint256 internal constant GLOBAL_OPENSEA_ZONE = 14;\n    uint256 internal constant GLOBAL_PROPOSAL_MAX_CANCEL_DURATION = 15;\n    uint256 internal constant GLOBAL_ZORA_MIN_AUCTION_DURATION = 16;\n    uint256 internal constant GLOBAL_ZORA_MAX_AUCTION_DURATION = 17;\n    uint256 internal constant GLOBAL_ZORA_MAX_AUCTION_TIMEOUT = 18;\n    uint256 internal constant GLOBAL_OS_MIN_ORDER_DURATION = 19;\n    uint256 internal constant GLOBAL_OS_MAX_ORDER_DURATION = 20;\n    uint256 internal constant GLOBAL_DISABLE_PARTY_ACTIONS = 21;\n    uint256 internal constant GLOBAL_RENDERER_STORAGE = 22;\n    uint256 internal constant GLOBAL_PROPOSAL_MIN_CANCEL_DURATION = 23;\n    // uint256 internal constant GLOBAL_ROLLING_AUCTION_CF_IMPL = 24;\n    // uint256 internal constant GLOBAL_COLLECTION_BATCH_BUY_CF_IMPL = 25;\n    uint256 internal constant GLOBAL_METADATA_REGISTRY = 26;\n    // uint256 internal constant GLOBAL_CROWDFUND_FACTORY = 27;\n    // uint256 internal constant GLOBAL_INITIAL_ETH_CF_IMPL = 28;\n    // uint256 internal constant GLOBAL_RERAISE_ETH_CF_IMPL = 29;\n    uint256 internal constant GLOBAL_SEAPORT = 30;\n    uint256 internal constant GLOBAL_CONDUIT_CONTROLLER = 31;\n    uint256 internal constant GLOBAL_OFF_CHAIN_SIGNATURE_VALIDATOR = 32;\n}\n"
      },
      "contracts/renderers/fonts/IFont.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @title The interface on-chain font contracts must implement to be added to the registry.\n///\n/// Uploading fonts to chain is open ended and up to the dev (SSTORE2 or hardcoded string or etc).\n///\n/// As long as the font contract implements this interface and has immutable font data, it can be added\n/// to the registry.\n///\n/// @author @0x_beans\ninterface IFont {\n    /// @notice Address that uploaded font for credits\n    function fontUploader() external returns (address);\n\n    /// @notice Format type of font (e.g. ttf, woff, otf, etc). Must be lowercase.\n    ///         This info is necessary so projects know how to properly render the fonts.\n    function fontFormatType() external returns (string memory);\n\n    /// @notice Font name (ie. 'space-grotesk'). Must be lowercase.\n    function fontName() external returns (string memory);\n\n    /// @notice Weight used by the font (e.g. bold, medium, light, etc). Must be lowercase.\n    ///         Necessary to differentiate uploaded fonts that are the same but different weights.\n    function fontWeight() external returns (string memory);\n\n    /// @notice Style used by the font (e.g. lowercase normal, italic, oblique, etc). Must be lowercase.\n    //          Necessary to differentiate uploaded fonts that are the same but different style.\n    function fontStyle() external returns (string memory);\n\n    /// @notice The full base64 encoded font with data URI scheme prefix\n    ///         (e.g. 'data:font/ttf;charset=utf-8;base64,').\n    function getFont() external view returns (string memory);\n}\n"
      },
      "contracts/party/Party.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../tokens/IERC721.sol\";\n\nimport \"./PartyGovernanceNFT.sol\";\nimport \"./PartyGovernance.sol\";\n\n/// @notice The governance contract that also custodies the precious NFTs. This\n///         is also the Governance NFT 721 contract.\ncontract Party is PartyGovernanceNFT {\n    // Arguments used to initialize the party.\n    struct PartyOptions {\n        PartyGovernance.GovernanceOpts governance;\n        ProposalStorage.ProposalEngineOpts proposalEngine;\n        string name;\n        string symbol;\n        uint256 customizationPresetId;\n    }\n\n    // Arguments used to initialize the `PartyGovernanceNFT`.\n    struct PartyInitData {\n        PartyOptions options;\n        IERC721[] preciousTokens;\n        uint256[] preciousTokenIds;\n        address[] authorities;\n        uint40 rageQuitTimestamp;\n    }\n\n    /// @notice Version ID of the party implementation contract.\n    uint16 public constant VERSION_ID = 2;\n\n    // Set the `Globals` contract.\n    constructor(IGlobals globals) PartyGovernanceNFT(globals) {}\n\n    /// @notice Initializer to be called prior to using the contract.\n    /// @param initData Options used to initialize the party governance.\n    function initialize(PartyInitData memory initData) external onlyInitialize {\n        PartyGovernanceNFT._initialize(\n            initData.options.name,\n            initData.options.symbol,\n            initData.options.customizationPresetId,\n            initData.options.governance,\n            initData.options.proposalEngine,\n            initData.preciousTokens,\n            initData.preciousTokenIds,\n            initData.authorities,\n            initData.rageQuitTimestamp\n        );\n    }\n\n    receive() external payable {}\n}\n"
      },
      "contracts/renderers/IERC721Renderer.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.20;\n\ninterface IERC721Renderer {\n    function tokenURI(uint256 tokenId) external view returns (string memory);\n\n    function contractURI() external view returns (string memory);\n}\n"
      },
      "lib/solmate/src/utils/SSTORE2.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Read and write to persistent storage at a fraction of the cost.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)\n/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)\nlibrary SSTORE2 {\n    uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.\n\n    /*//////////////////////////////////////////////////////////////\n                               WRITE LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function write(bytes memory data) internal returns (address pointer) {\n        // Prefix the bytecode with a STOP opcode to ensure it cannot be called.\n        bytes memory runtimeCode = abi.encodePacked(hex\"00\", data);\n\n        bytes memory creationCode = abi.encodePacked(\n            //---------------------------------------------------------------------------------------------------------------//\n            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //\n            //---------------------------------------------------------------------------------------------------------------//\n            // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //\n            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //\n            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //\n            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //\n            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //\n            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //\n            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //\n            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //\n            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //\n            // 0xf3    |  0xf3               | RETURN       |                                                                //\n            //---------------------------------------------------------------------------------------------------------------//\n            hex\"60_0B_59_81_38_03_80_92_59_39_F3\", // Returns all code in the contract except for the first 11 (0B in hex) bytes.\n            runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.\n        );\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Deploy a new contract with the generated creation code.\n            // We start 32 bytes into the code to avoid copying the byte length.\n            pointer := create(0, add(creationCode, 32), mload(creationCode))\n        }\n\n        require(pointer != address(0), \"DEPLOYMENT_FAILED\");\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               READ LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function read(address pointer) internal view returns (bytes memory) {\n        return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);\n    }\n\n    function read(address pointer, uint256 start) internal view returns (bytes memory) {\n        start += DATA_OFFSET;\n\n        return readBytecode(pointer, start, pointer.code.length - start);\n    }\n\n    function read(\n        address pointer,\n        uint256 start,\n        uint256 end\n    ) internal view returns (bytes memory) {\n        start += DATA_OFFSET;\n        end += DATA_OFFSET;\n\n        require(pointer.code.length >= end, \"OUT_OF_BOUNDS\");\n\n        return readBytecode(pointer, start, end - start);\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                          INTERNAL HELPER LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function readBytecode(\n        address pointer,\n        uint256 start,\n        uint256 size\n    ) private view returns (bytes memory data) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Get a pointer to some free memory.\n            data := mload(0x40)\n\n            // Update the free memory pointer to prevent overriding our data.\n            // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).\n            // Adding 31 to size and running the result through the logic above ensures\n            // the memory pointer remains word-aligned, following the Solidity convention.\n            mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))\n\n            // Store the size of the data in the first 32 byte chunk of free memory.\n            mstore(data, size)\n\n            // Copy the code into memory right after the 32 bytes we used to store the size.\n            extcodecopy(pointer, add(data, 32), start, size)\n        }\n    }\n}\n"
      },
      "contracts/utils/Multicall.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../utils/LibRawResult.sol\";\n\nabstract contract Multicall {\n    using LibRawResult for bytes;\n\n    /// @notice Perform multiple delegatecalls on ourselves.\n    function multicall(bytes[] calldata multicallData) external {\n        for (uint256 i; i < multicallData.length; ++i) {\n            (bool s, bytes memory r) = address(this).delegatecall(multicallData[i]);\n            if (!s) {\n                r.rawRevert();\n            }\n        }\n    }\n}\n"
      },
      "contracts/renderers/IMetadataProvider.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8;\n\ninterface IMetadataProvider {\n    /// @notice Whether or not the metadata provider supports registrars that can\n    ///         set metadata for other instances.\n    /// @dev See `MetadataRegistry` for more information on the registrar role.\n    function supportsRegistrars() external view returns (bool);\n\n    /// @notice Get the metadata for a Party instance.\n    /// @param instance The address of the instance.\n    /// @param tokenId The ID of the token to get the metadata for.\n    /// @return metadata The encoded metadata.\n    function getMetadata(\n        address instance,\n        uint256 tokenId\n    ) external view returns (bytes memory metadata);\n}\n"
      },
      "contracts/distribution/ITokenDistributor.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../tokens/IERC20.sol\";\n\nimport \"../party/Party.sol\";\n\n/// @notice Creates token distributions for parties.\ninterface ITokenDistributor {\n    enum TokenType {\n        Native,\n        Erc20\n    }\n\n    // Info on a distribution, created by createDistribution().\n    struct DistributionInfo {\n        // Type of distribution/token.\n        TokenType tokenType;\n        // ID of the distribution. Assigned by createDistribution().\n        uint256 distributionId;\n        // The party whose members can claim the distribution.\n        Party party;\n        // Who can claim `fee`.\n        address payable feeRecipient;\n        // The token being distributed.\n        address token;\n        // Total amount of `token` that can be claimed by party members.\n        uint128 memberSupply;\n        // Amount of `token` to be redeemed by `feeRecipient`.\n        uint128 fee;\n        // Total shares at time distribution was created.\n        uint96 totalShares;\n    }\n\n    event DistributionCreated(Party indexed party, DistributionInfo info);\n    event DistributionFeeClaimed(\n        Party indexed party,\n        address indexed feeRecipient,\n        TokenType tokenType,\n        address token,\n        uint256 amount\n    );\n    event DistributionClaimedByPartyToken(\n        Party indexed party,\n        uint256 indexed partyTokenId,\n        address indexed owner,\n        TokenType tokenType,\n        address token,\n        uint256 amountClaimed\n    );\n\n    /// @notice Create a new distribution for an outstanding native token balance\n    ///         governed by a party.\n    /// @dev Native tokens should be transferred directly into this contract\n    ///      immediately prior (same tx) to calling `createDistribution()` or\n    ///      attached to the call itself.\n    /// @param party The party whose members can claim the distribution.\n    /// @param feeRecipient Who can claim `fee`.\n    /// @param feeBps Percentage (in bps) of the distribution `feeRecipient` receives.\n    /// @return info Information on the created distribution.\n    function createNativeDistribution(\n        Party party,\n        address payable feeRecipient,\n        uint16 feeBps\n    ) external payable returns (DistributionInfo memory info);\n\n    /// @notice Create a new distribution for an outstanding ERC20 token balance\n    ///         governed by a party.\n    /// @dev ERC20 tokens should be transferred directly into this contract\n    ///      immediately prior (same tx) to calling `createDistribution()` or\n    ///      attached to the call itself.\n    /// @param token The ERC20 token to distribute.\n    /// @param party The party whose members can claim the distribution.\n    /// @param feeRecipient Who can claim `fee`.\n    /// @param feeBps Percentage (in bps) of the distribution `feeRecipient` receives.\n    /// @return info Information on the created distribution.\n    function createErc20Distribution(\n        IERC20 token,\n        Party party,\n        address payable feeRecipient,\n        uint16 feeBps\n    ) external returns (DistributionInfo memory info);\n\n    /// @notice Claim a portion of a distribution owed to a `partyTokenId` belonging\n    ///         to the party that created the distribution. The caller\n    ///         must own this token.\n    /// @param info Information on the distribution being claimed.\n    /// @param partyTokenId The ID of the party token to claim for.\n    /// @return amountClaimed The amount of the distribution claimed.\n    function claim(\n        DistributionInfo calldata info,\n        uint256 partyTokenId\n    ) external returns (uint128 amountClaimed);\n\n    /// @notice Claim the fee for a distribution. Only a distribution's `feeRecipient`\n    ///         can call this.\n    /// @param info Information on the distribution being claimed.\n    /// @param recipient The address to send the fee to.\n    function claimFee(DistributionInfo calldata info, address payable recipient) external;\n\n    /// @notice Batch version of `claim()`.\n    /// @param infos Information on the distributions being claimed.\n    /// @param partyTokenIds The ID of the party tokens to claim for.\n    /// @return amountsClaimed The amount of the distributions claimed.\n    function batchClaim(\n        DistributionInfo[] calldata infos,\n        uint256[] calldata partyTokenIds\n    ) external returns (uint128[] memory amountsClaimed);\n\n    /// @notice Batch version of `claimFee()`.\n    /// @param infos Information on the distributions to claim fees for.\n    /// @param recipients The addresses to send the fees to.\n    function batchClaimFee(\n        DistributionInfo[] calldata infos,\n        address payable[] calldata recipients\n    ) external;\n\n    /// @notice Compute the amount of a distribution's token are owed to a party\n    ///         member, identified by the `partyTokenId`.\n    /// @param info Information on the distribution being claimed.\n    /// @param partyTokenId The ID of the party token to claim for.\n    /// @return claimAmount The amount of the distribution owed to the party member.\n    function getClaimAmount(\n        DistributionInfo calldata info,\n        uint256 partyTokenId\n    ) external view returns (uint128);\n\n    /// @notice Check whether the fee has been claimed for a distribution.\n    /// @param party The party to use for checking whether the fee has been claimed.\n    /// @param distributionId The ID of the distribution to check.\n    /// @return feeClaimed Whether the fee has been claimed.\n    function wasFeeClaimed(Party party, uint256 distributionId) external view returns (bool);\n\n    /// @notice Check whether a `partyTokenId` has claimed their share of a distribution.\n    /// @param party The party to use for checking whether the `partyTokenId` has claimed.\n    /// @param partyTokenId The ID of the party token to check.\n    /// @param distributionId The ID of the distribution to check.\n    /// @return hasClaimed Whether the `partyTokenId` has claimed.\n    function hasPartyTokenIdClaimed(\n        Party party,\n        uint256 partyTokenId,\n        uint256 distributionId\n    ) external view returns (bool);\n\n    /// @notice Get how much unclaimed member tokens are left in a distribution.\n    /// @param party The party to use for checking the unclaimed member tokens.\n    /// @param distributionId The ID of the distribution to check.\n    /// @return remainingMemberSupply The amount of distribution supply remaining.\n    function getRemainingMemberSupply(\n        Party party,\n        uint256 distributionId\n    ) external view returns (uint128);\n}\n"
      },
      "contracts/utils/ReadOnlyDelegateCall.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"./LibRawResult.sol\";\n\ninterface IReadOnlyDelegateCall {\n    // Marked `view` so that `_readOnlyDelegateCall` can be `view` as well.\n    function delegateCallAndRevert(address impl, bytes memory callData) external view;\n}\n\n// Inherited by contracts to perform read-only delegate calls.\nabstract contract ReadOnlyDelegateCall {\n    using LibRawResult for bytes;\n\n    // Delegatecall into implement and revert with the raw result.\n    function delegateCallAndRevert(address impl, bytes memory callData) external {\n        // Attempt to gate to only `_readOnlyDelegateCall()` invocations.\n        require(msg.sender == address(this));\n        (bool s, bytes memory r) = impl.delegatecall(callData);\n        // Revert with success status and return data.\n        abi.encode(s, r).rawRevert();\n    }\n\n    // Perform a `delegateCallAndRevert()` then return the raw result data.\n    function _readOnlyDelegateCall(address impl, bytes memory callData) internal view {\n        try IReadOnlyDelegateCall(address(this)).delegateCallAndRevert(impl, callData) {\n            // Should never happen.\n            assert(false);\n        } catch (bytes memory r) {\n            (bool success, bytes memory resultData) = abi.decode(r, (bool, bytes));\n            if (!success) {\n                resultData.rawRevert();\n            }\n            resultData.rawReturn();\n        }\n    }\n}\n"
      },
      "contracts/tokens/IERC721.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity ^0.8;\n\n// Minimal ERC721 interface.\ninterface IERC721 {\n    event Transfer(address indexed owner, address indexed to, uint256 indexed tokenId);\n    event Approval(address indexed owner, address indexed operator, uint256 indexed tokenId);\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    function safeTransferFrom(\n        address from,\n        address to,\n        uint256 tokenId,\n        bytes calldata data\n    ) external;\n\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    function approve(address operator, uint256 tokenId) external;\n\n    function setApprovalForAll(address operator, bool isApproved) external;\n\n    function name() external view returns (string memory);\n\n    function symbol() external view returns (string memory);\n\n    function getApproved(uint256 tokenId) external view returns (address);\n\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n    function ownerOf(uint256 tokenId) external view returns (address);\n\n    function balanceOf(address owner) external view returns (uint256);\n}\n"
      },
      "contracts/tokens/IERC20.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity ^0.8;\n\n// Minimal ERC20 interface.\ninterface IERC20 {\n    event Transfer(address indexed owner, address indexed to, uint256 amount);\n    event Approval(address indexed owner, address indexed spender, uint256 allowance);\n\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n\n    function approve(address spender, uint256 allowance) external returns (bool);\n\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    function balanceOf(address owner) external view returns (uint256);\n}\n"
      },
      "contracts/tokens/IERC721Receiver.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity ^0.8;\n\ninterface IERC721Receiver {\n    function onERC721Received(\n        address operator,\n        address from,\n        uint256 tokenId,\n        bytes memory data\n    ) external returns (bytes4);\n}\n"
      },
      "contracts/vendor/solmate/ERC1155.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\n// Only modified to inherit IERC1155 and rename ERC1155TokenReceiver -> ERC1155TokenReceiverBase.\npragma solidity ^0.8;\n\nimport \"../../tokens/IERC1155.sol\";\n\n/// @notice Minimalist and gas efficient standard ERC1155 implementation.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)\nabstract contract ERC1155 is IERC1155 {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event URI(string value, uint256 indexed id);\n\n    /*//////////////////////////////////////////////////////////////\n                             ERC1155 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    mapping(address => mapping(uint256 => uint256)) public balanceOf;\n\n    mapping(address => mapping(address => bool)) public isApprovedForAll;\n\n    /*//////////////////////////////////////////////////////////////\n                             METADATA LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function uri(uint256 id) public view virtual returns (string memory);\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC1155 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function setApprovalForAll(address operator, bool approved) public virtual {\n        isApprovedForAll[msg.sender][operator] = approved;\n\n        emit ApprovalForAll(msg.sender, operator, approved);\n    }\n\n    function safeTransferFrom(\n        address from,\n        address to,\n        uint256 id,\n        uint256 amount,\n        bytes calldata data\n    ) public virtual {\n        require(msg.sender == from || isApprovedForAll[from][msg.sender], \"NOT_AUTHORIZED\");\n\n        balanceOf[from][id] -= amount;\n        balanceOf[to][id] += amount;\n\n        emit TransferSingle(msg.sender, from, to, id, amount);\n\n        require(\n            to.code.length == 0\n                ? to != address(0)\n                : ERC1155TokenReceiverBase(to).onERC1155Received(\n                    msg.sender,\n                    from,\n                    id,\n                    amount,\n                    data\n                ) == ERC1155TokenReceiverBase.onERC1155Received.selector,\n            \"UNSAFE_RECIPIENT\"\n        );\n    }\n\n    function safeBatchTransferFrom(\n        address from,\n        address to,\n        uint256[] calldata ids,\n        uint256[] calldata amounts,\n        bytes calldata data\n    ) public virtual {\n        require(ids.length == amounts.length, \"LENGTH_MISMATCH\");\n\n        require(msg.sender == from || isApprovedForAll[from][msg.sender], \"NOT_AUTHORIZED\");\n\n        // Storing these outside the loop saves ~15 gas per iteration.\n        uint256 id;\n        uint256 amount;\n\n        for (uint256 i; i < ids.length; ) {\n            id = ids[i];\n            amount = amounts[i];\n\n            balanceOf[from][id] -= amount;\n            balanceOf[to][id] += amount;\n\n            // An array can't have a total length\n            // larger than the max uint256 value.\n            unchecked {\n                ++i;\n            }\n        }\n\n        emit TransferBatch(msg.sender, from, to, ids, amounts);\n\n        require(\n            to.code.length == 0\n                ? to != address(0)\n                : ERC1155TokenReceiverBase(to).onERC1155BatchReceived(\n                    msg.sender,\n                    from,\n                    ids,\n                    amounts,\n                    data\n                ) == ERC1155TokenReceiverBase.onERC1155BatchReceived.selector,\n            \"UNSAFE_RECIPIENT\"\n        );\n    }\n\n    function balanceOfBatch(\n        address[] calldata owners,\n        uint256[] calldata ids\n    ) public view virtual returns (uint256[] memory balances) {\n        require(owners.length == ids.length, \"LENGTH_MISMATCH\");\n\n        balances = new uint256[](owners.length);\n\n        // Unchecked because the only math done is incrementing\n        // the array index counter which cannot possibly overflow.\n        unchecked {\n            for (uint256 i; i < owners.length; ++i) {\n                balances[i] = balanceOf[owners[i]][ids[i]];\n            }\n        }\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC165 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n        return\n            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165\n            interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155\n            interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL MINT/BURN LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {\n        balanceOf[to][id] += amount;\n\n        emit TransferSingle(msg.sender, address(0), to, id, amount);\n\n        require(\n            to.code.length == 0\n                ? to != address(0)\n                : ERC1155TokenReceiverBase(to).onERC1155Received(\n                    msg.sender,\n                    address(0),\n                    id,\n                    amount,\n                    data\n                ) == ERC1155TokenReceiverBase.onERC1155Received.selector,\n            \"UNSAFE_RECIPIENT\"\n        );\n    }\n\n    function _batchMint(\n        address to,\n        uint256[] memory ids,\n        uint256[] memory amounts,\n        bytes memory data\n    ) internal virtual {\n        uint256 idsLength = ids.length; // Saves MLOADs.\n\n        require(idsLength == amounts.length, \"LENGTH_MISMATCH\");\n\n        for (uint256 i; i < idsLength; ) {\n            balanceOf[to][ids[i]] += amounts[i];\n\n            // An array can't have a total length\n            // larger than the max uint256 value.\n            unchecked {\n                ++i;\n            }\n        }\n\n        emit TransferBatch(msg.sender, address(0), to, ids, amounts);\n\n        require(\n            to.code.length == 0\n                ? to != address(0)\n                : ERC1155TokenReceiverBase(to).onERC1155BatchReceived(\n                    msg.sender,\n                    address(0),\n                    ids,\n                    amounts,\n                    data\n                ) == ERC1155TokenReceiverBase.onERC1155BatchReceived.selector,\n            \"UNSAFE_RECIPIENT\"\n        );\n    }\n\n    function _batchBurn(\n        address from,\n        uint256[] memory ids,\n        uint256[] memory amounts\n    ) internal virtual {\n        uint256 idsLength = ids.length; // Saves MLOADs.\n\n        require(idsLength == amounts.length, \"LENGTH_MISMATCH\");\n\n        for (uint256 i; i < idsLength; ) {\n            balanceOf[from][ids[i]] -= amounts[i];\n\n            // An array can't have a total length\n            // larger than the max uint256 value.\n            unchecked {\n                ++i;\n            }\n        }\n\n        emit TransferBatch(msg.sender, from, address(0), ids, amounts);\n    }\n\n    function _burn(address from, uint256 id, uint256 amount) internal virtual {\n        balanceOf[from][id] -= amount;\n\n        emit TransferSingle(msg.sender, from, address(0), id, amount);\n    }\n}\n\n/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)\nabstract contract ERC1155TokenReceiverBase {\n    function onERC1155Received(\n        address,\n        address,\n        uint256,\n        uint256,\n        bytes calldata\n    ) external virtual returns (bytes4) {\n        return ERC1155TokenReceiverBase.onERC1155Received.selector;\n    }\n\n    function onERC1155BatchReceived(\n        address,\n        address,\n        uint256[] calldata,\n        uint256[] calldata,\n        bytes calldata\n    ) external virtual returns (bytes4) {\n        return ERC1155TokenReceiverBase.onERC1155BatchReceived.selector;\n    }\n}\n"
      },
      "contracts/utils/LibERC20Compat.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../tokens/IERC20.sol\";\n\n// Compatibility helpers for ERC20s.\nlibrary LibERC20Compat {\n    error NotATokenError(IERC20 token);\n    error TokenTransferFailedError(IERC20 token, address to, uint256 amount);\n    error TokenApprovalFailed(IERC20 token, address spender, uint256 amount);\n\n    // Perform an `IERC20.transfer()` handling non-compliant implementations.\n    function compatTransfer(IERC20 token, address to, uint256 amount) internal {\n        (bool s, bytes memory r) = address(token).call(\n            abi.encodeCall(IERC20.transfer, (to, amount))\n        );\n        if (s) {\n            if (r.length == 0) {\n                uint256 cs;\n                assembly {\n                    cs := extcodesize(token)\n                }\n                if (cs == 0) {\n                    revert NotATokenError(token);\n                }\n                return;\n            }\n            if (abi.decode(r, (bool))) {\n                return;\n            }\n        }\n        revert TokenTransferFailedError(token, to, amount);\n    }\n\n    // Perform an `IERC20.transferFrom()` handling non-compliant implementations.\n    function compatTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {\n        (bool s, bytes memory r) = address(token).call(\n            abi.encodeCall(IERC20.transferFrom, (from, to, amount))\n        );\n        if (s) {\n            if (r.length == 0) {\n                uint256 cs;\n                assembly {\n                    cs := extcodesize(token)\n                }\n                if (cs == 0) {\n                    revert NotATokenError(token);\n                }\n                return;\n            }\n            if (abi.decode(r, (bool))) {\n                return;\n            }\n        }\n        revert TokenTransferFailedError(token, to, amount);\n    }\n\n    function compatApprove(IERC20 token, address spender, uint256 amount) internal {\n        (bool s, bytes memory r) = address(token).call(\n            abi.encodeCall(IERC20.approve, (spender, amount))\n        );\n        if (s) {\n            if (r.length == 0) {\n                uint256 cs;\n                assembly {\n                    cs := extcodesize(token)\n                }\n                if (cs == 0) {\n                    revert NotATokenError(token);\n                }\n                return;\n            }\n            if (abi.decode(r, (bool))) {\n                return;\n            }\n        }\n        revert TokenApprovalFailed(token, spender, amount);\n    }\n}\n"
      },
      "contracts/utils/IERC4906.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity ^0.8;\n\ninterface IERC4906 {\n    event MetadataUpdate(uint256 _tokenId);\n\n    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);\n}\n"
      },
      "contracts/proposals/IProposalExecutionEngine.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../tokens/IERC721.sol\";\n\n// Upgradeable proposals logic contract interface.\ninterface IProposalExecutionEngine {\n    struct ExecuteProposalParams {\n        uint256 proposalId;\n        bytes proposalData;\n        bytes progressData;\n        bytes extraData;\n        uint256 flags;\n        IERC721[] preciousTokens;\n        uint256[] preciousTokenIds;\n    }\n\n    function initialize(address oldImpl, bytes memory initData) external;\n\n    /// @notice Execute a proposal.\n    /// @dev Must be delegatecalled into by PartyGovernance.\n    ///      If the proposal is incomplete, continues its next step (if possible).\n    ///      If another proposal is incomplete, this will fail. Only one\n    ///      incomplete proposal is allowed at a time.\n    /// @param params The data needed to execute the proposal.\n    /// @return nextProgressData Bytes to be passed into the next `execute()` call,\n    ///         if the proposal execution is incomplete. Otherwise, empty bytes\n    ///         to indicate the proposal is complete.\n    function executeProposal(\n        ExecuteProposalParams memory params\n    ) external payable returns (bytes memory nextProgressData);\n\n    /// @notice Forcibly cancel an incomplete proposal.\n    /// @param proposalId The ID of the proposal to cancel.\n    /// @dev This is intended to be a last resort as it can leave a party in a\n    ///      broken step. Whenever possible, proposals should be allowed to\n    ///      complete their entire lifecycle.\n    function cancelProposal(uint256 proposalId) external;\n}\n"
      },
      "contracts/proposals/LibProposal.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"../tokens/IERC721.sol\";\n\nlibrary LibProposal {\n    uint256 internal constant PROPOSAL_FLAG_UNANIMOUS = 0x1;\n    uint256 internal constant PROPOSAL_FLAG_HOSTS_ACCEPT = 0x2;\n\n    function isTokenPrecious(\n        IERC721 token,\n        IERC721[] memory preciousTokens\n    ) internal pure returns (bool) {\n        for (uint256 i; i < preciousTokens.length; ++i) {\n            if (token == preciousTokens[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    function isTokenIdPrecious(\n        IERC721 token,\n        uint256 tokenId,\n        IERC721[] memory preciousTokens,\n        uint256[] memory preciousTokenIds\n    ) internal pure returns (bool) {\n        for (uint256 i; i < preciousTokens.length; ++i) {\n            if (token == preciousTokens[i] && tokenId == preciousTokenIds[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
      },
      "contracts/proposals/ProposalStorage.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nimport \"./IProposalExecutionEngine.sol\";\nimport \"../utils/LibRawResult.sol\";\n\n// The storage bucket shared by `PartyGovernance` and the `ProposalExecutionEngine`.\n// Read this for more context on the pattern motivating this:\n// https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/explicit-storage-buckets\nabstract contract ProposalStorage {\n    using LibRawResult for bytes;\n\n    struct SharedProposalStorage {\n        IProposalExecutionEngine engineImpl;\n        ProposalEngineOpts opts;\n        GovernanceValues governanceValues;\n    }\n\n    /// @notice Governance values stored for a party\n    struct GovernanceValues {\n        uint40 voteDuration;\n        uint40 executionDelay;\n        uint16 passThresholdBps;\n        uint96 totalVotingPower;\n    }\n\n    enum DistributionsConfig {\n        AllowedWithoutVote,\n        AllowedWithVote,\n        NotAllowed\n    }\n\n    struct ProposalEngineOpts {\n        // Whether the party can add new authorities with the add authority proposal.\n        bool enableAddAuthorityProposal;\n        // Whether the party can spend ETH from the party's balance with\n        // arbitrary call proposals.\n        bool allowArbCallsToSpendPartyEth;\n        // Whether operators can be used.\n        bool allowOperators;\n        // Distributions config for the party.\n        DistributionsConfig distributionsConfig;\n    }\n\n    uint256 internal constant PROPOSAL_FLAG_UNANIMOUS = 0x1;\n    uint256 private constant SHARED_STORAGE_SLOT =\n        uint256(keccak256(\"ProposalStorage.SharedProposalStorage\"));\n\n    function _initProposalImpl(IProposalExecutionEngine impl, bytes memory initData) internal {\n        SharedProposalStorage storage stor = _getSharedProposalStorage();\n        IProposalExecutionEngine oldImpl = stor.engineImpl;\n        stor.engineImpl = impl;\n        (bool s, bytes memory r) = address(impl).delegatecall(\n            abi.encodeCall(IProposalExecutionEngine.initialize, (address(oldImpl), initData))\n        );\n        if (!s) {\n            r.rawRevert();\n        }\n    }\n\n    function _getSharedProposalStorage()\n        internal\n        pure\n        returns (SharedProposalStorage storage stor)\n    {\n        uint256 s = SHARED_STORAGE_SLOT;\n        assembly {\n            stor.slot := s\n        }\n    }\n}\n"
      },
      "contracts/utils/Implementation.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\n// Base contract for all contracts intended to be delegatecalled into.\nabstract contract Implementation {\n    event Initialized();\n\n    error AlreadyInitialized();\n    error OnlyDelegateCallError();\n\n    /// @notice The address of the implementation contract.\n    address public immutable implementation;\n\n    /// @notice Whether or not the implementation has been initialized.\n    bool public initialized;\n\n    constructor() {\n        implementation = address(this);\n    }\n\n    // Reverts if the current function context is not inside of a delegatecall.\n    modifier onlyDelegateCall() virtual {\n        if (address(this) == implementation) {\n            revert OnlyDelegateCallError();\n        }\n        _;\n    }\n\n    modifier onlyInitialize() {\n        if (initialized) revert AlreadyInitialized();\n\n        initialized = true;\n        emit Initialized();\n\n        _;\n    }\n\n    /// @notice The address of the implementation contract.\n    /// @dev This is an alias for `implementation` for backwards compatibility.\n    function IMPL() external view returns (address) {\n        return implementation;\n    }\n}\n"
      },
      "contracts/utils/LibAddress.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nlibrary LibAddress {\n    error EthTransferFailed(address receiver, bytes errData);\n\n    // Transfer ETH with full gas stipend.\n    function transferEth(address payable receiver, uint256 amount) internal {\n        if (amount == 0) return;\n\n        (bool s, bytes memory r) = receiver.call{ value: amount }(\"\");\n        if (!s) {\n            revert EthTransferFailed(receiver, r);\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)\n\npragma solidity ^0.8.19;\n\nimport \"../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Interface for the NFT Royalty Standard.\n *\n * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal\n * support for royalty payments across all NFT marketplaces and ecosystem participants.\n *\n * _Available since v4.5._\n */\ninterface IERC2981 is IERC165 {\n    /**\n     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of\n     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.\n     */\n    function royaltyInfo(\n        uint256 tokenId,\n        uint256 salePrice\n    ) external view returns (address receiver, uint256 royaltyAmount);\n}\n"
      },
      "contracts/vendor/solmate/ERC721.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport \"../../tokens/IERC721.sol\";\nimport \"../../utils/EIP165.sol\";\n\n/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)\n/// @dev Modified from Solmate\nabstract contract ERC721 is IERC721, EIP165 {\n    error NotMinted();\n    error ZeroAddress();\n    error UnsafeRecipient();\n    error Unauthorized();\n\n    /*//////////////////////////////////////////////////////////////\n                         METADATA STORAGE/LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    string public name;\n\n    string public symbol;\n\n    function tokenURI(uint256 id /* view */) public virtual returns (string memory);\n\n    /*//////////////////////////////////////////////////////////////\n                      ERC721 BALANCE/OWNER STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    mapping(uint256 => address) internal _ownerOf;\n\n    mapping(address => uint256) internal _balanceOf;\n\n    function ownerOf(uint256 id) public view virtual returns (address owner) {\n        owner = _ownerOf[id];\n        if (owner == address(0)) {\n            revert NotMinted();\n        }\n    }\n\n    function balanceOf(address owner) public view virtual returns (uint256) {\n        if (owner == address(0)) {\n            revert ZeroAddress();\n        }\n\n        return _balanceOf[owner];\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                         ERC721 APPROVAL STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    mapping(uint256 => address) public getApproved;\n\n    mapping(address => mapping(address => bool)) public isApprovedForAll;\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(string memory _name, string memory _symbol) {\n        name = _name;\n        symbol = _symbol;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC721 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function approve(address spender, uint256 id) public virtual {\n        address owner = _ownerOf[id];\n\n        if (msg.sender != owner && !isApprovedForAll[owner][msg.sender]) {\n            revert Unauthorized();\n        }\n\n        getApproved[id] = spender;\n\n        emit Approval(owner, spender, id);\n    }\n\n    function setApprovalForAll(address operator, bool approved) public virtual {\n        isApprovedForAll[msg.sender][operator] = approved;\n\n        emit ApprovalForAll(msg.sender, operator, approved);\n    }\n\n    function transferFrom(address from, address to, uint256 id) public virtual {\n        if (from != _ownerOf[id]) {\n            revert Unauthorized();\n        }\n\n        if (to == address(0)) {\n            revert ZeroAddress();\n        }\n\n        if (\n            msg.sender != from &&\n            !isApprovedForAll[from][msg.sender] &&\n            msg.sender != getApproved[id]\n        ) {\n            revert Unauthorized();\n        }\n\n        // Underflow of the sender's balance is impossible because we check for\n        // ownership above and the recipient's balance can't realistically overflow.\n        unchecked {\n            _balanceOf[from]--;\n\n            _balanceOf[to]++;\n        }\n\n        _ownerOf[id] = to;\n\n        delete getApproved[id];\n\n        emit Transfer(from, to, id);\n    }\n\n    function safeTransferFrom(address from, address to, uint256 id) public virtual {\n        transferFrom(from, to, id);\n\n        if (\n            to.code.length != 0 &&\n            ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, \"\") !=\n            ERC721TokenReceiver.onERC721Received.selector\n        ) {\n            revert UnsafeRecipient();\n        }\n    }\n\n    function safeTransferFrom(\n        address from,\n        address to,\n        uint256 id,\n        bytes calldata data\n    ) public virtual {\n        transferFrom(from, to, id);\n\n        if (\n            to.code.length != 0 &&\n            ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) !=\n            ERC721TokenReceiver.onERC721Received.selector\n        ) {\n            revert UnsafeRecipient();\n        }\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC165 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {\n        // NOTE: modified from original to call super.\n        return\n            super.supportsInterface(interfaceId) ||\n            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721\n            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL MINT/BURN LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _mint(address to, uint256 id) internal virtual {\n        if (to == address(0)) {\n            revert ZeroAddress();\n        }\n\n        if (_ownerOf[id] != address(0)) {\n            revert Unauthorized();\n        }\n\n        // Counter overflow is incredibly unrealistic.\n        unchecked {\n            _balanceOf[to]++;\n        }\n\n        _ownerOf[id] = to;\n\n        emit Transfer(address(0), to, id);\n    }\n\n    function _burn(uint256 id) internal virtual {\n        address owner = _ownerOf[id];\n\n        if (owner == address(0)) {\n            revert Unauthorized();\n        }\n\n        // Ownership check above ensures no underflow.\n        unchecked {\n            _balanceOf[owner]--;\n        }\n\n        delete _ownerOf[id];\n\n        delete getApproved[id];\n\n        emit Transfer(owner, address(0), id);\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL SAFE MINT LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _safeMint(address to, uint256 id) internal virtual {\n        _mint(to, id);\n\n        if (\n            to.code.length != 0 &&\n            ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, \"\") !=\n            ERC721TokenReceiver.onERC721Received.selector\n        ) {\n            revert UnsafeRecipient();\n        }\n    }\n\n    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {\n        _mint(to, id);\n\n        if (\n            to.code.length != 0 &&\n            ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) !=\n            ERC721TokenReceiver.onERC721Received.selector\n        ) {\n            revert UnsafeRecipient();\n        }\n    }\n}\n\n/// @notice A generic interface for a contract which properly accepts ERC721 tokens.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)\nabstract contract ERC721TokenReceiver {\n    function onERC721Received(\n        address,\n        address,\n        uint256,\n        bytes calldata\n    ) external virtual returns (bytes4) {\n        return ERC721TokenReceiver.onERC721Received.selector;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.19;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    /**\n     * @dev Unauthorized reentrant call.\n     */\n    error ReentrancyGuardReentrantCall();\n\n    constructor() {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        // On the first call to nonReentrant, _status will be _NOT_ENTERED\n        if (_status == _ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        return _status == _ENTERED;\n    }\n}\n"
      },
      "contracts/tokens/IERC1155.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity ^0.8;\n\n// Minimal ERC1155 interface.\ninterface IERC1155 {\n    event TransferSingle(\n        address indexed operator,\n        address indexed from,\n        address indexed to,\n        uint256 id,\n        uint256 amount\n    );\n    event TransferBatch(\n        address indexed operator,\n        address indexed from,\n        address indexed to,\n        uint256[] ids,\n        uint256[] amounts\n    );\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    function setApprovalForAll(address operator, bool approved) external;\n\n    function safeTransferFrom(\n        address from,\n        address to,\n        uint256 id,\n        uint256 amount,\n        bytes calldata data\n    ) external;\n\n    function safeBatchTransferFrom(\n        address from,\n        address to,\n        uint256[] calldata ids,\n        uint256[] calldata amounts,\n        bytes calldata data\n    ) external;\n\n    function balanceOf(address owner, uint256 tokenId) external view returns (uint256);\n\n    function isApprovedForAll(address owner, address spender) external view returns (bool);\n\n    function balanceOfBatch(\n        address[] calldata owners,\n        uint256[] calldata ids\n    ) external view returns (uint256[] memory balances);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.19;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
      },
      "contracts/utils/EIP165.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.20;\n\nabstract contract EIP165 {\n    /// @notice Query if a contract implements an interface.\n    /// @param interfaceId The interface identifier, as specified in ERC-165\n    /// @return `true` if the contract implements `interfaceId` and\n    ///         `interfaceId` is not 0xffffffff, `false` otherwise\n    function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {\n        return interfaceId == this.supportsInterface.selector;\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "forge-std/=lib/forge-std/src/",
        "openzeppelin/=lib/openzeppelin-contracts/",
        "solmate/=lib/solmate/src/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "party-addresses/=lib/party-addresses/",
        "uniswap-v2-core/=lib/v2-core/contracts/",
        "uniswap-v2-periphery/=lib/v2-periphery/contracts/",
        "erc20-creator/=lib/erc20-creator/src/",
        "@openzeppelin/contracts/=lib/erc20-creator/lib/openzeppelin-contracts/contracts/",
        "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
        "old-oz/=lib/erc20-creator/lib/old-oz/contracts/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "party-protocol/=lib/erc20-creator/lib/party-protocol/",
        "v2-core/=lib/v2-core/contracts/",
        "v2-periphery/=lib/v2-periphery/contracts/",
        "v3-core/=lib/erc20-creator/lib/v3-core/",
        "v3-periphery/=lib/erc20-creator/lib/v3-periphery/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 50
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "shanghai",
      "viaIR": true,
      "libraries": {
        "contracts/utils/LibRenderer.sol": {
          "LibRenderer": "0x09DDb6cc7D2B3b71559888E1e3Ea74e7DbE0E9Cd"
        }
      }
    }
  }}