ETH Price: $3,322.66 (-1.97%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ProposalExecutionEngine

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 44 : ProposalExecutionEngine.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../utils/Implementation.sol";
import "../utils/LibRawResult.sol";
import "../globals/IGlobals.sol";

import "./IProposalExecutionEngine.sol";
import "./ListOnOpenseaProposal.sol";
import "./ListOnOpenseaAdvancedProposal.sol";
import "./ListOnZoraProposal.sol";
import "./FractionalizeProposal.sol";
import "./ArbitraryCallsProposal.sol";
import "./ProposalStorage.sol";

/// @notice Upgradable implementation of proposal execution logic for parties that use it.
/// @dev This contract will be delegatecall'ed into by `Party` proxy instances.
contract ProposalExecutionEngine is
    IProposalExecutionEngine,
    Implementation,
    ProposalStorage,
    ListOnOpenseaProposal,
    ListOnOpenseaAdvancedProposal,
    ListOnZoraProposal,
    FractionalizeProposal,
    ArbitraryCallsProposal
{
    using LibRawResult for bytes;

    error UnsupportedProposalTypeError(uint32 proposalType);

    // The types of proposals supported.
    // The first 4 bytes of a proposal's `proposalData` determine the proposal
    // type.
    // WARNING: This should be append-only.
    enum ProposalType {
        Invalid,
        ListOnOpensea,
        ListOnZora,
        Fractionalize,
        ArbitraryCalls,
        UpgradeProposalEngineImpl,
        ListOnOpenseaAdvanced
    }

    // Explicit storage bucket for "private" state owned by the `ProposalExecutionEngine`.
    // See `_getStorage()` for how this is addressed.
    //
    // Read this for more context on the pattern motivating this:
    // https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/explicit-storage-buckets
    struct Storage {
        // The hash of the next `progressData` for the current `InProgress`
        // proposal. This is updated to the hash of the next `progressData` every
        // time a proposal is executed. This enforces that the next call to
        // `executeProposal()` receives the correct `progressData`.
        // If there is no current `InProgress` proposal, this will be 0x0.
        bytes32 nextProgressDataHash;
        // The proposal ID of the current, in progress proposal being executed.
        // `InProgress` proposals need to have `executeProposal()` called on them
        // multiple times until they complete. Only one proposal may be
        // in progress at a time, meaning no other proposals can be executed
        // if this value is nonzero.
        uint256 currentInProgressProposalId;
    }

    event ProposalEngineImplementationUpgraded(address oldImpl, address newImpl);

    error ZeroProposalIdError();
    error MalformedProposalDataError();
    error ProposalExecutionBlockedError(uint256 proposalId, uint256 currentInProgressProposalId);
    error ProposalProgressDataInvalidError(
        bytes32 actualProgressDataHash,
        bytes32 expectedProgressDataHash
    );
    error ProposalNotInProgressError(uint256 proposalId);
    error UnexpectedProposalEngineImplementationError(
        IProposalExecutionEngine actualImpl,
        IProposalExecutionEngine expectedImpl
    );

    // The `Globals` contract storing global configuration values. This contract
    // is immutable and it’s address will never change.
    IGlobals private immutable _GLOBALS;
    // Storage slot for `Storage`.
    // Use a constant, non-overlapping slot offset for the storage bucket.
    uint256 private constant _STORAGE_SLOT = uint256(keccak256("ProposalExecutionEngine.Storage"));

    // Set immutables.
    constructor(
        IGlobals globals,
        IOpenseaExchange seaport,
        IOpenseaConduitController seaportConduitController,
        IZoraAuctionHouse zoraAuctionHouse,
        IFractionalV1VaultFactory fractionalVaultFactory
    )
        ListOnOpenseaAdvancedProposal(globals, seaport, seaportConduitController)
        ListOnZoraProposal(globals, zoraAuctionHouse)
        FractionalizeProposal(fractionalVaultFactory)
        ArbitraryCallsProposal(zoraAuctionHouse)
    {
        _GLOBALS = globals;
    }

    // Used by `Party` to setup the execution engine.
    // Currently does nothing, but may be changed in future versions.
    function initialize(
        address oldImpl,
        bytes calldata initializeData
    ) external override onlyDelegateCall {
        /* NOOP */
    }

    /// @notice Get the current `InProgress` proposal ID.
    /// @dev With this version, only one proposal may be in progress at a time.
    function getCurrentInProgressProposalId() external view returns (uint256 id) {
        return _getStorage().currentInProgressProposalId;
    }

    /// @inheritdoc IProposalExecutionEngine
    function executeProposal(
        ExecuteProposalParams memory params
    ) external onlyDelegateCall returns (bytes memory nextProgressData) {
        // Must have a valid proposal ID.
        if (params.proposalId == 0) {
            revert ZeroProposalIdError();
        }
        Storage storage stor = _getStorage();
        uint256 currentInProgressProposalId = stor.currentInProgressProposalId;
        if (currentInProgressProposalId == 0) {
            // No proposal is currently in progress.
            // Mark this proposal as the one in progress.
            stor.currentInProgressProposalId = params.proposalId;
        } else if (currentInProgressProposalId != params.proposalId) {
            // Only one proposal can be in progress at a time.
            revert ProposalExecutionBlockedError(params.proposalId, currentInProgressProposalId);
        }
        {
            bytes32 nextProgressDataHash = stor.nextProgressDataHash;
            if (nextProgressDataHash == 0) {
                // Expecting no progress data.
                // This is the state if there is no current `InProgress` proposal.
                assert(currentInProgressProposalId == 0);
                if (params.progressData.length != 0) {
                    revert ProposalProgressDataInvalidError(
                        keccak256(params.progressData),
                        nextProgressDataHash
                    );
                }
            } else {
                // Expecting progress data.
                bytes32 progressDataHash = keccak256(params.progressData);
                // Progress data must match the one stored.
                if (nextProgressDataHash != progressDataHash) {
                    revert ProposalProgressDataInvalidError(progressDataHash, nextProgressDataHash);
                }
            }
            // Temporarily set the expected next progress data hash to an
            // unachievable constant to act as a reentrancy guard.
            stor.nextProgressDataHash = bytes32(type(uint256).max);
        }

        // Note that we do not enforce that the proposal has not been executed
        // (and completed) before in this contract. That is enforced by PartyGovernance.

        // Execute the proposal.
        ProposalType pt;
        (pt, params.proposalData) = _extractProposalType(params.proposalData);
        nextProgressData = _execute(pt, params);

        // If progress data is empty, the proposal is complete.
        if (nextProgressData.length == 0) {
            stor.currentInProgressProposalId = 0;
            stor.nextProgressDataHash = 0;
        } else {
            // Remember the next progress data.
            stor.nextProgressDataHash = keccak256(nextProgressData);
        }
    }

    /// @inheritdoc IProposalExecutionEngine
    function cancelProposal(uint256 proposalId) external onlyDelegateCall {
        // Must be a valid proposal ID.
        if (proposalId == 0) {
            revert ZeroProposalIdError();
        }
        Storage storage stor = _getStorage();
        {
            // Must be the current InProgress proposal.
            uint256 currentInProgressProposalId = stor.currentInProgressProposalId;
            if (currentInProgressProposalId != proposalId) {
                revert ProposalNotInProgressError(proposalId);
            }
        }
        // Clear the current InProgress proposal ID and next progress data.
        stor.currentInProgressProposalId = 0;
        stor.nextProgressDataHash = 0;
    }

    // Switch statement used to execute the right proposal.
    function _execute(
        ProposalType pt,
        ExecuteProposalParams memory params
    ) internal virtual returns (bytes memory nextProgressData) {
        if (pt == ProposalType.ListOnOpensea) {
            nextProgressData = _executeListOnOpensea(params);
        } else if (pt == ProposalType.ListOnOpenseaAdvanced) {
            nextProgressData = _executeListOnOpenseaAdvanced(params);
        } else if (pt == ProposalType.ListOnZora) {
            nextProgressData = _executeListOnZora(params);
        } else if (pt == ProposalType.Fractionalize) {
            nextProgressData = _executeFractionalize(params);
        } else if (pt == ProposalType.ArbitraryCalls) {
            nextProgressData = _executeArbitraryCalls(params);
        } else if (pt == ProposalType.UpgradeProposalEngineImpl) {
            _executeUpgradeProposalsImplementation(params.proposalData);
        } else {
            revert UnsupportedProposalTypeError(uint32(pt));
        }
    }

    // Destructively pops off the first 4 bytes of `proposalData` to determine
    // the type. This modifies `proposalData` and returns the updated
    // pointer to it.
    function _extractProposalType(
        bytes memory proposalData
    ) private pure returns (ProposalType proposalType, bytes memory offsetProposalData) {
        // First 4 bytes is proposal type. While the proposal type could be
        // stored in just 1 byte, this makes it easier to encode with
        // `abi.encodeWithSelector`.
        if (proposalData.length < 4) {
            revert MalformedProposalDataError();
        }
        assembly {
            // By reading 4 bytes into the length prefix, the leading 4 bytes
            // of the data will be in the lower bits of the read word.
            proposalType := and(mload(add(proposalData, 4)), 0xffffffff)
            mstore(add(proposalData, 4), sub(mload(proposalData), 4))
            offsetProposalData := add(proposalData, 4)
        }
        require(proposalType != ProposalType.Invalid);
        require(uint8(proposalType) <= uint8(type(ProposalType).max));
    }

    // Upgrade implementation to the latest version.
    function _executeUpgradeProposalsImplementation(bytes memory proposalData) private {
        (address expectedImpl, bytes memory initData) = abi.decode(proposalData, (address, bytes));
        // Always upgrade to latest implementation stored in `_GLOBALS`.
        IProposalExecutionEngine newImpl = IProposalExecutionEngine(
            _GLOBALS.getAddress(LibGlobals.GLOBAL_PROPOSAL_ENGINE_IMPL)
        );
        if (expectedImpl != address(newImpl)) {
            revert UnexpectedProposalEngineImplementationError(
                newImpl,
                IProposalExecutionEngine(expectedImpl)
            );
        }
        _initProposalImpl(newImpl, initData);
        emit ProposalEngineImplementationUpgraded(address(IMPL), expectedImpl);
    }

    // Retrieve the explicit storage bucket for the ProposalExecutionEngine logic.
    function _getStorage() internal pure returns (Storage storage stor) {
        uint256 slot = _STORAGE_SLOT;
        assembly {
            stor.slot := slot
        }
    }
}

File 2 of 44 : ITokenDistributor.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC20.sol";

import "./ITokenDistributorParty.sol";

/// @notice Creates token distributions for parties.
interface ITokenDistributor {
    enum TokenType {
        Native,
        Erc20
    }

    // Info on a distribution, created by createDistribution().
    struct DistributionInfo {
        // Type of distribution/token.
        TokenType tokenType;
        // ID of the distribution. Assigned by createDistribution().
        uint256 distributionId;
        // The party whose members can claim the distribution.
        ITokenDistributorParty party;
        // Who can claim `fee`.
        address payable feeRecipient;
        // The token being distributed.
        address token;
        // Total amount of `token` that can be claimed by party members.
        uint128 memberSupply;
        // Amount of `token` to be redeemed by `feeRecipient`.
        uint128 fee;
    }

    event DistributionCreated(ITokenDistributorParty indexed party, DistributionInfo info);
    event DistributionFeeClaimed(
        ITokenDistributorParty indexed party,
        address indexed feeRecipient,
        TokenType tokenType,
        address token,
        uint256 amount
    );
    event DistributionClaimedByPartyToken(
        ITokenDistributorParty indexed party,
        uint256 indexed partyTokenId,
        address indexed owner,
        TokenType tokenType,
        address token,
        uint256 amountClaimed
    );

    /// @notice Create a new distribution for an outstanding native token balance
    ///         governed by a party.
    /// @dev Native tokens should be transferred directly into this contract
    ///      immediately prior (same tx) to calling `createDistribution()` or
    ///      attached to the call itself.
    /// @param party The party whose members can claim the distribution.
    /// @param feeRecipient Who can claim `fee`.
    /// @param feeBps Percentage (in bps) of the distribution `feeRecipient` receives.
    /// @return info Information on the created distribution.
    function createNativeDistribution(
        ITokenDistributorParty party,
        address payable feeRecipient,
        uint16 feeBps
    ) external payable returns (DistributionInfo memory info);

    /// @notice Create a new distribution for an outstanding ERC20 token balance
    ///         governed by a party.
    /// @dev ERC20 tokens should be transferred directly into this contract
    ///      immediately prior (same tx) to calling `createDistribution()` or
    ///      attached to the call itself.
    /// @param token The ERC20 token to distribute.
    /// @param party The party whose members can claim the distribution.
    /// @param feeRecipient Who can claim `fee`.
    /// @param feeBps Percentage (in bps) of the distribution `feeRecipient` receives.
    /// @return info Information on the created distribution.
    function createErc20Distribution(
        IERC20 token,
        ITokenDistributorParty party,
        address payable feeRecipient,
        uint16 feeBps
    ) external returns (DistributionInfo memory info);

    /// @notice Claim a portion of a distribution owed to a `partyTokenId` belonging
    ///         to the party that created the distribution. The caller
    ///         must own this token.
    /// @param info Information on the distribution being claimed.
    /// @param partyTokenId The ID of the party token to claim for.
    /// @return amountClaimed The amount of the distribution claimed.
    function claim(
        DistributionInfo calldata info,
        uint256 partyTokenId
    ) external returns (uint128 amountClaimed);

    /// @notice Claim the fee for a distribution. Only a distribution's `feeRecipient`
    ///         can call this.
    /// @param info Information on the distribution being claimed.
    /// @param recipient The address to send the fee to.
    function claimFee(DistributionInfo calldata info, address payable recipient) external;

    /// @notice Batch version of `claim()`.
    /// @param infos Information on the distributions being claimed.
    /// @param partyTokenIds The ID of the party tokens to claim for.
    /// @return amountsClaimed The amount of the distributions claimed.
    function batchClaim(
        DistributionInfo[] calldata infos,
        uint256[] calldata partyTokenIds
    ) external returns (uint128[] memory amountsClaimed);

    /// @notice Batch version of `claimFee()`.
    /// @param infos Information on the distributions to claim fees for.
    /// @param recipients The addresses to send the fees to.
    function batchClaimFee(
        DistributionInfo[] calldata infos,
        address payable[] calldata recipients
    ) external;

    /// @notice Compute the amount of a distribution's token are owed to a party
    ///         member, identified by the `partyTokenId`.
    /// @param party The party to use for computing the claim amount.
    /// @param memberSupply Total amount of tokens that can be claimed in the distribution.
    /// @param partyTokenId The ID of the party token to claim for.
    /// @return claimAmount The amount of the distribution owed to the party member.
    function getClaimAmount(
        ITokenDistributorParty party,
        uint256 memberSupply,
        uint256 partyTokenId
    ) external view returns (uint128);

    /// @notice Check whether the fee has been claimed for a distribution.
    /// @param party The party to use for checking whether the fee has been claimed.
    /// @param distributionId The ID of the distribution to check.
    /// @return feeClaimed Whether the fee has been claimed.
    function wasFeeClaimed(
        ITokenDistributorParty party,
        uint256 distributionId
    ) external view returns (bool);

    /// @notice Check whether a `partyTokenId` has claimed their share of a distribution.
    /// @param party The party to use for checking whether the `partyTokenId` has claimed.
    /// @param partyTokenId The ID of the party token to check.
    /// @param distributionId The ID of the distribution to check.
    /// @return hasClaimed Whether the `partyTokenId` has claimed.
    function hasPartyTokenIdClaimed(
        ITokenDistributorParty party,
        uint256 partyTokenId,
        uint256 distributionId
    ) external view returns (bool);

    /// @notice Get how much unclaimed member tokens are left in a distribution.
    /// @param party The party to use for checking the unclaimed member tokens.
    /// @param distributionId The ID of the distribution to check.
    /// @return remainingMemberSupply The amount of distribution supply remaining.
    function getRemainingMemberSupply(
        ITokenDistributorParty party,
        uint256 distributionId
    ) external view returns (uint128);
}

File 3 of 44 : ITokenDistributorParty.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

// Interface the caller of `ITokenDistributor.createDistribution()` must implement.
interface ITokenDistributorParty {
    /// @notice Return the owner of a token.
    /// @param tokenId The token ID to query.
    /// @return owner The owner of `tokenId`.
    function ownerOf(uint256 tokenId) external view returns (address);

    /// @notice Return the distribution share of a token. Denominated fractions
    ///         of 1e18. I.e., 1e18 = 100%.
    /// @param tokenId The token ID to query.
    /// @return share The distribution percentage of `tokenId`.
    function getDistributionShareOf(uint256 tokenId) external view returns (uint256);
}

File 4 of 44 : IGlobals.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../utils/Implementation.sol";

// Single registry of global values controlled by multisig.
// See `LibGlobals` for all valid keys.
interface IGlobals {
    function multiSig() external view returns (address);

    function getBytes32(uint256 key) external view returns (bytes32);

    function getUint256(uint256 key) external view returns (uint256);

    function getBool(uint256 key) external view returns (bool);

    function getAddress(uint256 key) external view returns (address);

    function getImplementation(uint256 key) external view returns (Implementation);

    function getIncludesBytes32(uint256 key, bytes32 value) external view returns (bool);

    function getIncludesUint256(uint256 key, uint256 value) external view returns (bool);

    function getIncludesAddress(uint256 key, address value) external view returns (bool);

    function setBytes32(uint256 key, bytes32 value) external;

    function setUint256(uint256 key, uint256 value) external;

    function setBool(uint256 key, bool value) external;

    function setAddress(uint256 key, address value) external;

    function setIncludesBytes32(uint256 key, bytes32 value, bool isIncluded) external;

    function setIncludesUint256(uint256 key, uint256 value, bool isIncluded) external;

    function setIncludesAddress(uint256 key, address value, bool isIncluded) external;
}

File 5 of 44 : LibGlobals.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

// Valid keys in `IGlobals`. Append-only.
library LibGlobals {
    uint256 internal constant GLOBAL_PARTY_IMPL = 1;
    uint256 internal constant GLOBAL_PROPOSAL_ENGINE_IMPL = 2;
    uint256 internal constant GLOBAL_PARTY_FACTORY = 3;
    uint256 internal constant GLOBAL_GOVERNANCE_NFT_RENDER_IMPL = 4;
    uint256 internal constant GLOBAL_CF_NFT_RENDER_IMPL = 5;
    uint256 internal constant GLOBAL_OS_ZORA_AUCTION_TIMEOUT = 6;
    uint256 internal constant GLOBAL_OS_ZORA_AUCTION_DURATION = 7;
    uint256 internal constant GLOBAL_AUCTION_CF_IMPL = 8;
    uint256 internal constant GLOBAL_BUY_CF_IMPL = 9;
    uint256 internal constant GLOBAL_COLLECTION_BUY_CF_IMPL = 10;
    uint256 internal constant GLOBAL_DAO_WALLET = 11;
    uint256 internal constant GLOBAL_TOKEN_DISTRIBUTOR = 12;
    uint256 internal constant GLOBAL_OPENSEA_CONDUIT_KEY = 13;
    uint256 internal constant GLOBAL_OPENSEA_ZONE = 14;
    uint256 internal constant GLOBAL_PROPOSAL_MAX_CANCEL_DURATION = 15;
    uint256 internal constant GLOBAL_ZORA_MIN_AUCTION_DURATION = 16;
    uint256 internal constant GLOBAL_ZORA_MAX_AUCTION_DURATION = 17;
    uint256 internal constant GLOBAL_ZORA_MAX_AUCTION_TIMEOUT = 18;
    uint256 internal constant GLOBAL_OS_MIN_ORDER_DURATION = 19;
    uint256 internal constant GLOBAL_OS_MAX_ORDER_DURATION = 20;
    uint256 internal constant GLOBAL_DISABLE_PARTY_ACTIONS = 21;
    uint256 internal constant GLOBAL_RENDERER_STORAGE = 22;
    uint256 internal constant GLOBAL_PROPOSAL_MIN_CANCEL_DURATION = 23;
    uint256 internal constant GLOBAL_ROLLING_AUCTION_CF_IMPL = 24;
    uint256 internal constant GLOBAL_COLLECTION_BATCH_BUY_CF_IMPL = 25;
    uint256 internal constant GLOBAL_METADATA_REGISTRY = 26;
}

File 6 of 44 : IPartyFactory.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../globals/IGlobals.sol";
import "../tokens/IERC721.sol";

import "./Party.sol";

// Creates generic Party instances.
interface IPartyFactory {
    event PartyCreated(
        Party indexed party,
        Party.PartyOptions opts,
        IERC721[] preciousTokens,
        uint256[] preciousTokenIds,
        address creator
    );

    /// @notice Deploy a new party instance. Afterwards, governance NFTs can be minted
    ///         for party members using the `mint()` function from the newly
    ///         created party.
    /// @param authority The address that can call `mint()`.
    /// @param opts Options used to initialize the party. These are fixed
    ///             and cannot be changed later.
    /// @param preciousTokens The tokens that are considered precious by the
    ///                       party.These are protected assets and are subject
    ///                       to extra restrictions in proposals vs other
    ///                       assets.
    /// @param preciousTokenIds The IDs associated with each token in `preciousTokens`.
    /// @return party The newly created `Party` instance.
    function createParty(
        address authority,
        Party.PartyOptions calldata opts,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds
    ) external returns (Party party);

    /// @notice The `Globals` contract storing global configuration values. This contract
    ///         is immutable and it’s address will never change.
    function GLOBALS() external view returns (IGlobals);
}

File 7 of 44 : Party.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC721.sol";

import "./PartyGovernanceNFT.sol";
import "./PartyGovernance.sol";

/// @notice The governance contract that also custodies the precious NFTs. This
///         is also the Governance NFT 721 contract.
contract Party is PartyGovernanceNFT {
    // Arguments used to initialize the party.
    struct PartyOptions {
        PartyGovernance.GovernanceOpts governance;
        string name;
        string symbol;
        uint256 customizationPresetId;
    }

    // Arguments used to initialize the `PartyGovernanceNFT`.
    struct PartyInitData {
        PartyOptions options;
        IERC721[] preciousTokens;
        uint256[] preciousTokenIds;
        address mintAuthority;
    }

    // Set the `Globals` contract.
    constructor(IGlobals globals) PartyGovernanceNFT(globals) {}

    /// @notice Initializer to be delegatecalled by `Proxy` constructor. Will
    ///         revert if called outside the constructor.
    /// @param initData Options used to initialize the party governance.
    function initialize(PartyInitData memory initData) external onlyConstructor {
        PartyGovernanceNFT._initialize(
            initData.options.name,
            initData.options.symbol,
            initData.options.customizationPresetId,
            initData.options.governance,
            initData.preciousTokens,
            initData.preciousTokenIds,
            initData.mintAuthority
        );
    }

    receive() external payable {}
}

File 8 of 44 : PartyGovernance.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../distribution/ITokenDistributorParty.sol";
import "../distribution/ITokenDistributor.sol";
import "../utils/ReadOnlyDelegateCall.sol";
import "../tokens/IERC721.sol";
import "../tokens/IERC20.sol";
import "../tokens/IERC1155.sol";
import "../tokens/ERC721Receiver.sol";
import "../tokens/ERC1155Receiver.sol";
import "../utils/LibERC20Compat.sol";
import "../utils/LibRawResult.sol";
import "../utils/LibSafeCast.sol";
import "../globals/IGlobals.sol";
import "../globals/LibGlobals.sol";
import "../proposals/IProposalExecutionEngine.sol";
import "../proposals/LibProposal.sol";
import "../proposals/ProposalStorage.sol";

import "./IPartyFactory.sol";

/// @notice Base contract for a Party encapsulating all governance functionality.
abstract contract PartyGovernance is
    ITokenDistributorParty,
    ERC721Receiver,
    ERC1155Receiver,
    ProposalStorage,
    Implementation,
    ReadOnlyDelegateCall
{
    using LibERC20Compat for IERC20;
    using LibRawResult for bytes;
    using LibSafeCast for uint256;
    using LibSafeCast for int192;
    using LibSafeCast for uint96;

    // States a proposal can be in.
    enum ProposalStatus {
        // The proposal does not exist.
        Invalid,
        // The proposal has been proposed (via `propose()`), has not been vetoed
        // by a party host, and is within the voting window. Members can vote on
        // the proposal and party hosts can veto the proposal.
        Voting,
        // The proposal has either exceeded its voting window without reaching
        // `passThresholdBps` of votes or was vetoed by a party host.
        Defeated,
        // The proposal reached at least `passThresholdBps` of votes but is still
        // waiting for `executionDelay` to pass before it can be executed. Members
        // can continue to vote on the proposal and party hosts can veto at this time.
        Passed,
        // Same as `Passed` but now `executionDelay` has been satisfied. Any member
        // may execute the proposal via `execute()`, unless `maxExecutableTime`
        // has arrived.
        Ready,
        // The proposal has been executed at least once but has further steps to
        // complete so it needs to be executed again. No other proposals may be
        // executed while a proposal is in the `InProgress` state. No voting or
        // vetoing of the proposal is allowed, however it may be forcibly cancelled
        // via `cancel()` if the `cancelDelay` has passed since being first executed.
        InProgress,
        // The proposal was executed and completed all its steps. No voting or
        // vetoing can occur and it cannot be cancelled nor executed again.
        Complete,
        // The proposal was executed at least once but did not complete before
        // `cancelDelay` seconds passed since the first execute and was forcibly cancelled.
        Cancelled
    }

    struct GovernanceOpts {
        // Address of initial party hosts.
        address[] hosts;
        // How long people can vote on a proposal.
        uint40 voteDuration;
        // How long to wait after a proposal passes before it can be
        // executed.
        uint40 executionDelay;
        // Minimum ratio of accept votes to consider a proposal passed,
        // in bps, where 10,000 == 100%.
        uint16 passThresholdBps;
        // Total voting power of governance NFTs.
        uint96 totalVotingPower;
        // Fee bps for distributions.
        uint16 feeBps;
        // Fee recipeint for distributions.
        address payable feeRecipient;
    }

    // Subset of `GovernanceOpts` that are commonly read together for
    // efficiency.
    struct GovernanceValues {
        uint40 voteDuration;
        uint40 executionDelay;
        uint16 passThresholdBps;
        uint96 totalVotingPower;
    }

    // A snapshot of voting power for a member.
    struct VotingPowerSnapshot {
        // The timestamp when the snapshot was taken.
        uint40 timestamp;
        // Voting power that was delegated to this user by others.
        uint96 delegatedVotingPower;
        // The intrinsic (not delegated from someone else) voting power of this user.
        uint96 intrinsicVotingPower;
        // Whether the user was delegated to another at this snapshot.
        bool isDelegated;
    }

    // Proposal details chosen by proposer.
    struct Proposal {
        // Time beyond which the proposal can no longer be executed.
        // If the proposal has already been executed, and is still InProgress,
        // this value is ignored.
        uint40 maxExecutableTime;
        // The minimum seconds this proposal can remain in the InProgress status
        // before it can be cancelled.
        uint40 cancelDelay;
        // Encoded proposal data. The first 4 bytes are the proposal type, followed
        // by encoded proposal args specific to the proposal type. See
        // ProposalExecutionEngine for details.
        bytes proposalData;
    }

    // Accounting and state tracking values for a proposal.
    // Fits in a word.
    struct ProposalStateValues {
        // When the proposal was proposed.
        uint40 proposedTime;
        // When the proposal passed the vote.
        uint40 passedTime;
        // When the proposal was first executed.
        uint40 executedTime;
        // When the proposal completed.
        uint40 completedTime;
        // Number of accept votes.
        uint96 votes; // -1 == vetoed
    }

    // Storage states for a proposal.
    struct ProposalState {
        // Accounting and state tracking values.
        ProposalStateValues values;
        // Hash of the proposal.
        bytes32 hash;
        // Whether a member has voted for (accepted) this proposal already.
        mapping(address => bool) hasVoted;
    }

    event Proposed(uint256 proposalId, address proposer, Proposal proposal);
    event ProposalAccepted(uint256 proposalId, address voter, uint256 weight);
    event EmergencyExecute(address target, bytes data, uint256 amountEth);

    event ProposalPassed(uint256 indexed proposalId);
    event ProposalVetoed(uint256 indexed proposalId, address host);
    event ProposalExecuted(uint256 indexed proposalId, address executor, bytes nextProgressData);
    event ProposalCancelled(uint256 indexed proposalId);
    event DistributionCreated(
        ITokenDistributor.TokenType tokenType,
        address token,
        uint256 tokenId
    );
    event VotingPowerDelegated(address indexed owner, address indexed delegate);
    event HostStatusTransferred(address oldHost, address newHost);
    event EmergencyExecuteDisabled();

    error MismatchedPreciousListLengths();
    error BadProposalStatusError(ProposalStatus status);
    error BadProposalHashError(bytes32 proposalHash, bytes32 actualHash);
    error ExecutionTimeExceededError(uint40 maxExecutableTime, uint40 timestamp);
    error OnlyPartyHostError();
    error OnlyActiveMemberError();
    error InvalidDelegateError();
    error BadPreciousListError();
    error OnlyPartyDaoError(address notDao, address partyDao);
    error OnlyPartyDaoOrHostError(address notDao, address partyDao);
    error OnlyWhenEmergencyActionsAllowedError();
    error OnlyWhenEnabledError();
    error AlreadyVotedError(address voter);
    error InvalidNewHostError();
    error ProposalCannotBeCancelledYetError(uint40 currentTime, uint40 cancelTime);
    error InvalidBpsError(uint16 bps);

    uint256 private constant UINT40_HIGH_BIT = 1 << 39;
    uint96 private constant VETO_VALUE = type(uint96).max;

    // The `Globals` contract storing global configuration values. This contract
    // is immutable and it’s address will never change.
    IGlobals private immutable _GLOBALS;

    /// @notice Whether the DAO has emergency powers for this party.
    bool public emergencyExecuteDisabled;
    /// @notice Distribution fee bps.
    uint16 public feeBps;
    /// @notice Distribution fee recipient.
    address payable public feeRecipient;
    /// @notice The hash of the list of precious NFTs guarded by the party.
    bytes32 public preciousListHash;
    /// @notice The last proposal ID that was used. 0 means no proposals have been made.
    uint256 public lastProposalId;
    /// @notice Whether an address is a party host.
    mapping(address => bool) public isHost;
    /// @notice The last person a voter delegated its voting power to.
    mapping(address => address) public delegationsByVoter;
    // Constant governance parameters, fixed from the inception of this party.
    GovernanceValues internal _governanceValues;
    // ProposalState by proposal ID.
    mapping(uint256 => ProposalState) private _proposalStateByProposalId;
    // Snapshots of voting power per user, each sorted by increasing time.
    mapping(address => VotingPowerSnapshot[]) private _votingPowerSnapshotsByVoter;

    modifier onlyHost() {
        if (!isHost[msg.sender]) {
            revert OnlyPartyHostError();
        }
        _;
    }

    // Caller must have voting power at the current time.
    modifier onlyActiveMember() {
        {
            VotingPowerSnapshot memory snap = _getLastVotingPowerSnapshotForVoter(msg.sender);
            // Must have either delegated voting power or intrinsic voting power.
            if (snap.intrinsicVotingPower == 0 && snap.delegatedVotingPower == 0) {
                revert OnlyActiveMemberError();
            }
        }
        _;
    }

    // Caller must have voting power at the current time or be the `Party` instance.
    modifier onlyActiveMemberOrSelf() {
        // Ignore if the party is calling functions on itself, like with
        // `FractionalizeProposal` calling `distribute()`.
        if (msg.sender != address(this)) {
            VotingPowerSnapshot memory snap = _getLastVotingPowerSnapshotForVoter(msg.sender);
            // Must have either delegated voting power or intrinsic voting power.
            if (snap.intrinsicVotingPower == 0 && snap.delegatedVotingPower == 0) {
                revert OnlyActiveMemberError();
            }
        }
        _;
    }

    // Only the party DAO multisig can call.
    modifier onlyPartyDao() {
        {
            address partyDao = _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);
            if (msg.sender != partyDao) {
                revert OnlyPartyDaoError(msg.sender, partyDao);
            }
        }
        _;
    }

    // Only the party DAO multisig or a party host can call.
    modifier onlyPartyDaoOrHost() {
        address partyDao = _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);
        if (msg.sender != partyDao && !isHost[msg.sender]) {
            revert OnlyPartyDaoOrHostError(msg.sender, partyDao);
        }
        _;
    }

    // Only if `emergencyExecuteDisabled` is not true.
    modifier onlyWhenEmergencyExecuteAllowed() {
        if (emergencyExecuteDisabled) {
            revert OnlyWhenEmergencyActionsAllowedError();
        }
        _;
    }

    modifier onlyWhenNotGloballyDisabled() {
        if (_GLOBALS.getBool(LibGlobals.GLOBAL_DISABLE_PARTY_ACTIONS)) {
            revert OnlyWhenEnabledError();
        }
        _;
    }

    // Set the `Globals` contract.
    constructor(IGlobals globals) {
        _GLOBALS = globals;
    }

    // Initialize storage for proxy contracts and initialize the proposal execution engine.
    function _initialize(
        GovernanceOpts memory opts,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds
    ) internal virtual {
        // Check BPS are valid.
        if (opts.feeBps > 1e4) {
            revert InvalidBpsError(opts.feeBps);
        }
        if (opts.passThresholdBps > 1e4) {
            revert InvalidBpsError(opts.passThresholdBps);
        }
        // Initialize the proposal execution engine.
        _initProposalImpl(
            IProposalExecutionEngine(_GLOBALS.getAddress(LibGlobals.GLOBAL_PROPOSAL_ENGINE_IMPL)),
            ""
        );
        // Set the governance parameters.
        _governanceValues = GovernanceValues({
            voteDuration: opts.voteDuration,
            executionDelay: opts.executionDelay,
            passThresholdBps: opts.passThresholdBps,
            totalVotingPower: opts.totalVotingPower
        });
        // Set fees.
        feeBps = opts.feeBps;
        feeRecipient = opts.feeRecipient;
        // Set the precious list.
        _setPreciousList(preciousTokens, preciousTokenIds);
        // Set the party hosts.
        for (uint256 i = 0; i < opts.hosts.length; ++i) {
            isHost[opts.hosts[i]] = true;
        }
    }

    /// @dev Forward all unknown read-only calls to the proposal execution engine.
    ///      Initial use case is to facilitate eip-1271 signatures.
    fallback() external {
        _readOnlyDelegateCall(address(_getProposalExecutionEngine()), msg.data);
    }

    /// @inheritdoc EIP165
    /// @dev Combined logic for `ERC721Receiver` and `ERC1155Receiver`.
    function supportsInterface(
        bytes4 interfaceId
    ) public pure virtual override(ERC721Receiver, ERC1155Receiver) returns (bool) {
        return
            ERC721Receiver.supportsInterface(interfaceId) ||
            ERC1155Receiver.supportsInterface(interfaceId);
    }

    /// @notice Get the current `ProposalExecutionEngine` instance.
    function getProposalExecutionEngine() external view returns (IProposalExecutionEngine) {
        return _getProposalExecutionEngine();
    }

    /// @notice Get the total voting power of `voter` at a `timestamp`.
    /// @param voter The address of the voter.
    /// @param timestamp The timestamp to get the voting power at.
    /// @return votingPower The total voting power of `voter` at `timestamp`.
    function getVotingPowerAt(
        address voter,
        uint40 timestamp
    ) external view returns (uint96 votingPower) {
        return getVotingPowerAt(voter, timestamp, type(uint256).max);
    }

    /// @notice Get the total voting power of `voter` at a snapshot `snapIndex`, with checks to
    ///         make sure it is the latest voting snapshot =< `timestamp`.
    /// @param voter The address of the voter.
    /// @param timestamp The timestamp to get the voting power at.
    /// @param snapIndex The index of the snapshot to get the voting power at.
    /// @return votingPower The total voting power of `voter` at `timestamp`.
    function getVotingPowerAt(
        address voter,
        uint40 timestamp,
        uint256 snapIndex
    ) public view returns (uint96 votingPower) {
        VotingPowerSnapshot memory snap = _getVotingPowerSnapshotAt(voter, timestamp, snapIndex);
        return (snap.isDelegated ? 0 : snap.intrinsicVotingPower) + snap.delegatedVotingPower;
    }

    /// @notice Get the state of a proposal.
    /// @param proposalId The ID of the proposal.
    /// @return status The status of the proposal.
    /// @return values The state of the proposal.
    function getProposalStateInfo(
        uint256 proposalId
    ) external view returns (ProposalStatus status, ProposalStateValues memory values) {
        values = _proposalStateByProposalId[proposalId].values;
        status = _getProposalStatus(values);
    }

    /// @notice Retrieve fixed governance parameters.
    /// @return gv The governance parameters of this party.
    function getGovernanceValues() external view returns (GovernanceValues memory gv) {
        return _governanceValues;
    }

    /// @notice Get the hash of a proposal.
    /// @dev Proposal details are not stored on-chain so the hash is used to enforce
    ///      consistency between calls.
    /// @param proposal The proposal to hash.
    /// @return proposalHash The hash of the proposal.
    function getProposalHash(Proposal memory proposal) public pure returns (bytes32 proposalHash) {
        // Hash the proposal in-place. Equivalent to:
        // keccak256(abi.encode(
        //   proposal.maxExecutableTime,
        //   proposal.cancelDelay,
        //   keccak256(proposal.proposalData)
        // ))
        bytes32 dataHash = keccak256(proposal.proposalData);
        assembly {
            // Overwrite the data field with the hash of its contents and then
            // hash the struct.
            let dataPos := add(proposal, 0x40)
            let t := mload(dataPos)
            mstore(dataPos, dataHash)
            proposalHash := keccak256(proposal, 0x60)
            // Restore the data field.
            mstore(dataPos, t)
        }
    }

    /// @notice Get the index of the most recent voting power snapshot <= `timestamp`.
    /// @param voter The address of the voter.
    /// @param timestamp The timestamp to get the snapshot index at.
    /// @return index The index of the snapshot.
    function findVotingPowerSnapshotIndex(
        address voter,
        uint40 timestamp
    ) public view returns (uint256 index) {
        VotingPowerSnapshot[] storage snaps = _votingPowerSnapshotsByVoter[voter];

        // Derived from Open Zeppelin binary search
        // ref: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Checkpoints.sol#L39
        uint256 high = snaps.length;
        uint256 low = 0;
        while (low < high) {
            uint256 mid = (low + high) / 2;
            if (snaps[mid].timestamp > timestamp) {
                // Entry is too recent.
                high = mid;
            } else {
                // Entry is older. This is our best guess for now.
                low = mid + 1;
            }
        }

        // Return `type(uint256).max` if no valid voting snapshots found.
        return high == 0 ? type(uint256).max : high - 1;
    }

    /// @notice Pledge your intrinsic voting power to a new delegate, removing it from
    ///         the old one (if any).
    /// @param delegate The address to delegating voting power to.
    function delegateVotingPower(address delegate) external onlyDelegateCall {
        _adjustVotingPower(msg.sender, 0, delegate);
        emit VotingPowerDelegated(msg.sender, delegate);
    }

    /// @notice Transfer party host status to another.
    /// @param newPartyHost The address of the new host.
    function abdicate(address newPartyHost) external onlyHost onlyDelegateCall {
        // 0 is a special case burn address.
        if (newPartyHost != address(0)) {
            // Cannot transfer host status to an existing host.
            if (isHost[newPartyHost]) {
                revert InvalidNewHostError();
            }
            isHost[newPartyHost] = true;
        }
        isHost[msg.sender] = false;
        emit HostStatusTransferred(msg.sender, newPartyHost);
    }

    /// @notice Create a token distribution by moving the party's entire balance
    ///         to the `TokenDistributor` contract and immediately creating a
    ///         distribution governed by this party.
    /// @dev The `feeBps` and `feeRecipient` this party was created with will be
    ///      propagated to the distribution. Party members are entitled to a
    ///      share of the distribution's tokens proportionate to their relative
    ///      voting power in this party (less the fee).
    /// @dev Allow this to be called by the party itself for `FractionalizeProposal`.
    /// @param tokenType The type of token to distribute.
    /// @param token The address of the token to distribute.
    /// @param tokenId The ID of the token to distribute. Currently unused but
    ///                may be used in the future to support other distribution types.
    /// @return distInfo The information about the created distribution.
    function distribute(
        ITokenDistributor.TokenType tokenType,
        address token,
        uint256 tokenId
    )
        external
        onlyActiveMemberOrSelf
        onlyWhenNotGloballyDisabled
        onlyDelegateCall
        returns (ITokenDistributor.DistributionInfo memory distInfo)
    {
        // Get the address of the token distributor.
        ITokenDistributor distributor = ITokenDistributor(
            _GLOBALS.getAddress(LibGlobals.GLOBAL_TOKEN_DISTRIBUTOR)
        );
        emit DistributionCreated(tokenType, token, tokenId);
        // Create a native token distribution.
        address payable feeRecipient_ = feeRecipient;
        uint16 feeBps_ = feeBps;
        if (tokenType == ITokenDistributor.TokenType.Native) {
            return
                distributor.createNativeDistribution{ value: address(this).balance }(
                    this,
                    feeRecipient_,
                    feeBps_
                );
        }
        // Otherwise must be an ERC20 token distribution.
        assert(tokenType == ITokenDistributor.TokenType.Erc20);
        IERC20(token).compatTransfer(address(distributor), IERC20(token).balanceOf(address(this)));
        return distributor.createErc20Distribution(IERC20(token), this, feeRecipient_, feeBps_);
    }

    /// @notice Make a proposal for members to vote on and cast a vote to accept it
    ///         as well.
    /// @dev Only an active member (has voting power) can call this.
    ///      Afterwards, members can vote to support it with `accept()` or a party
    ///      host can unilaterally reject the proposal with `veto()`.
    /// @param proposal The details of the proposal.
    /// @param latestSnapIndex The index of the caller's most recent voting power
    ///                        snapshot before the proposal was created. Should
    ///                        be retrieved off-chain and passed in.
    function propose(
        Proposal memory proposal,
        uint256 latestSnapIndex
    ) external onlyActiveMember onlyDelegateCall returns (uint256 proposalId) {
        proposalId = ++lastProposalId;
        // Store the time the proposal was created and the proposal hash.
        (
            _proposalStateByProposalId[proposalId].values,
            _proposalStateByProposalId[proposalId].hash
        ) = (
            ProposalStateValues({
                proposedTime: uint40(block.timestamp),
                passedTime: 0,
                executedTime: 0,
                completedTime: 0,
                votes: 0
            }),
            getProposalHash(proposal)
        );
        emit Proposed(proposalId, msg.sender, proposal);
        accept(proposalId, latestSnapIndex);
    }

    /// @notice Vote to support a proposed proposal.
    /// @dev The voting power cast will be the effective voting power of the caller
    ///      just before `propose()` was called (see `getVotingPowerAt()`).
    ///      If the proposal reaches `passThresholdBps` acceptance ratio then the
    ///      proposal will be in the `Passed` state and will be executable after
    ///      the `executionDelay` has passed, putting it in the `Ready` state.
    /// @param proposalId The ID of the proposal to accept.
    /// @param snapIndex The index of the caller's last voting power snapshot
    ///                  before the proposal was created. Should be retrieved
    ///                  off-chain and passed in.
    /// @return totalVotes The total votes cast on the proposal.
    function accept(
        uint256 proposalId,
        uint256 snapIndex
    ) public onlyDelegateCall returns (uint256 totalVotes) {
        // Get the information about the proposal.
        ProposalState storage info = _proposalStateByProposalId[proposalId];
        ProposalStateValues memory values = info.values;

        // Can only vote in certain proposal statuses.
        {
            ProposalStatus status = _getProposalStatus(values);
            // Allow voting even if the proposal is passed/ready so it can
            // potentially reach 100% consensus, which unlocks special
            // behaviors for certain proposal types.
            if (
                status != ProposalStatus.Voting &&
                status != ProposalStatus.Passed &&
                status != ProposalStatus.Ready
            ) {
                revert BadProposalStatusError(status);
            }
        }

        // Cannot vote twice.
        if (info.hasVoted[msg.sender]) {
            revert AlreadyVotedError(msg.sender);
        }
        // Mark the caller as having voted.
        info.hasVoted[msg.sender] = true;

        // Increase the total votes that have been cast on this proposal.
        uint96 votingPower = getVotingPowerAt(msg.sender, values.proposedTime - 1, snapIndex);
        values.votes += votingPower;
        info.values = values;
        emit ProposalAccepted(proposalId, msg.sender, votingPower);

        // Update the proposal status if it has reached the pass threshold.
        if (
            values.passedTime == 0 &&
            _areVotesPassing(
                values.votes,
                _governanceValues.totalVotingPower,
                _governanceValues.passThresholdBps
            )
        ) {
            info.values.passedTime = uint40(block.timestamp);
            emit ProposalPassed(proposalId);
        }
        return values.votes;
    }

    /// @notice As a party host, veto a proposal, unilaterally rejecting it.
    /// @dev The proposal will never be executable and cannot be voted on anymore.
    ///      A proposal that has been already executed at least once (in the `InProgress` status)
    ///      cannot be vetoed.
    /// @param proposalId The ID of the proposal to veto.
    function veto(uint256 proposalId) external onlyHost onlyDelegateCall {
        // Setting `votes` to -1 indicates a veto.
        ProposalState storage info = _proposalStateByProposalId[proposalId];
        ProposalStateValues memory values = info.values;

        {
            ProposalStatus status = _getProposalStatus(values);
            // Proposal must be in one of the following states.
            if (
                status != ProposalStatus.Voting &&
                status != ProposalStatus.Passed &&
                status != ProposalStatus.Ready
            ) {
                revert BadProposalStatusError(status);
            }
        }

        // -1 indicates veto.
        info.values.votes = VETO_VALUE;
        emit ProposalVetoed(proposalId, msg.sender);
    }

    /// @notice Executes a proposal that has passed governance.
    /// @dev The proposal must be in the `Ready` or `InProgress` status.
    ///      A `ProposalExecuted` event will be emitted with a non-empty `nextProgressData`
    ///      if the proposal has extra steps (must be executed again) to carry out,
    ///      in which case `nextProgressData` should be passed into the next `execute()` call.
    ///      The `ProposalExecutionEngine` enforces that only one `InProgress` proposal
    ///      is active at a time, so that proposal must be completed or cancelled via `cancel()`
    ///      in order to execute a different proposal.
    ///      `extraData` is optional, off-chain data a proposal might need to execute a step.
    /// @param proposalId The ID of the proposal to execute.
    /// @param proposal The details of the proposal.
    /// @param preciousTokens The tokens that the party considers precious.
    /// @param preciousTokenIds The token IDs associated with each precious token.
    /// @param progressData The data returned from the last `execute()` call, if any.
    /// @param extraData Off-chain data a proposal might need to execute a step.
    function execute(
        uint256 proposalId,
        Proposal memory proposal,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds,
        bytes calldata progressData,
        bytes calldata extraData
    ) external payable onlyActiveMember onlyWhenNotGloballyDisabled onlyDelegateCall {
        // Get information about the proposal.
        ProposalState storage proposalState = _proposalStateByProposalId[proposalId];
        // Proposal details must remain the same from `propose()`.
        _validateProposalHash(proposal, proposalState.hash);
        ProposalStateValues memory values = proposalState.values;
        ProposalStatus status = _getProposalStatus(values);
        // The proposal must be executable or have already been executed but still
        // has more steps to go.
        if (status != ProposalStatus.Ready && status != ProposalStatus.InProgress) {
            revert BadProposalStatusError(status);
        }
        if (status == ProposalStatus.Ready) {
            // If the proposal has not been executed yet, make sure it hasn't
            // expired. Note that proposals that have been executed
            // (but still have more steps) ignore `maxExecutableTime`.
            if (proposal.maxExecutableTime < block.timestamp) {
                revert ExecutionTimeExceededError(
                    proposal.maxExecutableTime,
                    uint40(block.timestamp)
                );
            }
            proposalState.values.executedTime = uint40(block.timestamp);
        }
        // Check that the precious list is valid.
        if (!_isPreciousListCorrect(preciousTokens, preciousTokenIds)) {
            revert BadPreciousListError();
        }
        // Preemptively set the proposal to completed to avoid it being executed
        // again in a deeper call.
        proposalState.values.completedTime = uint40(block.timestamp);
        // Execute the proposal.
        bool completed = _executeProposal(
            proposalId,
            proposal,
            preciousTokens,
            preciousTokenIds,
            _getProposalFlags(values),
            progressData,
            extraData
        );
        if (!completed) {
            // Proposal did not complete.
            proposalState.values.completedTime = 0;
        }
    }

    /// @notice Cancel a (probably stuck) InProgress proposal.
    /// @dev `proposal.cancelDelay` seconds must have passed since it was first
    ///      executed for this to be valid. The currently active proposal will
    ///      simply be yeeted out of existence so another proposal can execute.
    ///      This is intended to be a last resort and can leave the party in a
    ///      broken state. Whenever possible, active proposals should be
    ///      allowed to complete their lifecycle.
    /// @param proposalId The ID of the proposal to cancel.
    /// @param proposal The details of the proposal to cancel.
    function cancel(
        uint256 proposalId,
        Proposal calldata proposal
    ) external onlyActiveMember onlyDelegateCall {
        // Get information about the proposal.
        ProposalState storage proposalState = _proposalStateByProposalId[proposalId];
        // Proposal details must remain the same from `propose()`.
        _validateProposalHash(proposal, proposalState.hash);
        ProposalStateValues memory values = proposalState.values;
        {
            // Must be `InProgress`.
            ProposalStatus status = _getProposalStatus(values);
            if (status != ProposalStatus.InProgress) {
                revert BadProposalStatusError(status);
            }
        }
        {
            // Limit the `cancelDelay` to the global max and min cancel delay
            // to mitigate parties accidentally getting stuck forever by setting an
            // unrealistic `cancelDelay` or being reckless with too low a
            // cancel delay.
            uint256 cancelDelay = proposal.cancelDelay;
            uint256 globalMaxCancelDelay = _GLOBALS.getUint256(
                LibGlobals.GLOBAL_PROPOSAL_MAX_CANCEL_DURATION
            );
            uint256 globalMinCancelDelay = _GLOBALS.getUint256(
                LibGlobals.GLOBAL_PROPOSAL_MIN_CANCEL_DURATION
            );
            if (globalMaxCancelDelay != 0) {
                // Only if we have one set.
                if (cancelDelay > globalMaxCancelDelay) {
                    cancelDelay = globalMaxCancelDelay;
                }
            }
            if (globalMinCancelDelay != 0) {
                // Only if we have one set.
                if (cancelDelay < globalMinCancelDelay) {
                    cancelDelay = globalMinCancelDelay;
                }
            }
            uint256 cancelTime = values.executedTime + cancelDelay;
            // Must not be too early.
            if (block.timestamp < cancelTime) {
                revert ProposalCannotBeCancelledYetError(
                    uint40(block.timestamp),
                    uint40(cancelTime)
                );
            }
        }
        // Mark the proposal as cancelled by setting the completed time to the current
        // time with the high bit set.
        proposalState.values.completedTime = uint40(block.timestamp | UINT40_HIGH_BIT);
        {
            // Delegatecall into the proposal engine impl to perform the cancel.
            (bool success, bytes memory resultData) = (address(_getProposalExecutionEngine()))
                .delegatecall(
                    abi.encodeCall(IProposalExecutionEngine.cancelProposal, (proposalId))
                );
            if (!success) {
                resultData.rawRevert();
            }
        }
        emit ProposalCancelled(proposalId);
    }

    /// @notice As the DAO, execute an arbitrary function call from this contract.
    /// @dev Emergency actions must not be revoked for this to work.
    /// @param targetAddress The contract to call.
    /// @param targetCallData The data to pass to the contract.
    /// @param amountEth The amount of ETH to send to the contract.
    function emergencyExecute(
        address targetAddress,
        bytes calldata targetCallData,
        uint256 amountEth
    ) external payable onlyPartyDao onlyWhenEmergencyExecuteAllowed onlyDelegateCall {
        (bool success, bytes memory res) = targetAddress.call{ value: amountEth }(targetCallData);
        if (!success) {
            res.rawRevert();
        }
        emit EmergencyExecute(targetAddress, targetCallData, amountEth);
    }

    /// @notice Revoke the DAO's ability to call emergencyExecute().
    /// @dev Either the DAO or the party host can call this.
    function disableEmergencyExecute() external onlyPartyDaoOrHost onlyDelegateCall {
        emergencyExecuteDisabled = true;
        emit EmergencyExecuteDisabled();
    }

    function _executeProposal(
        uint256 proposalId,
        Proposal memory proposal,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds,
        uint256 flags,
        bytes memory progressData,
        bytes memory extraData
    ) private returns (bool completed) {
        // Setup the arguments for the proposal execution engine.
        IProposalExecutionEngine.ExecuteProposalParams
            memory executeParams = IProposalExecutionEngine.ExecuteProposalParams({
                proposalId: proposalId,
                proposalData: proposal.proposalData,
                progressData: progressData,
                extraData: extraData,
                preciousTokens: preciousTokens,
                preciousTokenIds: preciousTokenIds,
                flags: flags
            });
        // Get the progress data returned after the proposal is executed.
        bytes memory nextProgressData;
        {
            // Execute the proposal.
            (bool success, bytes memory resultData) = address(_getProposalExecutionEngine())
                .delegatecall(
                    abi.encodeCall(IProposalExecutionEngine.executeProposal, (executeParams))
                );
            if (!success) {
                resultData.rawRevert();
            }
            nextProgressData = abi.decode(resultData, (bytes));
        }
        emit ProposalExecuted(proposalId, msg.sender, nextProgressData);
        // If the returned progress data is empty, then the proposal completed
        // and it should not be executed again.
        return nextProgressData.length == 0;
    }

    // Get the most recent voting power snapshot <= timestamp using `hintindex` as a "hint".
    function _getVotingPowerSnapshotAt(
        address voter,
        uint40 timestamp,
        uint256 hintIndex
    ) internal view returns (VotingPowerSnapshot memory snap) {
        VotingPowerSnapshot[] storage snaps = _votingPowerSnapshotsByVoter[voter];
        uint256 snapsLength = snaps.length;
        if (snapsLength != 0) {
            if (
                // Hint is within bounds.
                hintIndex < snapsLength &&
                // Snapshot is not too recent.
                snaps[hintIndex].timestamp <= timestamp &&
                // Snapshot is not too old.
                (hintIndex == snapsLength - 1 || snaps[hintIndex + 1].timestamp > timestamp)
            ) {
                return snaps[hintIndex];
            }

            // Hint was wrong, fallback to binary search to find snapshot.
            hintIndex = findVotingPowerSnapshotIndex(voter, timestamp);
            // Check that snapshot was found.
            if (hintIndex != type(uint256).max) {
                return snaps[hintIndex];
            }
        }

        // No snapshot found.
        return snap;
    }

    // Transfers some voting power of `from` to `to`. The total voting power of
    // their respective delegates will be updated as well.
    function _transferVotingPower(address from, address to, uint256 power) internal {
        int192 powerI192 = power.safeCastUint256ToInt192();
        _adjustVotingPower(from, -powerI192, address(0));
        _adjustVotingPower(to, powerI192, address(0));
    }

    // Increase `voter`'s intrinsic voting power and update their delegate if delegate is nonzero.
    function _adjustVotingPower(address voter, int192 votingPower, address delegate) internal {
        VotingPowerSnapshot memory oldSnap = _getLastVotingPowerSnapshotForVoter(voter);
        address oldDelegate = delegationsByVoter[voter];
        // If `oldDelegate` is zero and `voter` never delegated, then have
        // `voter` delegate to themself.
        oldDelegate = oldDelegate == address(0) ? voter : oldDelegate;
        // If the new `delegate` is zero, use the current (old) delegate.
        delegate = delegate == address(0) ? oldDelegate : delegate;

        VotingPowerSnapshot memory newSnap = VotingPowerSnapshot({
            timestamp: uint40(block.timestamp),
            delegatedVotingPower: oldSnap.delegatedVotingPower,
            intrinsicVotingPower: (oldSnap.intrinsicVotingPower.safeCastUint96ToInt192() +
                votingPower).safeCastInt192ToUint96(),
            isDelegated: delegate != voter
        });
        _insertVotingPowerSnapshot(voter, newSnap);
        delegationsByVoter[voter] = delegate;
        // Handle rebalancing delegates.
        _rebalanceDelegates(voter, oldDelegate, delegate, oldSnap, newSnap);
    }

    function _getTotalVotingPower() internal view returns (uint256) {
        return _governanceValues.totalVotingPower;
    }

    // Update the delegated voting power of the old and new delegates delegated to
    // by `voter` based on the snapshot change.
    function _rebalanceDelegates(
        address voter,
        address oldDelegate,
        address newDelegate,
        VotingPowerSnapshot memory oldSnap,
        VotingPowerSnapshot memory newSnap
    ) private {
        if (newDelegate == address(0) || oldDelegate == address(0)) {
            revert InvalidDelegateError();
        }
        if (oldDelegate != voter && oldDelegate != newDelegate) {
            // Remove past voting power from old delegate.
            VotingPowerSnapshot memory oldDelegateSnap = _getLastVotingPowerSnapshotForVoter(
                oldDelegate
            );
            VotingPowerSnapshot memory updatedOldDelegateSnap = VotingPowerSnapshot({
                timestamp: uint40(block.timestamp),
                delegatedVotingPower: oldDelegateSnap.delegatedVotingPower -
                    oldSnap.intrinsicVotingPower,
                intrinsicVotingPower: oldDelegateSnap.intrinsicVotingPower,
                isDelegated: oldDelegateSnap.isDelegated
            });
            _insertVotingPowerSnapshot(oldDelegate, updatedOldDelegateSnap);
        }
        if (newDelegate != voter) {
            // Not delegating to self.
            // Add new voting power to new delegate.
            VotingPowerSnapshot memory newDelegateSnap = _getLastVotingPowerSnapshotForVoter(
                newDelegate
            );
            uint96 newDelegateDelegatedVotingPower = newDelegateSnap.delegatedVotingPower +
                newSnap.intrinsicVotingPower;
            if (newDelegate == oldDelegate) {
                // If the old and new delegate are the same, subtract the old
                // intrinsic voting power of the voter, or else we will double
                // count a portion of it.
                newDelegateDelegatedVotingPower -= oldSnap.intrinsicVotingPower;
            }
            VotingPowerSnapshot memory updatedNewDelegateSnap = VotingPowerSnapshot({
                timestamp: uint40(block.timestamp),
                delegatedVotingPower: newDelegateDelegatedVotingPower,
                intrinsicVotingPower: newDelegateSnap.intrinsicVotingPower,
                isDelegated: newDelegateSnap.isDelegated
            });
            _insertVotingPowerSnapshot(newDelegate, updatedNewDelegateSnap);
        }
    }

    // Append a new voting power snapshot, overwriting the last one if possible.
    function _insertVotingPowerSnapshot(address voter, VotingPowerSnapshot memory snap) private {
        VotingPowerSnapshot[] storage voterSnaps = _votingPowerSnapshotsByVoter[voter];
        uint256 n = voterSnaps.length;
        // If same timestamp as last entry, overwrite the last snapshot, otherwise append.
        if (n != 0) {
            VotingPowerSnapshot memory lastSnap = voterSnaps[n - 1];
            if (lastSnap.timestamp == snap.timestamp) {
                voterSnaps[n - 1] = snap;
                return;
            }
        }
        voterSnaps.push(snap);
    }

    function _getLastVotingPowerSnapshotForVoter(
        address voter
    ) private view returns (VotingPowerSnapshot memory snap) {
        VotingPowerSnapshot[] storage voterSnaps = _votingPowerSnapshotsByVoter[voter];
        uint256 n = voterSnaps.length;
        if (n != 0) {
            snap = voterSnaps[n - 1];
        }
    }

    function _getProposalFlags(ProposalStateValues memory pv) private view returns (uint256) {
        if (_isUnanimousVotes(pv.votes, _governanceValues.totalVotingPower)) {
            return LibProposal.PROPOSAL_FLAG_UNANIMOUS;
        }
        return 0;
    }

    function _getProposalStatus(
        ProposalStateValues memory pv
    ) private view returns (ProposalStatus status) {
        // Never proposed.
        if (pv.proposedTime == 0) {
            return ProposalStatus.Invalid;
        }
        // Executed at least once.
        if (pv.executedTime != 0) {
            if (pv.completedTime == 0) {
                return ProposalStatus.InProgress;
            }
            // completedTime high bit will be set if cancelled.
            if (pv.completedTime & UINT40_HIGH_BIT == UINT40_HIGH_BIT) {
                return ProposalStatus.Cancelled;
            }
            return ProposalStatus.Complete;
        }
        // Vetoed.
        if (pv.votes == type(uint96).max) {
            return ProposalStatus.Defeated;
        }
        uint40 t = uint40(block.timestamp);
        GovernanceValues memory gv = _governanceValues;
        if (pv.passedTime != 0) {
            // Ready.
            if (pv.passedTime + gv.executionDelay <= t) {
                return ProposalStatus.Ready;
            }
            // If unanimous, we skip the execution delay.
            if (_isUnanimousVotes(pv.votes, gv.totalVotingPower)) {
                return ProposalStatus.Ready;
            }
            // Passed.
            return ProposalStatus.Passed;
        }
        // Voting window expired.
        if (pv.proposedTime + gv.voteDuration <= t) {
            return ProposalStatus.Defeated;
        }
        return ProposalStatus.Voting;
    }

    function _isUnanimousVotes(
        uint96 totalVotes,
        uint96 totalVotingPower
    ) private pure returns (bool) {
        uint256 acceptanceRatio = (totalVotes * 1e4) / totalVotingPower;
        // If >= 99.99% acceptance, consider it unanimous.
        // The minting formula for voting power is a bit lossy, so we check
        // for slightly less than 100%.
        return acceptanceRatio >= 0.9999e4;
    }

    function _areVotesPassing(
        uint96 voteCount,
        uint96 totalVotingPower,
        uint16 passThresholdBps
    ) private pure returns (bool) {
        return (uint256(voteCount) * 1e4) / uint256(totalVotingPower) >= uint256(passThresholdBps);
    }

    function _setPreciousList(
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds
    ) private {
        if (preciousTokens.length != preciousTokenIds.length) {
            revert MismatchedPreciousListLengths();
        }
        preciousListHash = _hashPreciousList(preciousTokens, preciousTokenIds);
    }

    function _isPreciousListCorrect(
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds
    ) private view returns (bool) {
        return preciousListHash == _hashPreciousList(preciousTokens, preciousTokenIds);
    }

    function _hashPreciousList(
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds
    ) internal pure returns (bytes32 h) {
        assembly {
            mstore(0x00, keccak256(add(preciousTokens, 0x20), mul(mload(preciousTokens), 0x20)))
            mstore(0x20, keccak256(add(preciousTokenIds, 0x20), mul(mload(preciousTokenIds), 0x20)))
            h := keccak256(0x00, 0x40)
        }
    }

    // Assert that the hash of a proposal matches expectedHash.
    function _validateProposalHash(Proposal memory proposal, bytes32 expectedHash) private pure {
        bytes32 actualHash = getProposalHash(proposal);
        if (expectedHash != actualHash) {
            revert BadProposalHashError(actualHash, expectedHash);
        }
    }
}

File 9 of 44 : PartyGovernanceNFT.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../utils/ReadOnlyDelegateCall.sol";
import "../utils/LibSafeCast.sol";
import "openzeppelin/contracts/interfaces/IERC2981.sol";
import "../globals/IGlobals.sol";
import "../tokens/IERC721.sol";
import "../vendor/solmate/ERC721.sol";
import "./PartyGovernance.sol";
import "../renderers/RendererStorage.sol";

/// @notice ERC721 functionality built on top of `PartyGovernance`.
contract PartyGovernanceNFT is PartyGovernance, ERC721, IERC2981 {
    using LibSafeCast for uint256;
    using LibSafeCast for uint96;

    error OnlyMintAuthorityError(address actual, address expected);

    // The `Globals` contract storing global configuration values. This contract
    // is immutable and it’s address will never change.
    IGlobals private immutable _GLOBALS;

    /// @notice Who can call `mint()`. Usually this will be the crowdfund contract that
    /// created the party.
    address public mintAuthority;
    /// @notice The number of tokens that have been minted.
    uint96 public tokenCount;
    /// @notice The total minted voting power.
    ///         Capped to `_governanceValues.totalVotingPower`
    uint96 public mintedVotingPower;
    /// @notice The voting power of `tokenId`.
    mapping(uint256 => uint256) public votingPowerByTokenId;

    modifier onlyMinter() {
        address minter = mintAuthority;
        if (msg.sender != minter) {
            revert OnlyMintAuthorityError(msg.sender, minter);
        }
        _;
    }

    // Set the `Globals` contract. The name of symbol of ERC721 does not matter;
    // it will be set in `_initialize()`.
    constructor(IGlobals globals) PartyGovernance(globals) ERC721("", "") {
        _GLOBALS = globals;
    }

    // Initialize storage for proxy contracts.
    function _initialize(
        string memory name_,
        string memory symbol_,
        uint256 customizationPresetId,
        PartyGovernance.GovernanceOpts memory governanceOpts,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds,
        address mintAuthority_
    ) internal {
        PartyGovernance._initialize(governanceOpts, preciousTokens, preciousTokenIds);
        name = name_;
        symbol = symbol_;
        mintAuthority = mintAuthority_;
        if (customizationPresetId != 0) {
            RendererStorage(_GLOBALS.getAddress(LibGlobals.GLOBAL_RENDERER_STORAGE))
                .useCustomizationPreset(customizationPresetId);
        }
    }

    /// @inheritdoc ERC721
    function ownerOf(
        uint256 tokenId
    ) public view override(ERC721, ITokenDistributorParty) returns (address owner) {
        return ERC721.ownerOf(tokenId);
    }

    /// @inheritdoc EIP165
    function supportsInterface(
        bytes4 interfaceId
    ) public pure override(PartyGovernance, ERC721, IERC165) returns (bool) {
        return
            PartyGovernance.supportsInterface(interfaceId) ||
            ERC721.supportsInterface(interfaceId) ||
            interfaceId == type(IERC2981).interfaceId;
    }

    /// @inheritdoc ERC721
    function tokenURI(uint256) public view override returns (string memory) {
        _delegateToRenderer();
        return ""; // Just to make the compiler happy.
    }

    /// @notice Returns a URI for the storefront-level metadata for your contract.
    function contractURI() external view returns (string memory) {
        _delegateToRenderer();
        return ""; // Just to make the compiler happy.
    }

    /// @notice Called with the sale price to determine how much royalty
    //          is owed and to whom.
    function royaltyInfo(uint256, uint256) external view returns (address, uint256) {
        _delegateToRenderer();
        return (address(0), 0); // Just to make the compiler happy.
    }

    /// @inheritdoc ITokenDistributorParty
    function getDistributionShareOf(uint256 tokenId) external view returns (uint256) {
        return (votingPowerByTokenId[tokenId] * 1e18) / _getTotalVotingPower();
    }

    /// @notice Mint a governance NFT for `owner` with `votingPower` and
    /// immediately delegate voting power to `delegate.`
    /// @param owner The owner of the NFT.
    /// @param votingPower The voting power of the NFT.
    /// @param delegate The address to delegate voting power to.
    function mint(
        address owner,
        uint256 votingPower,
        address delegate
    ) external onlyMinter onlyDelegateCall returns (uint256 tokenId) {
        (uint96 tokenCount_, uint96 mintedVotingPower_) = (tokenCount, mintedVotingPower);
        uint96 totalVotingPower = _governanceValues.totalVotingPower;
        // Cap voting power to remaining unminted voting power supply.
        uint96 votingPower_ = votingPower.safeCastUint256ToUint96();
        if (totalVotingPower - mintedVotingPower_ < votingPower_) {
            votingPower_ = totalVotingPower - mintedVotingPower_;
        }
        mintedVotingPower_ += votingPower_;
        // Update state.
        tokenId = tokenCount = tokenCount_ + 1;
        mintedVotingPower = mintedVotingPower_;
        votingPowerByTokenId[tokenId] = votingPower_;

        // Use delegate from party over the one set during crowdfund.
        address delegate_ = delegationsByVoter[owner];
        if (delegate_ != address(0)) {
            delegate = delegate_;
        }

        _adjustVotingPower(owner, votingPower_.safeCastUint96ToInt192(), delegate);
        _safeMint(owner, tokenId);
    }

    /// @inheritdoc ERC721
    function transferFrom(
        address owner,
        address to,
        uint256 tokenId
    ) public override onlyDelegateCall {
        // Transfer voting along with token.
        _transferVotingPower(owner, to, votingPowerByTokenId[tokenId]);
        super.transferFrom(owner, to, tokenId);
    }

    /// @inheritdoc ERC721
    function safeTransferFrom(
        address owner,
        address to,
        uint256 tokenId
    ) public override onlyDelegateCall {
        // super.safeTransferFrom() will call transferFrom() first which will
        // transfer voting power.
        super.safeTransferFrom(owner, to, tokenId);
    }

    /// @inheritdoc ERC721
    function safeTransferFrom(
        address owner,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) public override onlyDelegateCall {
        // super.safeTransferFrom() will call transferFrom() first which will
        // transfer voting power.
        super.safeTransferFrom(owner, to, tokenId, data);
    }

    /// @notice Relinquish the ability to call `mint()` by an authority.
    function abdicate() external onlyMinter onlyDelegateCall {
        delete mintAuthority;
    }

    function _delegateToRenderer() private view {
        _readOnlyDelegateCall(
            // Instance of IERC721Renderer.
            _GLOBALS.getAddress(LibGlobals.GLOBAL_GOVERNANCE_NFT_RENDER_IMPL),
            msg.data
        );
        assert(false); // Will not be reached.
    }
}

File 10 of 44 : ArbitraryCallsProposal.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC721.sol";
import "../tokens/IERC721Receiver.sol";
import "../tokens/ERC1155Receiver.sol";
import "../utils/LibSafeERC721.sol";
import "../utils/LibAddress.sol";
import "../vendor/markets/IZoraAuctionHouse.sol";
import "./vendor/IOpenseaExchange.sol";

import "./LibProposal.sol";
import "./IProposalExecutionEngine.sol";

// Implements arbitrary call proposals. Inherited by the `ProposalExecutionEngine`.
// This contract will be delegatecall'ed into by `Party` proxy instances.
contract ArbitraryCallsProposal {
    using LibSafeERC721 for IERC721;
    using LibAddress for address payable;

    struct ArbitraryCall {
        // The call target.
        address payable target;
        // Amount of ETH to attach to the call.
        uint256 value;
        // Calldata.
        bytes data;
        // Hash of the successful return data of the call.
        // If 0x0, no return data checking will occur for this call.
        bytes32 expectedResultHash;
    }

    error PreciousLostError(IERC721 token, uint256 tokenId);
    error CallProhibitedError(address target, bytes data);
    error ArbitraryCallFailedError(bytes revertData);
    error UnexpectedCallResultHashError(
        uint256 idx,
        bytes32 resultHash,
        bytes32 expectedResultHash
    );
    error NotEnoughEthAttachedError(uint256 callValue, uint256 ethAvailable);
    error InvalidApprovalCallLength(uint256 callDataLength);

    event ArbitraryCallExecuted(uint256 proposalId, uint256 idx, uint256 count);

    IZoraAuctionHouse private immutable _ZORA;

    constructor(IZoraAuctionHouse zora) {
        _ZORA = zora;
    }

    function _executeArbitraryCalls(
        IProposalExecutionEngine.ExecuteProposalParams memory params
    ) internal returns (bytes memory nextProgressData) {
        // Get the calls to execute.
        ArbitraryCall[] memory calls = abi.decode(params.proposalData, (ArbitraryCall[]));
        // Check whether the proposal was unanimously passed.
        bool isUnanimous = params.flags & LibProposal.PROPOSAL_FLAG_UNANIMOUS ==
            LibProposal.PROPOSAL_FLAG_UNANIMOUS;
        // If not unanimous, keep track of which preciouses we had before the calls
        // so we can check that we still have them later.
        bool[] memory hadPreciouses = new bool[](params.preciousTokenIds.length);
        if (!isUnanimous) {
            for (uint256 i; i < hadPreciouses.length; ++i) {
                hadPreciouses[i] = _getHasPrecious(
                    params.preciousTokens[i],
                    params.preciousTokenIds[i]
                );
            }
        }
        // Can only forward ETH attached to the call.
        uint256 ethAvailable = msg.value;
        for (uint256 i; i < calls.length; ++i) {
            // Execute an arbitrary call.
            _executeSingleArbitraryCall(
                i,
                calls,
                params.preciousTokens,
                params.preciousTokenIds,
                isUnanimous,
                ethAvailable
            );
            // Update the amount of ETH available for the subsequent calls.
            ethAvailable -= calls[i].value;
            emit ArbitraryCallExecuted(params.proposalId, i, calls.length);
        }
        // If not a unanimous vote and we had a precious beforehand,
        // ensure that we still have it now.
        if (!isUnanimous) {
            for (uint256 i; i < hadPreciouses.length; ++i) {
                if (hadPreciouses[i]) {
                    if (!_getHasPrecious(params.preciousTokens[i], params.preciousTokenIds[i])) {
                        revert PreciousLostError(
                            params.preciousTokens[i],
                            params.preciousTokenIds[i]
                        );
                    }
                }
            }
        }
        // Refund leftover ETH.
        if (ethAvailable > 0) {
            payable(msg.sender).transferEth(ethAvailable);
        }
        // No next step, so no progressData.
        return "";
    }

    function _executeSingleArbitraryCall(
        uint256 idx,
        ArbitraryCall[] memory calls,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds,
        bool isUnanimous,
        uint256 ethAvailable
    ) private {
        ArbitraryCall memory call = calls[idx];
        // Check that the call is not prohibited.
        if (
            !_isCallAllowed(call, isUnanimous, idx, calls.length, preciousTokens, preciousTokenIds)
        ) {
            revert CallProhibitedError(call.target, call.data);
        }
        // Check that we have enough ETH to execute the call.
        if (ethAvailable < call.value) {
            revert NotEnoughEthAttachedError(call.value, ethAvailable);
        }
        // Execute the call.
        (bool s, bytes memory r) = call.target.call{ value: call.value }(call.data);
        if (!s) {
            // Call failed. If not optional, revert.
            revert ArbitraryCallFailedError(r);
        } else {
            // Call succeeded.
            // If we have a nonzero expectedResultHash, check that the result data
            // from the call has a matching hash.
            if (call.expectedResultHash != bytes32(0)) {
                bytes32 resultHash = keccak256(r);
                if (resultHash != call.expectedResultHash) {
                    revert UnexpectedCallResultHashError(idx, resultHash, call.expectedResultHash);
                }
            }
        }
    }

    // Do we possess the precious?
    function _getHasPrecious(
        IERC721 preciousToken,
        uint256 preciousTokenId
    ) private view returns (bool hasPrecious) {
        hasPrecious = preciousToken.safeOwnerOf(preciousTokenId) == address(this);
    }

    function _isCallAllowed(
        ArbitraryCall memory call,
        bool isUnanimous,
        uint256 callIndex,
        uint256 callsCount,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds
    ) private view returns (bool isAllowed) {
        // Cannot call ourselves.
        if (call.target == address(this)) {
            return false;
        }
        if (call.data.length >= 4) {
            // Get the function selector of the call (first 4 bytes of calldata).
            bytes4 selector;
            {
                bytes memory callData = call.data;
                assembly {
                    selector := and(
                        mload(add(callData, 32)),
                        0xffffffff00000000000000000000000000000000000000000000000000000000
                    )
                }
            }
            // Non-unanimous proposals restrict what ways some functions can be
            // called on a precious token.
            if (!isUnanimous) {
                // Cannot call `approve()` or `setApprovalForAll()` on the precious
                // unless it's to revoke approvals.
                if (selector == IERC721.approve.selector) {
                    // Can only call `approve()` on the precious if the operator is null.
                    (address op, uint256 tokenId) = _decodeApproveCallDataArgs(call.data);
                    if (op != address(0)) {
                        return
                            !LibProposal.isTokenIdPrecious(
                                IERC721(call.target),
                                tokenId,
                                preciousTokens,
                                preciousTokenIds
                            );
                    }
                    // Can only call `setApprovalForAll()` on the precious if
                    // toggling off.
                } else if (selector == IERC721.setApprovalForAll.selector) {
                    (, bool isApproved) = _decodeSetApprovalForAllCallDataArgs(call.data);
                    if (isApproved) {
                        return !LibProposal.isTokenPrecious(IERC721(call.target), preciousTokens);
                    }
                    // Can only call cancelAuction on the zora AH if it's the last call
                    // in the sequence.
                } else if (selector == IZoraAuctionHouse.cancelAuction.selector) {
                    if (call.target == address(_ZORA)) {
                        return callIndex + 1 == callsCount;
                    }
                }
            }
            // Can never call receive hooks on any target.
            if (
                selector == IERC721Receiver.onERC721Received.selector ||
                selector == ERC1155TokenReceiverBase.onERC1155Received.selector ||
                selector == ERC1155TokenReceiverBase.onERC1155BatchReceived.selector
            ) {
                return false;
            }
            // Disallow calling `validate()` on Seaport.
            if (selector == IOpenseaExchange.validate.selector) {
                return false;
            }
        }
        // All other calls are allowed.
        return true;
    }

    // Get the `operator` and `tokenId` from the `approve()` call data.
    function _decodeApproveCallDataArgs(
        bytes memory callData
    ) private pure returns (address operator, uint256 tokenId) {
        if (callData.length < 68) {
            revert InvalidApprovalCallLength(callData.length);
        }
        assembly {
            operator := and(mload(add(callData, 36)), 0xffffffffffffffffffffffffffffffffffffffff)
            tokenId := mload(add(callData, 68))
        }
    }

    // Get the `operator` and `tokenId` from the `setApprovalForAll()` call data.
    function _decodeSetApprovalForAllCallDataArgs(
        bytes memory callData
    ) private pure returns (address operator, bool isApproved) {
        if (callData.length < 68) {
            revert InvalidApprovalCallLength(callData.length);
        }
        assembly {
            operator := and(mload(add(callData, 36)), 0xffffffffffffffffffffffffffffffffffffffff)
            isApproved := xor(iszero(mload(add(callData, 68))), 1)
        }
    }
}

File 11 of 44 : FractionalizeProposal.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC721.sol";
import "../party/PartyGovernance.sol";

import "./IProposalExecutionEngine.sol";
import "./vendor/FractionalV1.sol";

// Implements fractionalizing an NFT to ERC20s on Fractional V1. Inherited by the `ProposalExecutionEngine`.
// This contract will be delegatecall'ed into by `Party` proxy instances.
contract FractionalizeProposal {
    struct FractionalizeProposalData {
        // The ERC721 token contract to fractionalize.
        IERC721 token;
        // The ERC721 token ID to fractionalize.
        uint256 tokenId;
    }

    event FractionalV1VaultCreated(
        IERC721 indexed token,
        uint256 indexed tokenId,
        uint256 vaultId,
        IERC20 vault
    );

    /// @notice Deployment of https://github.com/fractional-company/contracts/blob/master/src/ERC721TokenVault.sol.
    IFractionalV1VaultFactory public immutable VAULT_FACTORY;

    // Set the `VAULT_FACTORY`.
    constructor(IFractionalV1VaultFactory vaultFactory) {
        VAULT_FACTORY = vaultFactory;
    }

    // Fractionalize an NFT held by this party on Fractional V1.
    function _executeFractionalize(
        IProposalExecutionEngine.ExecuteProposalParams memory params
    ) internal returns (bytes memory nextProgressData) {
        // Decode the proposal data.
        FractionalizeProposalData memory data = abi.decode(
            params.proposalData,
            (FractionalizeProposalData)
        );
        // The supply of fractional vault ERC20 tokens will be equal to the total
        // voting power of the party.
        uint256 supply = PartyGovernance(address(this)).getGovernanceValues().totalVotingPower;
        // Create a vault around the NFT.
        data.token.approve(address(VAULT_FACTORY), data.tokenId);
        uint256 vaultId = VAULT_FACTORY.mint(
            IERC721(address(this)).name(),
            IERC721(address(this)).symbol(),
            data.token,
            data.tokenId,
            supply,
            // Since we are distributing the entire supply immediately after
            // fractionalizing, in practice setting an initial reserve price
            // does not do anything because it will get reset to 0 after the
            // distribution is created.
            0,
            0
        );
        // Get the vault we just created.
        IFractionalV1Vault vault = VAULT_FACTORY.vaults(vaultId);
        // Check that we now hold the correct amount of fractional tokens.
        // Should always succeed.
        assert(vault.balanceOf(address(this)) == supply);
        // Remove ourselves as curator. Set to `address(1)` to avoid issues with
        // reverting when minting to `address(0)`.
        vault.updateCurator(address(1));
        emit FractionalV1VaultCreated(data.token, data.tokenId, vaultId, vault);
        // Create distribution for fractional tokens for party.
        PartyGovernance(address(this)).distribute(
            ITokenDistributor.TokenType.Erc20,
            address(vault),
            vaultId
        );
        // Nothing left to do.
        return "";
    }
}

File 12 of 44 : IProposalExecutionEngine.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC721.sol";

// Upgradeable proposals logic contract interface.
interface IProposalExecutionEngine {
    struct ExecuteProposalParams {
        uint256 proposalId;
        bytes proposalData;
        bytes progressData;
        bytes extraData;
        uint256 flags;
        IERC721[] preciousTokens;
        uint256[] preciousTokenIds;
    }

    function initialize(address oldImpl, bytes memory initData) external;

    /// @notice Execute a proposal.
    /// @dev Must be delegatecalled into by PartyGovernance.
    ///      If the proposal is incomplete, continues its next step (if possible).
    ///      If another proposal is incomplete, this will fail. Only one
    ///      incomplete proposal is allowed at a time.
    /// @param params The data needed to execute the proposal.
    /// @return nextProgressData Bytes to be passed into the next `execute()` call,
    ///         if the proposal execution is incomplete. Otherwise, empty bytes
    ///         to indicate the proposal is complete.
    function executeProposal(
        ExecuteProposalParams memory params
    ) external returns (bytes memory nextProgressData);

    /// @notice Forcibly cancel an incomplete proposal.
    /// @param proposalId The ID of the proposal to cancel.
    /// @dev This is intended to be a last resort as it can leave a party in a
    ///      broken step. Whenever possible, proposals should be allowed to
    ///      complete their entire lifecycle.
    function cancelProposal(uint256 proposalId) external;
}

File 13 of 44 : LibProposal.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC721.sol";

library LibProposal {
    uint256 internal constant PROPOSAL_FLAG_UNANIMOUS = 0x1;

    function isTokenPrecious(
        IERC721 token,
        IERC721[] memory preciousTokens
    ) internal pure returns (bool) {
        for (uint256 i; i < preciousTokens.length; ++i) {
            if (token == preciousTokens[i]) {
                return true;
            }
        }
        return false;
    }

    function isTokenIdPrecious(
        IERC721 token,
        uint256 tokenId,
        IERC721[] memory preciousTokens,
        uint256[] memory preciousTokenIds
    ) internal pure returns (bool) {
        for (uint256 i; i < preciousTokens.length; ++i) {
            if (token == preciousTokens[i] && tokenId == preciousTokenIds[i]) {
                return true;
            }
        }
        return false;
    }
}

File 14 of 44 : ListOnOpenseaAdvancedProposal.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../globals/IGlobals.sol";
import "../globals/LibGlobals.sol";
import "../tokens/IERC721.sol";
import "../tokens/IERC1155.sol";
import "../utils/LibSafeCast.sol";

import "./vendor/IOpenseaExchange.sol";
import "./vendor/IOpenseaConduitController.sol";
import "./OpenseaHelpers.sol";
import "./ZoraHelpers.sol";
import "./LibProposal.sol";
import "./IProposalExecutionEngine.sol";

// Implements proposal listing an NFT on OpenSea (Seaport) with extra
// parameters. Inherited by the `ProposalExecutionEngine`.
// This contract will be delegatecall'ed into by `Party` proxy instances.
abstract contract ListOnOpenseaAdvancedProposal is OpenseaHelpers, ZoraHelpers {
    using LibSafeCast for uint256;

    // ABI-encoded `proposalData` passed into execute.
    struct OpenseaAdvancedProposalData {
        // The starting price (in ETH) when the listing is created.
        // This is also the reserve bid for the safety Zora auction.
        uint256 startPrice;
        // The ending price (in ETH) when the listing expires.
        // Unless listing as a dutch auction, this should be the same as `startPrice` in most cases.
        uint256 endPrice;
        // How long the listing is valid for.
        uint40 duration;
        // The type of the NFT token.
        TokenType tokenType;
        // The NFT token contract.
        address token;
        // the NFT token ID.
        uint256 tokenId;
        // Fees the taker must pay when filling the listing.
        uint256[] fees;
        // Respective recipients for each fee.
        address payable[] feeRecipients;
        // The first 4 bytes of the hash of a domain to attribute the listing to.
        // https://opensea.notion.site/opensea/Proposal-for-Seaport-Order-Attributions-via-Arbitrary-Domain-Hash-d0ad30b994ba48278c6e922983175285
        bytes4 domainHashPrefix;
    }

    enum TokenType {
        // The NFT is an ERC721.
        ERC721,
        // The NFT is an ERC1155.
        ERC1155
    }

    enum ListOnOpenseaStep {
        // The proposal hasn't been executed yet.
        None,
        // The NFT was placed in a Zora auction.
        ListedOnZora,
        // The Zora auction was either skipped or cancelled.
        RetrievedFromZora,
        // The NFT was listed on OpenSea.
        ListedOnOpenSea
    }

    // ABI-encoded `progressData` passed into execute in the `ListedOnOpenSea` step.
    struct OpenseaProgressData {
        // Hash of the OS order that was listed.
        bytes32 orderHash;
        // Conduit approved to spend the NFT.
        address conduit;
        // Expiration timestamp of the listing.
        uint40 expiry;
    }

    error OpenseaOrderStillActiveError(
        bytes32 orderHash,
        address token,
        uint256 tokenId,
        uint256 expiry
    );
    error InvalidFeeRecipients();

    event OpenseaOrderListed(
        IOpenseaExchange.OrderParameters orderParams,
        bytes32 orderHash,
        IERC721 token,
        uint256 tokenId,
        uint256 listPrice,
        uint256 expiry
    );
    event OpenseaAdvancedOrderListed(
        IOpenseaExchange.OrderParameters orderParams,
        bytes32 orderHash,
        address token,
        uint256 tokenId,
        uint256 startPrice,
        uint256 endPrice,
        uint256 expiry
    );
    event OpenseaOrderSold(bytes32 orderHash, IERC721 token, uint256 tokenId, uint256 listPrice);
    event OpenseaAdvancedOrderSold(
        bytes32 orderHash,
        address token,
        uint256 tokenId,
        uint256 startPrice,
        uint256 endPrice
    );
    event OpenseaOrderExpired(bytes32 orderHash, address token, uint256 tokenId, uint256 expiry);

    /// @notice The Seaport contract.
    IOpenseaExchange public immutable SEAPORT;
    /// @notice The Seaport conduit controller.
    IOpenseaConduitController public immutable CONDUIT_CONTROLLER;
    // The `Globals` contract storing global configuration values. This contract
    // is immutable and it’s address will never change.
    IGlobals private immutable _GLOBALS;

    // Set immutables.
    constructor(
        IGlobals globals,
        IOpenseaExchange seaport,
        IOpenseaConduitController conduitController
    ) {
        SEAPORT = seaport;
        CONDUIT_CONTROLLER = conduitController;
        _GLOBALS = globals;
    }

    // Try to create a listing (ultimately) on OpenSea (Seaport).
    // Creates a listing on Zora auction house for list price first. When that ends,
    // calling this function again will list on OpenSea. When that ends,
    // calling this function again will cancel the listing.
    function _executeListOnOpenseaAdvanced(
        IProposalExecutionEngine.ExecuteProposalParams memory params
    ) internal returns (bytes memory nextProgressData) {
        OpenseaAdvancedProposalData memory data = abi.decode(
            params.proposalData,
            (OpenseaAdvancedProposalData)
        );

        return _executeAdvancedOpenseaProposal(params, data);
    }

    function _executeAdvancedOpenseaProposal(
        IProposalExecutionEngine.ExecuteProposalParams memory params,
        OpenseaAdvancedProposalData memory data
    ) internal override returns (bytes memory nextProgressData) {
        bool isUnanimous = params.flags & LibProposal.PROPOSAL_FLAG_UNANIMOUS ==
            LibProposal.PROPOSAL_FLAG_UNANIMOUS;
        // If there is no `progressData` passed in, we're on the first step,
        // otherwise parse the first word of the `progressData` as the current step.
        ListOnOpenseaStep step = params.progressData.length == 0
            ? ListOnOpenseaStep.None
            : abi.decode(params.progressData, (ListOnOpenseaStep));
        if (step == ListOnOpenseaStep.None) {
            // First time executing the proposal.
            if (
                !isUnanimous &&
                LibProposal.isTokenIdPrecious(
                    IERC721(data.token),
                    data.tokenId,
                    params.preciousTokens,
                    params.preciousTokenIds
                )
            ) {
                // Not a unanimous vote and the token is precious, so list on Zora
                // auction house first.
                uint40 zoraTimeout = uint40(
                    _GLOBALS.getUint256(LibGlobals.GLOBAL_OS_ZORA_AUCTION_TIMEOUT)
                );
                uint40 zoraDuration = uint40(
                    _GLOBALS.getUint256(LibGlobals.GLOBAL_OS_ZORA_AUCTION_DURATION)
                );
                if (zoraTimeout != 0) {
                    uint256 auctionId = _createZoraAuction(
                        data.startPrice,
                        zoraTimeout,
                        zoraDuration,
                        IERC721(data.token),
                        data.tokenId
                    );
                    // Return the next step and data required to execute that step.
                    return
                        abi.encode(
                            ListOnOpenseaStep.ListedOnZora,
                            ZoraProgressData({
                                auctionId: auctionId,
                                minExpiry: (block.timestamp + zoraTimeout).safeCastUint256ToUint40()
                            })
                        );
                }
            }
            // Unanimous vote, not a precious, or no Zora duration.
            // Advance past the Zora auction phase by pretending we already
            // retrieved it from Zora.
            step = ListOnOpenseaStep.RetrievedFromZora;
        }
        if (step == ListOnOpenseaStep.ListedOnZora) {
            // The last time this proposal was executed, we listed it on Zora.
            // Now retrieve it from Zora.
            (, ZoraProgressData memory zpd) = abi.decode(
                params.progressData,
                (uint8, ZoraProgressData)
            );
            // Try to settle the Zora auction. This will revert if the auction
            // is still ongoing.
            ZoraAuctionStatus statusCode = _settleZoraAuction(
                zpd.auctionId,
                zpd.minExpiry,
                IERC721(data.token),
                data.tokenId
            );
            if (statusCode == ZoraAuctionStatus.Sold || statusCode == ZoraAuctionStatus.Cancelled) {
                // Auction sold or was cancelled. If it sold, there is nothing left to do.
                // If it was cancelled, we cannot safely proceed with the listing. Return
                // empty progress data to indicate there are no more steps to
                // execute.
                return "";
            }
            // The auction simply expired before anyone bid on it. We have the NFT
            // back now so move on to listing it on OpenSea immediately.
            step = ListOnOpenseaStep.RetrievedFromZora;
        }
        if (step == ListOnOpenseaStep.RetrievedFromZora) {
            // This step occurs if either:
            // 1) This is the first time this proposal is being executed and
            //    it is a unanimous vote or the NFT is not precious (guarded)
            //    so we intentionally skip the Zora listing step.
            // 2) The last time this proposal was executed, we settled an expired
            //    (no bids) Zora auction and can now proceed to the OpenSea
            //    listing step.

            {
                // Clamp the order duration to the global minimum and maximum.
                uint40 minDuration = uint40(
                    _GLOBALS.getUint256(LibGlobals.GLOBAL_OS_MIN_ORDER_DURATION)
                );
                uint40 maxDuration = uint40(
                    _GLOBALS.getUint256(LibGlobals.GLOBAL_OS_MAX_ORDER_DURATION)
                );
                if (minDuration != 0 && data.duration < minDuration) {
                    data.duration = minDuration;
                } else if (maxDuration != 0 && data.duration > maxDuration) {
                    data.duration = maxDuration;
                }
            }
            uint256 expiry = block.timestamp + uint256(data.duration);
            (address conduit, bytes32 conduitKey) = _approveConduit(
                data.token,
                data.tokenId,
                data.tokenType
            );
            bytes32 orderHash = _listOnOpensea(data, conduitKey, expiry);
            return abi.encode(ListOnOpenseaStep.ListedOnOpenSea, orderHash, conduit, expiry);
        }
        assert(step == ListOnOpenseaStep.ListedOnOpenSea);
        // The last time this proposal was executed, we listed it on OpenSea.
        // Now try to settle the listing (either it has expired or been filled).
        (, OpenseaProgressData memory opd) = abi.decode(
            params.progressData,
            (uint8, OpenseaProgressData)
        );
        _cleanUpListing(
            opd.orderHash,
            opd.conduit,
            opd.expiry,
            data.token,
            data.tokenId,
            data.tokenType,
            data.startPrice,
            data.endPrice
        );
        // This is the last possible step so return empty progress data
        // to indicate there are no more steps to execute.
        return "";
    }

    function _listOnOpensea(
        OpenseaAdvancedProposalData memory data,
        bytes32 conduitKey,
        uint256 expiry
    ) private returns (bytes32 orderHash) {
        if (data.fees.length != data.feeRecipients.length) {
            revert InvalidFeeRecipients();
        }

        // Create a (basic) Seaport 721 sell order.
        IOpenseaExchange.Order[] memory orders = new IOpenseaExchange.Order[](1);
        IOpenseaExchange.Order memory order = orders[0];
        IOpenseaExchange.OrderParameters memory orderParams = order.parameters;
        orderParams.offerer = address(this);
        orderParams.startTime = block.timestamp;
        orderParams.endTime = expiry;
        orderParams.zone = _GLOBALS.getAddress(LibGlobals.GLOBAL_OPENSEA_ZONE);
        orderParams.orderType = orderParams.zone == address(0)
            ? IOpenseaExchange.OrderType.FULL_OPEN
            : IOpenseaExchange.OrderType.FULL_RESTRICTED;
        orderParams.salt = uint256(bytes32(data.domainHashPrefix));
        orderParams.conduitKey = conduitKey;
        orderParams.totalOriginalConsiderationItems = 1 + data.fees.length;
        // What we are selling.
        orderParams.offer = new IOpenseaExchange.OfferItem[](1);
        IOpenseaExchange.OfferItem memory offer = orderParams.offer[0];
        offer.itemType = data.tokenType == TokenType.ERC721
            ? IOpenseaExchange.ItemType.ERC721
            : IOpenseaExchange.ItemType.ERC1155;
        offer.token = data.token;
        offer.identifierOrCriteria = data.tokenId;
        offer.startAmount = 1;
        offer.endAmount = 1;
        // What we want for it.
        orderParams.consideration = new IOpenseaExchange.ConsiderationItem[](1 + data.fees.length);
        IOpenseaExchange.ConsiderationItem memory cons = orderParams.consideration[0];
        cons.itemType = IOpenseaExchange.ItemType.NATIVE;
        cons.token = address(0);
        cons.identifierOrCriteria = 0;
        cons.startAmount = data.startPrice;
        cons.endAmount = data.endPrice;
        cons.recipient = payable(address(this));
        for (uint256 i; i < data.fees.length; ++i) {
            cons = orderParams.consideration[1 + i];
            cons.itemType = IOpenseaExchange.ItemType.NATIVE;
            cons.token = address(0);
            cons.identifierOrCriteria = 0;
            cons.startAmount = data.fees[i];
            // Adjusted such that the start amount and end amount of fees is
            // proportional to the start and end price of the listing for
            // dutch auctions.
            cons.endAmount = (data.fees[i] * data.endPrice) / data.startPrice;
            cons.recipient = data.feeRecipients[i];
        }
        orderHash = _getOrderHash(orderParams);
        // Validate the order on-chain so no signature is required to fill it.
        assert(SEAPORT.validate(orders));
        // Emit an event based on the type of listing.
        if (data.tokenType == TokenType.ERC721 && data.startPrice == data.endPrice) {
            emit OpenseaOrderListed(
                orderParams,
                orderHash,
                IERC721(data.token),
                data.tokenId,
                data.startPrice,
                expiry
            );
        } else {
            emit OpenseaAdvancedOrderListed(
                orderParams,
                orderHash,
                data.token,
                data.tokenId,
                data.startPrice,
                data.endPrice,
                expiry
            );
        }
    }

    function _approveConduit(
        address token,
        uint256 tokenId,
        TokenType tokenType
    ) private returns (address conduit, bytes32 conduitKey) {
        // Get the OpenSea conduit key.
        conduitKey = _GLOBALS.getBytes32(LibGlobals.GLOBAL_OPENSEA_CONDUIT_KEY);
        // Get conduit.
        (conduit, ) = CONDUIT_CONTROLLER.getConduit(conduitKey);
        // Approve OpenSea's conduit to spend our NFT. This should revert if we
        // do not own the NFT.
        if (tokenType == TokenType.ERC721) {
            IERC721(token).approve(conduit, tokenId);
        } else {
            IERC1155(token).setApprovalForAll(conduit, true);
        }
    }

    function _getOrderHash(
        IOpenseaExchange.OrderParameters memory orderParams
    ) private view returns (bytes32 orderHash) {
        // `getOrderHash()` wants an `OrderComponents` struct, which is an `OrderParameters`
        // struct but with the last field (`totalOriginalConsiderationItems`)
        // replaced with the maker's nonce. Since we (the maker) never increment
        // our Seaport nonce, it is always 0.
        // So we temporarily set the `totalOriginalConsiderationItems` field to 0,
        // force cast the `OrderParameters` into a `OrderComponents` type, call
        // `getOrderHash()`, and then restore the `totalOriginalConsiderationItems`
        // field's value before returning.
        uint256 origTotalOriginalConsiderationItems = orderParams.totalOriginalConsiderationItems;
        orderParams.totalOriginalConsiderationItems = 0;
        IOpenseaExchange.OrderComponents memory orderComps;
        assembly {
            orderComps := orderParams
        }
        orderHash = SEAPORT.getOrderHash(orderComps);
        orderParams.totalOriginalConsiderationItems = origTotalOriginalConsiderationItems;
    }

    function _cleanUpListing(
        bytes32 orderHash,
        address conduit,
        uint256 expiry,
        address token,
        uint256 tokenId,
        TokenType tokenType,
        uint256 startPrice,
        uint256 endPrice
    ) private {
        (, , uint256 totalFilled, ) = SEAPORT.getOrderStatus(orderHash);
        if (totalFilled != 0) {
            // The order was filled before it expired. We no longer have the NFT
            // and instead we have the ETH it was bought with.

            // Emit an event based on the type of listing.
            if (tokenType == TokenType.ERC721 && startPrice == endPrice) {
                emit OpenseaOrderSold(orderHash, IERC721(token), tokenId, startPrice);
            } else {
                emit OpenseaAdvancedOrderSold(orderHash, token, tokenId, startPrice, endPrice);
            }
        } else if (expiry <= block.timestamp) {
            // The order expired before it was filled. We retain the NFT.
            // Revoke Seaport approval.
            if (tokenType == TokenType.ERC721) {
                IERC721(token).approve(address(0), tokenId);
            } else {
                IERC1155(token).setApprovalForAll(conduit, false);
            }
            emit OpenseaOrderExpired(orderHash, token, tokenId, expiry);
        } else {
            // The order hasn't been bought and is still active.
            revert OpenseaOrderStillActiveError(orderHash, token, tokenId, expiry);
        }
    }
}

File 15 of 44 : ListOnOpenseaProposal.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "./ListOnOpenseaAdvancedProposal.sol";

// Implements proposal listing an NFT on OpenSea (Seaport). Inherited by the `ProposalExecutionEngine`.
// This contract will be delegatecall'ed into by `Party` proxy instances.
abstract contract ListOnOpenseaProposal is OpenseaHelpers {
    // ABI-encoded `proposalData` passed into execute.
    struct OpenseaProposalData {
        // The price (in ETH) to sell the NFT.
        uint256 listPrice;
        // How long the listing is valid for.
        uint40 duration;
        // The NFT token contract.
        IERC721 token;
        // the NFT token ID.
        uint256 tokenId;
        // Fees the taker must pay when filling the listing.
        uint256[] fees;
        // Respective recipients for each fee.
        address payable[] feeRecipients;
        // The first 4 bytes of the hash of a domain to attribute the listing to.
        // https://opensea.notion.site/opensea/Proposal-for-Seaport-Order-Attributions-via-Arbitrary-Domain-Hash-d0ad30b994ba48278c6e922983175285
        bytes4 domainHashPrefix;
    }

    // Try to create a listing (ultimately) on OpenSea (Seaport).
    // Creates a listing on Zora auction house for list price first. When that ends,
    // calling this function again will list on OpenSea. When that ends,
    // calling this function again will cancel the listing.
    function _executeListOnOpensea(
        IProposalExecutionEngine.ExecuteProposalParams memory params
    ) internal returns (bytes memory nextProgressData) {
        OpenseaProposalData memory data = abi.decode(params.proposalData, (OpenseaProposalData));

        return
            _executeAdvancedOpenseaProposal(
                params,
                ListOnOpenseaAdvancedProposal.OpenseaAdvancedProposalData({
                    startPrice: data.listPrice,
                    endPrice: data.listPrice,
                    duration: data.duration,
                    tokenType: ListOnOpenseaAdvancedProposal.TokenType.ERC721,
                    token: address(data.token),
                    tokenId: data.tokenId,
                    fees: data.fees,
                    feeRecipients: data.feeRecipients,
                    domainHashPrefix: data.domainHashPrefix
                })
            );
    }
}

File 16 of 44 : ListOnZoraProposal.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../globals/IGlobals.sol";
import "../globals/LibGlobals.sol";
import "../tokens/IERC721.sol";
import "../utils/LibRawResult.sol";
import "../utils/LibSafeERC721.sol";
import "../utils/LibSafeCast.sol";

import "../vendor/markets/IZoraAuctionHouse.sol";
import "./IProposalExecutionEngine.sol";
import "./ZoraHelpers.sol";

// Implements proposals auctioning an NFT on Zora. Inherited by the `ProposalExecutionEngine`.
// This contract will be delegatecall'ed into by `Party` proxy instances.
contract ListOnZoraProposal is ZoraHelpers {
    using LibRawResult for bytes;
    using LibSafeERC721 for IERC721;
    using LibSafeCast for uint256;

    enum ZoraStep {
        // Proposal has not been executed yet and should be listed on Zora.
        None,
        // Proposal was previously executed and the NFT is already listed on Zora.
        ListedOnZora
    }

    // ABI-encoded `proposalData` passed into execute.
    struct ZoraProposalData {
        // The minimum bid (ETH) for the NFT.
        uint256 listPrice;
        // How long before the auction can be cancelled if no one bids.
        uint40 timeout;
        // How long the auction lasts once a person bids on it.
        uint40 duration;
        // The token contract of the NFT being listed.
        IERC721 token;
        // The token ID of the NFT being listed.
        uint256 tokenId;
    }

    error ZoraListingNotExpired(uint256 auctionId, uint40 expiry);

    event ZoraAuctionCreated(
        uint256 auctionId,
        IERC721 token,
        uint256 tokenId,
        uint256 startingPrice,
        uint40 duration,
        uint40 timeoutTime
    );
    event ZoraAuctionExpired(uint256 auctionId, uint256 expiry);
    event ZoraAuctionSold(uint256 auctionId);
    event ZoraAuctionFailed(uint256 auctionId);

    // keccak256(abi.encodeWithSignature('Error(string)', "Auction hasn't begun"))
    bytes32 internal constant AUCTION_HASNT_BEGUN_ERROR_HASH =
        0x54a53788b7942d79bb6fcd40012c5e867208839fa1607e1f245558ee354e9565;
    // keccak256(abi.encodeWithSignature('Error(string)', "Auction doesn't exit"))
    bytes32 internal constant AUCTION_DOESNT_EXIST_ERROR_HASH =
        0x474ba0184a7cd5de777156a56f3859150719340a6974b6ee50f05c58139f4dc2;
    /// @notice Zora auction house contract.
    IZoraAuctionHouse public immutable ZORA;
    // The `Globals` contract storing global configuration values. This contract
    // is immutable and it’s address will never change.
    IGlobals private immutable _GLOBALS;

    // Set immutables.
    constructor(IGlobals globals, IZoraAuctionHouse zoraAuctionHouse) {
        ZORA = zoraAuctionHouse;
        _GLOBALS = globals;
    }

    // Auction an NFT we hold on Zora.
    // Calling this the first time will create a Zora auction.
    // Calling this the second time will either cancel or finalize the auction.
    function _executeListOnZora(
        IProposalExecutionEngine.ExecuteProposalParams memory params
    ) internal returns (bytes memory nextProgressData) {
        ZoraProposalData memory data = abi.decode(params.proposalData, (ZoraProposalData));
        // If there is progressData passed in, we're on the first step,
        // otherwise parse the first word of the progressData as the current step.
        ZoraStep step = params.progressData.length == 0
            ? ZoraStep.None
            : abi.decode(params.progressData, (ZoraStep));
        if (step == ZoraStep.None) {
            // Proposal hasn't executed yet.
            {
                // Clamp the Zora auction duration to the global minimum and maximum.
                uint40 minDuration = uint40(
                    _GLOBALS.getUint256(LibGlobals.GLOBAL_ZORA_MIN_AUCTION_DURATION)
                );
                uint40 maxDuration = uint40(
                    _GLOBALS.getUint256(LibGlobals.GLOBAL_ZORA_MAX_AUCTION_DURATION)
                );
                if (minDuration != 0 && data.duration < minDuration) {
                    data.duration = minDuration;
                } else if (maxDuration != 0 && data.duration > maxDuration) {
                    data.duration = maxDuration;
                }
                // Clamp the Zora auction timeout to the global maximum.
                uint40 maxTimeout = uint40(
                    _GLOBALS.getUint256(LibGlobals.GLOBAL_ZORA_MAX_AUCTION_TIMEOUT)
                );
                if (maxTimeout != 0 && data.timeout > maxTimeout) {
                    data.timeout = maxTimeout;
                }
            }
            // Create a Zora auction for the NFT.
            uint256 auctionId = _createZoraAuction(
                data.listPrice,
                data.timeout,
                data.duration,
                data.token,
                data.tokenId
            );
            return
                abi.encode(
                    ZoraStep.ListedOnZora,
                    ZoraProgressData({
                        auctionId: auctionId,
                        minExpiry: (block.timestamp + data.timeout).safeCastUint256ToUint40()
                    })
                );
        }
        assert(step == ZoraStep.ListedOnZora);
        (, ZoraProgressData memory pd) = abi.decode(
            params.progressData,
            (ZoraStep, ZoraProgressData)
        );
        _settleZoraAuction(pd.auctionId, pd.minExpiry, data.token, data.tokenId);
        // Nothing left to do.
        return "";
    }

    // Transfer and create a Zora auction for the `token` + `tokenId`.
    function _createZoraAuction(
        // The minimum bid.
        uint256 listPrice,
        // How long the auction must wait for the first bid.
        uint40 timeout,
        // How long the auction will run for once a bid has been placed.
        uint40 duration,
        IERC721 token,
        uint256 tokenId
    ) internal override returns (uint256 auctionId) {
        token.approve(address(ZORA), tokenId);
        auctionId = ZORA.createAuction(
            tokenId,
            token,
            duration,
            listPrice,
            payable(address(0)),
            0,
            IERC20(address(0)) // Indicates ETH sale
        );
        emit ZoraAuctionCreated(
            auctionId,
            token,
            tokenId,
            listPrice,
            duration,
            uint40(block.timestamp + timeout)
        );
    }

    // Either cancel or finalize a Zora auction.
    function _settleZoraAuction(
        uint256 auctionId,
        uint40 minExpiry,
        IERC721 token,
        uint256 tokenId
    ) internal override returns (ZoraAuctionStatus statusCode) {
        // Getting the state of an auction is super expensive so it seems
        // cheaper to just let `endAuction()` fail and react to the error.
        try ZORA.endAuction(auctionId) {
            // Check whether auction cancelled due to a failed transfer during
            // settlement by seeing if we now possess the NFT.
            if (token.safeOwnerOf(tokenId) == address(this)) {
                emit ZoraAuctionFailed(auctionId);
                return ZoraAuctionStatus.Cancelled;
            }
        } catch (bytes memory errData) {
            bytes32 errHash = keccak256(errData);
            if (errHash == AUCTION_HASNT_BEGUN_ERROR_HASH) {
                // No bids placed.
                // Cancel if we're past the timeout.
                if (minExpiry > uint40(block.timestamp)) {
                    revert ZoraListingNotExpired(auctionId, minExpiry);
                }
                ZORA.cancelAuction(auctionId);
                emit ZoraAuctionExpired(auctionId, minExpiry);
                return ZoraAuctionStatus.Expired;
            } else if (errHash != AUCTION_DOESNT_EXIST_ERROR_HASH) {
                // Otherwise, we should get an auction doesn't exist error,
                // because someone else must have called `endAuction()`.
                // If we didn't then something is wrong, so revert.
                errData.rawRevert();
            }
            // Already ended by someone else. Nothing to do.
        }
        emit ZoraAuctionSold(auctionId);
        return ZoraAuctionStatus.Sold;
    }
}

File 17 of 44 : OpenseaHelpers.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "./ListOnOpenseaAdvancedProposal.sol";
import "./IProposalExecutionEngine.sol";
import "./vendor/IOpenseaExchange.sol";

// Abstract Opensea interaction functions.
abstract contract OpenseaHelpers {
    function _executeAdvancedOpenseaProposal(
        IProposalExecutionEngine.ExecuteProposalParams memory params,
        ListOnOpenseaAdvancedProposal.OpenseaAdvancedProposalData memory data
    ) internal virtual returns (bytes memory nextProgressData);
}

File 18 of 44 : ProposalStorage.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "./IProposalExecutionEngine.sol";
import "../utils/LibRawResult.sol";

// The storage bucket shared by `PartyGovernance` and the `ProposalExecutionEngine`.
// Read this for more context on the pattern motivating this:
// https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/explicit-storage-buckets
abstract contract ProposalStorage {
    using LibRawResult for bytes;

    struct SharedProposalStorage {
        IProposalExecutionEngine engineImpl;
    }

    uint256 internal constant PROPOSAL_FLAG_UNANIMOUS = 0x1;
    uint256 private constant SHARED_STORAGE_SLOT =
        uint256(keccak256("ProposalStorage.SharedProposalStorage"));

    function _getProposalExecutionEngine() internal view returns (IProposalExecutionEngine impl) {
        return _getSharedProposalStorage().engineImpl;
    }

    function _setProposalExecutionEngine(IProposalExecutionEngine impl) internal {
        _getSharedProposalStorage().engineImpl = impl;
    }

    function _initProposalImpl(IProposalExecutionEngine impl, bytes memory initData) internal {
        SharedProposalStorage storage stor = _getSharedProposalStorage();
        IProposalExecutionEngine oldImpl = stor.engineImpl;
        stor.engineImpl = impl;
        (bool s, bytes memory r) = address(impl).delegatecall(
            abi.encodeCall(IProposalExecutionEngine.initialize, (address(oldImpl), initData))
        );
        if (!s) {
            r.rawRevert();
        }
    }

    function _getSharedProposalStorage() private pure returns (SharedProposalStorage storage stor) {
        uint256 s = SHARED_STORAGE_SLOT;
        assembly {
            stor.slot := s
        }
    }
}

File 19 of 44 : ZoraHelpers.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC721.sol";

// Abstract Zora interaction functions.
// Used by both `ListOnZoraProposal` and `ListOnOpenseaProposal`.
abstract contract ZoraHelpers {
    // ABI-encoded `progressData` passed into execute in the `ListedOnZora` step.
    struct ZoraProgressData {
        // Auction ID.
        uint256 auctionId;
        // The minimum timestamp when we can cancel the auction if no one bids.
        uint40 minExpiry;
    }

    enum ZoraAuctionStatus {
        Sold,
        Expired,
        Cancelled
    }

    // Transfer and create a Zora auction for the token + tokenId.
    function _createZoraAuction(
        // The minimum bid.
        uint256 listPrice,
        // How long the auction must wait for the first bid.
        uint40 timeout,
        // How long the auction will run for once a bid has been placed.
        uint40 duration,
        IERC721 token,
        uint256 tokenId
    ) internal virtual returns (uint256 auctionId);

    // Either cancel or finalize a Zora auction.
    function _settleZoraAuction(
        uint256 auctionId,
        uint40 minExpiry,
        IERC721 token,
        uint256 tokenId
    ) internal virtual returns (ZoraAuctionStatus statusCode);
}

File 20 of 44 : FractionalV1.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

import "../../tokens/IERC20.sol";
import "../../tokens/IERC721.sol";

/// @dev FractionalVaultFactory interface from
/// https://github.com/fractional-company/contracts/blob/643bb669ad71aac8d1b11f0300c9bb0dec494daa/src/ERC721VaultFactory.sol
interface IFractionalV1VaultFactory {
    function vaultCount() external view returns (uint256 count);

    function vaults(uint256 vaultId) external view returns (IFractionalV1Vault vault);

    function mint(
        string calldata name,
        string calldata symbol,
        IERC721 token,
        uint256 tokenId,
        uint256 supply,
        uint256 listPrice,
        uint256 fee
    ) external returns (uint256 vaultId);
}

/// @dev ERC721TokenVault interface from
/// https://github.com/fractional-company/contracts/blob/d4faa2dddf010d12b87eae8054f485656c8ed14b/src/ERC721TokenVault.sol
interface IFractionalV1Vault is IERC20 {
    function curator() external view returns (address curator_);

    function auctionEnd() external view returns (uint256);

    function reservePrice() external view returns (uint256);

    function livePrice() external view returns (uint256);

    function winning() external view returns (address);

    function updateCurator(address curator_) external;

    function updateUserPrice(uint256) external;

    function start() external payable;

    function bid() external payable;

    function end() external;

    function redeem() external;

    function cash() external;
}

File 21 of 44 : IOpenseaConduitController.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

interface IOpenseaConduitController {
    function getKey(address conduit) external view returns (bytes32 conduitKey);

    function getConduit(bytes32 conduitKey) external view returns (address conduit, bool exists);
}

File 22 of 44 : IOpenseaExchange.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

interface IOpenseaExchange {
    error InvalidTime();

    enum OrderType {
        FULL_OPEN,
        PARTIAL_OPEN,
        FULL_RESTRICTED,
        PARTIAL_RESTRICTED
    }

    enum ItemType {
        NATIVE,
        ERC20,
        ERC721,
        ERC1155,
        ERC721_WITH_CRITERIA,
        ERC1155_WITH_CRITERIA
    }

    enum BasicOrderType {
        ETH_TO_ERC721_FULL_OPEN,
        ETH_TO_ERC721_PARTIAL_OPEN,
        ETH_TO_ERC721_FULL_RESTRICTED,
        ETH_TO_ERC721_PARTIAL_RESTRICTED,
        ETH_TO_ERC1155_FULL_OPEN,
        ETH_TO_ERC1155_PARTIAL_OPEN,
        ETH_TO_ERC1155_FULL_RESTRICTED,
        ETH_TO_ERC1155_PARTIAL_RESTRICTED,
        ERC20_TO_ERC721_FULL_OPEN,
        ERC20_TO_ERC721_PARTIAL_OPEN,
        ERC20_TO_ERC721_FULL_RESTRICTED,
        ERC20_TO_ERC721_PARTIAL_RESTRICTED,
        ERC20_TO_ERC1155_FULL_OPEN,
        ERC20_TO_ERC1155_PARTIAL_OPEN,
        ERC20_TO_ERC1155_FULL_RESTRICTED,
        ERC20_TO_ERC1155_PARTIAL_RESTRICTED,
        ERC721_TO_ERC20_FULL_OPEN,
        ERC721_TO_ERC20_PARTIAL_OPEN,
        ERC721_TO_ERC20_FULL_RESTRICTED,
        ERC721_TO_ERC20_PARTIAL_RESTRICTED,
        ERC1155_TO_ERC20_FULL_OPEN,
        ERC1155_TO_ERC20_PARTIAL_OPEN,
        ERC1155_TO_ERC20_FULL_RESTRICTED,
        ERC1155_TO_ERC20_PARTIAL_RESTRICTED
    }

    struct OfferItem {
        ItemType itemType;
        address token;
        uint256 identifierOrCriteria;
        uint256 startAmount;
        uint256 endAmount;
    }

    struct ConsiderationItem {
        ItemType itemType;
        address token;
        uint256 identifierOrCriteria;
        uint256 startAmount;
        uint256 endAmount;
        address payable recipient;
    }

    struct OrderParameters {
        address offerer;
        address zone;
        OfferItem[] offer;
        ConsiderationItem[] consideration;
        OrderType orderType;
        uint256 startTime;
        uint256 endTime;
        bytes32 zoneHash;
        uint256 salt;
        bytes32 conduitKey;
        uint256 totalOriginalConsiderationItems;
    }

    struct Order {
        OrderParameters parameters;
        bytes signature;
    }

    struct OrderComponents {
        address offerer;
        address zone;
        OfferItem[] offer;
        ConsiderationItem[] consideration;
        OrderType orderType;
        uint256 startTime;
        uint256 endTime;
        bytes32 zoneHash;
        uint256 salt;
        bytes32 conduitKey;
        uint256 nonce;
    }

    struct AdditionalRecipient {
        uint256 amount;
        address payable recipient;
    }

    struct BasicOrderParameters {
        address considerationToken;
        uint256 considerationIdentifier;
        uint256 considerationAmount;
        address payable offerer;
        address zone;
        address offerToken;
        uint256 offerIdentifier;
        uint256 offerAmount;
        BasicOrderType basicOrderType;
        uint256 startTime;
        uint256 endTime;
        bytes32 zoneHash;
        uint256 salt;
        bytes32 offererConduitKey;
        bytes32 fulfillerConduitKey;
        uint256 totalOriginalAdditionalRecipients;
        AdditionalRecipient[] additionalRecipients;
        bytes signature;
    }

    function cancel(OrderComponents[] calldata orders) external returns (bool cancelled);

    function validate(Order[] calldata orders) external returns (bool validated);

    function fulfillBasicOrder(
        BasicOrderParameters calldata parameters
    ) external payable returns (bool fulfilled);

    function fulfillOrder(
        Order calldata order,
        bytes32 fulfillerConduitKey
    ) external payable returns (bool fulfilled);

    function getOrderStatus(
        bytes32 orderHash
    )
        external
        view
        returns (bool isValidated, bool isCancelled, uint256 totalFilled, uint256 totalSize);

    function getOrderHash(OrderComponents calldata order) external view returns (bytes32 orderHash);

    function getNonce(address offerer) external view returns (uint256 nonce);
}

File 23 of 44 : RendererStorage.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "solmate/utils/SSTORE2.sol";
import "../utils/Multicall.sol";

contract RendererStorage is Multicall {
    error AlreadySetError();
    error NotOwnerError(address caller, address owner);

    event OwnershipTransferred(address previousOwner, address newOwner);

    uint256 constant CROWDFUND_CARD_DATA = 0;
    uint256 constant PARTY_CARD_DATA = 1;

    /// @notice Address allowed to store new data.
    address public owner;

    /// @notice Customization presets by ID, used for rendering cards. Begins at
    ///         1, 0 is reserved to indicate in `getPresetFor()` that a
    ///         party instance use the preset set by the crowdfund instance that
    ///         created it.
    mapping(uint256 => bytes) public customizationPresets;
    /// @notice Customization preset used by a crowdfund or party instance.
    mapping(address => uint256) public getPresetFor;
    /// @notice Addresses where URI data chunks are stored.
    mapping(uint256 => address) public files;

    modifier onlyOwner() {
        address owner_ = owner;
        if (msg.sender != owner_) {
            revert NotOwnerError(msg.sender, owner_);
        }

        _;
    }

    constructor(address _owner) {
        // Set the address allowed to write new data.
        owner = _owner;

        // Write URI data used by V1 of the renderers:

        files[CROWDFUND_CARD_DATA] = SSTORE2.write(
            bytes(
                '<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>'
            )
        );

        files[PARTY_CARD_DATA] = SSTORE2.write(
            bytes(
                ' 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)"/>'
            )
        );
    }

    /// @notice Transfer ownership to a new owner.
    /// @param newOwner The address to transfer ownership to.
    function transferOwnership(address newOwner) external onlyOwner {
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    /// @notice Write data to be accessed by a given file key.
    /// @param key The key to access the written data.
    /// @param data The data to be written.
    function writeFile(uint256 key, string memory data) external onlyOwner {
        files[key] = SSTORE2.write(bytes(data));
    }

    /// @notice Read data using a given file key.
    /// @param key The key to access the stored data.
    /// @return data The data stored at the given key.
    function readFile(uint256 key) external view returns (string memory data) {
        return string(SSTORE2.read(files[key]));
    }

    /// @notice Create or set a customization preset for renderers to use.
    /// @param id The ID of the customization preset.
    /// @param customizationData Data decoded by renderers used to render the SVG according to the preset.
    function createCustomizationPreset(
        uint256 id,
        bytes memory customizationData
    ) external onlyOwner {
        customizationPresets[id] = customizationData;
    }

    /// @notice For crowdfund or party instances to set the customization preset they want to use.
    /// @param id The ID of the customization preset.
    function useCustomizationPreset(uint256 id) external {
        getPresetFor[msg.sender] = id;
    }
}

File 24 of 44 : ERC1155Receiver.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

import "../vendor/solmate/ERC1155.sol";
import "../utils/EIP165.sol";

abstract contract ERC1155Receiver is EIP165, ERC1155TokenReceiverBase {
    /// @inheritdoc EIP165
    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
        return
            super.supportsInterface(interfaceId) ||
            interfaceId == type(ERC1155TokenReceiverBase).interfaceId;
    }
}

File 25 of 44 : ERC721Receiver.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

import "./IERC721Receiver.sol";
import "../utils/EIP165.sol";
import "../vendor/solmate/ERC721.sol";

/// @notice Mixin for contracts that want to receive ERC721 tokens.
/// @dev Use this instead of solmate's ERC721TokenReceiver because the
///      compiler has issues when overriding EIP165/IERC721Receiver functions.
abstract contract ERC721Receiver is IERC721Receiver, EIP165, ERC721TokenReceiver {
    /// @inheritdoc IERC721Receiver
    function onERC721Received(
        address,
        address,
        uint256,
        bytes memory
    ) public virtual override(IERC721Receiver, ERC721TokenReceiver) returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }

    /// @inheritdoc EIP165
    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
        return
            EIP165.supportsInterface(interfaceId) ||
            interfaceId == type(IERC721Receiver).interfaceId;
    }
}

File 26 of 44 : IERC1155.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

// Minimal ERC1155 interface.
interface IERC1155 {
    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 amount
    );
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] amounts
    );
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    function setApprovalForAll(address operator, bool approved) external;

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;

    function balanceOf(address owner, uint256 tokenId) external view returns (uint256);

    function isApprovedForAll(address owner, address spender) external view returns (bool);

    function balanceOfBatch(
        address[] calldata owners,
        uint256[] calldata ids
    ) external view returns (uint256[] memory balances);
}

File 27 of 44 : IERC20.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

// Minimal ERC20 interface.
interface IERC20 {
    event Transfer(address indexed owner, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 allowance);

    function transfer(address to, uint256 amount) external returns (bool);

    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    function approve(address spender, uint256 allowance) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    function balanceOf(address owner) external view returns (uint256);
}

File 28 of 44 : IERC721.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

// Minimal ERC721 interface.
interface IERC721 {
    event Transfer(address indexed owner, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed operator, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    function transferFrom(address from, address to, uint256 tokenId) external;

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    function approve(address operator, uint256 tokenId) external;

    function setApprovalForAll(address operator, bool isApproved) external;

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function getApproved(uint256 tokenId) external view returns (address);

    function isApprovedForAll(address owner, address operator) external view returns (bool);

    function ownerOf(uint256 tokenId) external view returns (address);

    function balanceOf(address owner) external view returns (uint256);
}

File 29 of 44 : IERC721Receiver.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes memory data
    ) external returns (bytes4);
}

File 30 of 44 : EIP165.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

abstract contract EIP165 {
    /// @notice Query if a contract implements an interface.
    /// @param interfaceId The interface identifier, as specified in ERC-165
    /// @return `true` if the contract implements `interfaceId` and
    ///         `interfaceId` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
        return interfaceId == this.supportsInterface.selector;
    }
}

File 31 of 44 : Implementation.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

// Base contract for all contracts intended to be delegatecalled into.
abstract contract Implementation {
    error OnlyDelegateCallError();
    error OnlyConstructorError();

    address public immutable IMPL;

    constructor() {
        IMPL = address(this);
    }

    // Reverts if the current function context is not inside of a delegatecall.
    modifier onlyDelegateCall() virtual {
        if (address(this) == IMPL) {
            revert OnlyDelegateCallError();
        }
        _;
    }

    // Reverts if the current function context is not inside of a constructor.
    modifier onlyConstructor() {
        if (address(this).code.length != 0) {
            revert OnlyConstructorError();
        }
        _;
    }
}

File 32 of 44 : LibAddress.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

library LibAddress {
    error EthTransferFailed(address receiver, bytes errData);

    // Transfer ETH with full gas stipend.
    function transferEth(address payable receiver, uint256 amount) internal {
        if (amount == 0) return;

        (bool s, bytes memory r) = receiver.call{ value: amount }("");
        if (!s) {
            revert EthTransferFailed(receiver, r);
        }
    }
}

File 33 of 44 : LibERC20Compat.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC20.sol";

// Compatibility helpers for ERC20s.
library LibERC20Compat {
    error NotATokenError(IERC20 token);
    error TokenTransferFailedError(IERC20 token, address to, uint256 amount);

    // Perform an `IERC20.transfer()` handling non-compliant implementations.
    function compatTransfer(IERC20 token, address to, uint256 amount) internal {
        (bool s, bytes memory r) = address(token).call(
            abi.encodeCall(IERC20.transfer, (to, amount))
        );
        if (s) {
            if (r.length == 0) {
                uint256 cs;
                assembly {
                    cs := extcodesize(token)
                }
                if (cs == 0) {
                    revert NotATokenError(token);
                }
                return;
            }
            if (abi.decode(r, (bool))) {
                return;
            }
        }
        revert TokenTransferFailedError(token, to, amount);
    }
}

File 34 of 44 : LibRawResult.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

library LibRawResult {
    // Revert with the data in `b`.
    function rawRevert(bytes memory b) internal pure {
        assembly {
            revert(add(b, 32), mload(b))
        }
    }

    // Return with the data in `b`.
    function rawReturn(bytes memory b) internal pure {
        assembly {
            return(add(b, 32), mload(b))
        }
    }
}

File 35 of 44 : LibSafeCast.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

library LibSafeCast {
    error Uint256ToUint96CastOutOfRange(uint256 v);
    error Uint256ToInt192CastOutOfRange(uint256 v);
    error Int192ToUint96CastOutOfRange(int192 i192);
    error Uint256ToInt128CastOutOfRangeError(uint256 u256);
    error Uint256ToUint128CastOutOfRangeError(uint256 u256);
    error Uint256ToUint40CastOutOfRangeError(uint256 u256);

    function safeCastUint256ToUint96(uint256 v) internal pure returns (uint96) {
        if (v > uint256(type(uint96).max)) {
            revert Uint256ToUint96CastOutOfRange(v);
        }
        return uint96(v);
    }

    function safeCastUint256ToUint128(uint256 v) internal pure returns (uint128) {
        if (v > uint256(type(uint128).max)) {
            revert Uint256ToUint128CastOutOfRangeError(v);
        }
        return uint128(v);
    }

    function safeCastUint256ToInt192(uint256 v) internal pure returns (int192) {
        if (v > uint256(uint192(type(int192).max))) {
            revert Uint256ToInt192CastOutOfRange(v);
        }
        return int192(uint192(v));
    }

    function safeCastUint96ToInt192(uint96 v) internal pure returns (int192) {
        return int192(uint192(v));
    }

    function safeCastInt192ToUint96(int192 i192) internal pure returns (uint96) {
        if (i192 < 0 || i192 > int192(uint192(type(uint96).max))) {
            revert Int192ToUint96CastOutOfRange(i192);
        }
        return uint96(uint192(i192));
    }

    function safeCastUint256ToInt128(uint256 x) internal pure returns (int128) {
        if (x > uint256(uint128(type(int128).max))) {
            revert Uint256ToInt128CastOutOfRangeError(x);
        }
        return int128(uint128(x));
    }

    function safeCastUint256ToUint40(uint256 x) internal pure returns (uint40) {
        if (x > uint256(type(uint40).max)) {
            revert Uint256ToUint40CastOutOfRangeError(x);
        }
        return uint40(x);
    }
}

File 36 of 44 : LibSafeERC721.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../tokens/IERC721.sol";
import "./LibRawResult.sol";

library LibSafeERC721 {
    using LibRawResult for bytes;

    // Call `IERC721.ownerOf()` without reverting and return `address(0)` if:
    // - `tokenID` does not exist.
    // - `token` is an EOA
    // - `token` is an empty contract
    // - `token` is a "bad" implementation of ERC721 that returns nothing for
    //   `ownerOf()`
    function safeOwnerOf(IERC721 token, uint256 tokenId) internal view returns (address owner) {
        (bool s, bytes memory r) = address(token).staticcall(
            abi.encodeCall(token.ownerOf, (tokenId))
        );

        if (!s || r.length < 32) {
            return address(0);
        }

        return abi.decode(r, (address));
    }
}

File 37 of 44 : Multicall.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../utils/LibRawResult.sol";

abstract contract Multicall {
    using LibRawResult for bytes;

    /// @notice Perform multiple delegatecalls on ourselves.
    function multicall(bytes[] calldata multicallData) external {
        for (uint256 i; i < multicallData.length; ++i) {
            (bool s, bytes memory r) = address(this).delegatecall(multicallData[i]);
            if (!s) {
                r.rawRevert();
            }
        }
    }
}

File 38 of 44 : ReadOnlyDelegateCall.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "./LibRawResult.sol";

interface IReadOnlyDelegateCall {
    // Marked `view` so that `_readOnlyDelegateCall` can be `view` as well.
    function delegateCallAndRevert(address impl, bytes memory callData) external view;
}

// Inherited by contracts to perform read-only delegate calls.
abstract contract ReadOnlyDelegateCall {
    using LibRawResult for bytes;

    // Delegatecall into implement and revert with the raw result.
    function delegateCallAndRevert(address impl, bytes memory callData) external {
        // Attempt to gate to only `_readOnlyDelegateCall()` invocations.
        require(msg.sender == address(this));
        (bool s, bytes memory r) = impl.delegatecall(callData);
        // Revert with success status and return data.
        abi.encode(s, r).rawRevert();
    }

    // Perform a `delegateCallAndRevert()` then return the raw result data.
    function _readOnlyDelegateCall(address impl, bytes memory callData) internal view {
        try IReadOnlyDelegateCall(address(this)).delegateCallAndRevert(impl, callData) {
            // Should never happen.
            assert(false);
        } catch (bytes memory r) {
            (bool success, bytes memory resultData) = abi.decode(r, (bool, bytes));
            if (!success) {
                resultData.rawRevert();
            }
            resultData.rawReturn();
        }
    }
}

File 39 of 44 : IZoraAuctionHouse.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8;

import "../../tokens/IERC721.sol";
import "../../tokens/IERC20.sol";

// Based on https://etherscan.io/address/0xe468ce99444174bd3bbbed09209577d25d1ad673#code
interface IZoraAuctionHouse {
    struct Auction {
        // ID for the ERC721 token
        uint256 tokenId;
        // Address for the ERC721 contract
        IERC721 tokenContract;
        // Whether or not the auction curator has approved the auction to start
        bool approved;
        // The current highest bid amount
        uint256 amount;
        // The length of time to run the auction for, after the first bid was made
        uint256 duration;
        // The time of the first bid
        uint256 firstBidTime;
        // The minimum price of the first bid
        uint256 reservePrice;
        // The sale percentage to send to the curator
        uint8 curatorFeePercentage;
        // The address that should receive the funds once the NFT is sold.
        address tokenOwner;
        // The address of the current highest bid
        address payable bidder;
        // The address of the auction's curator.
        // The curator can reject or approve an auction
        address payable curator;
        // The address of the ERC-20 currency to run the auction with.
        // If set to 0x0, the auction will be run in ETH
        IERC20 auctionCurrency;
    }

    event AuctionCreated(
        uint256 indexed auctionId,
        uint256 indexed tokenId,
        address indexed tokenContract,
        uint256 duration,
        uint256 reservePrice,
        address tokenOwner,
        address curator,
        uint8 curatorFeePercentage,
        address auctionCurrency
    );

    function createAuction(
        uint256 tokenId,
        IERC721 tokenContract,
        uint256 duration,
        uint256 reservePrice,
        address payable curator,
        uint8 curatorFeePercentages,
        IERC20 auctionCurrency
    ) external returns (uint256);

    function createBid(uint256 auctionId, uint256 amount) external payable;

    function endAuction(uint256 auctionId) external;

    function cancelAuction(uint256 auctionId) external;

    function auctions(uint256 auctionId) external view returns (Auction memory auction);

    function timeBuffer() external view returns (uint256);

    function minBidIncrementPercentage() external view returns (uint8);
}

File 40 of 44 : ERC1155.sol
// SPDX-License-Identifier: AGPL-3.0-only
// Based on solmate commit 1681dc505f4897ef636f0435d01b1aa027fdafaf (v6.4.0)
//  @ https://github.com/Rari-Capital/solmate/blob/1681dc505f4897ef636f0435d01b1aa027fdafaf/src/tokens/ERC1155.sol
// Only modified to inherit IERC1155 and rename ERC1155TokenReceiver -> ERC1155TokenReceiverBase.
pragma solidity ^0.8;

import "../../tokens/IERC1155.sol";

/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 is IERC1155 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event URI(string value, uint256 indexed id);

    /*//////////////////////////////////////////////////////////////
                             ERC1155 STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(address => mapping(uint256 => uint256)) public balanceOf;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                             METADATA LOGIC
    //////////////////////////////////////////////////////////////*/

    function uri(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                              ERC1155 LOGIC
    //////////////////////////////////////////////////////////////*/

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) public virtual {
        require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");

        balanceOf[from][id] -= amount;
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, from, to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiverBase(to).onERC1155Received(
                    msg.sender,
                    from,
                    id,
                    amount,
                    data
                ) == ERC1155TokenReceiverBase.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) public virtual {
        require(ids.length == amounts.length, "LENGTH_MISMATCH");

        require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");

        // Storing these outside the loop saves ~15 gas per iteration.
        uint256 id;
        uint256 amount;

        for (uint256 i; i < ids.length; ) {
            id = ids[i];
            amount = amounts[i];

            balanceOf[from][id] -= amount;
            balanceOf[to][id] += amount;

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, from, to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiverBase(to).onERC1155BatchReceived(
                    msg.sender,
                    from,
                    ids,
                    amounts,
                    data
                ) == ERC1155TokenReceiverBase.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function balanceOfBatch(
        address[] calldata owners,
        uint256[] calldata ids
    ) public view virtual returns (uint256[] memory balances) {
        require(owners.length == ids.length, "LENGTH_MISMATCH");

        balances = new uint256[](owners.length);

        // Unchecked because the only math done is incrementing
        // the array index counter which cannot possibly overflow.
        unchecked {
            for (uint256 i; i < owners.length; ++i) {
                balances[i] = balanceOf[owners[i]][ids[i]];
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
            interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, address(0), to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiverBase(to).onERC1155Received(
                    msg.sender,
                    address(0),
                    id,
                    amount,
                    data
                ) == ERC1155TokenReceiverBase.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchMint(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i; i < idsLength; ) {
            balanceOf[to][ids[i]] += amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, address(0), to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiverBase(to).onERC1155BatchReceived(
                    msg.sender,
                    address(0),
                    ids,
                    amounts,
                    data
                ) == ERC1155TokenReceiverBase.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchBurn(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i; i < idsLength; ) {
            balanceOf[from][ids[i]] -= amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, from, address(0), ids, amounts);
    }

    function _burn(address from, uint256 id, uint256 amount) internal virtual {
        balanceOf[from][id] -= amount;

        emit TransferSingle(msg.sender, from, address(0), id, amount);
    }
}

/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155TokenReceiverBase {
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC1155TokenReceiverBase.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC1155TokenReceiverBase.onERC1155BatchReceived.selector;
    }
}

File 41 of 44 : ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-only
// Based on solmate commit 1681dc505f4897ef636f0435d01b1aa027fdafaf (v6.4.0)
//  @ https://github.com/Rari-Capital/solmate/blob/1681dc505f4897ef636f0435d01b1aa027fdafaf/src/tokens/ERC1155.sol
// Only modified to inherit IERC721 and EIP165.
pragma solidity >=0.8.0;

// NOTE: Only modified to inherit IERC20 and EIP165
import "../../tokens/IERC721.sol";
import "../../utils/EIP165.sol";

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 is IERC721, EIP165 {
    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id /* view */) public virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(address from, address to, uint256 id) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from ||
                isApprovedForAll[from][msg.sender] ||
                msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(address from, address to, uint256 id) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes calldata data
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
        // NOTE: modified from original to call super.
        return
            super.supportsInterface(interfaceId) ||
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}

File 42 of 44 : IERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 *
 * _Available since v4.5._
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

File 43 of 44 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 44 of 44 : SSTORE2.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
    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.

    /*//////////////////////////////////////////////////////////////
                               WRITE LOGIC
    //////////////////////////////////////////////////////////////*/

    function write(bytes memory data) internal returns (address pointer) {
        // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
        bytes memory runtimeCode = abi.encodePacked(hex"00", data);

        bytes memory creationCode = abi.encodePacked(
            //---------------------------------------------------------------------------------------------------------------//
            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
            //---------------------------------------------------------------------------------------------------------------//
            // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
            // 0xf3    |  0xf3               | RETURN       |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            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.
            runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
        );

        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with the generated creation code.
            // We start 32 bytes into the code to avoid copying the byte length.
            pointer := create(0, add(creationCode, 32), mload(creationCode))
        }

        require(pointer != address(0), "DEPLOYMENT_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                               READ LOGIC
    //////////////////////////////////////////////////////////////*/

    function read(address pointer) internal view returns (bytes memory) {
        return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
    }

    function read(address pointer, uint256 start) internal view returns (bytes memory) {
        start += DATA_OFFSET;

        return readBytecode(pointer, start, pointer.code.length - start);
    }

    function read(
        address pointer,
        uint256 start,
        uint256 end
    ) internal view returns (bytes memory) {
        start += DATA_OFFSET;
        end += DATA_OFFSET;

        require(pointer.code.length >= end, "OUT_OF_BOUNDS");

        return readBytecode(pointer, start, end - start);
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function readBytecode(
        address pointer,
        uint256 start,
        uint256 size
    ) private view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            data := mload(0x40)

            // Update the free memory pointer to prevent overriding our data.
            // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
            // Adding 31 to size and running the result through the logic above ensures
            // the memory pointer remains word-aligned, following the Solidity convention.
            mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))

            // Store the size of the data in the first 32 byte chunk of free memory.
            mstore(data, size)

            // Copy the code into memory right after the 32 bytes we used to store the size.
            extcodecopy(pointer, add(data, 32), start, size)
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IGlobals","name":"globals","type":"address"},{"internalType":"contract IOpenseaExchange","name":"seaport","type":"address"},{"internalType":"contract IOpenseaConduitController","name":"seaportConduitController","type":"address"},{"internalType":"contract IZoraAuctionHouse","name":"zoraAuctionHouse","type":"address"},{"internalType":"contract IFractionalV1VaultFactory","name":"fractionalVaultFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes","name":"revertData","type":"bytes"}],"name":"ArbitraryCallFailedError","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"CallProhibitedError","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"errData","type":"bytes"}],"name":"EthTransferFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"callDataLength","type":"uint256"}],"name":"InvalidApprovalCallLength","type":"error"},{"inputs":[],"name":"InvalidFeeRecipients","type":"error"},{"inputs":[],"name":"MalformedProposalDataError","type":"error"},{"inputs":[{"internalType":"uint256","name":"callValue","type":"uint256"},{"internalType":"uint256","name":"ethAvailable","type":"uint256"}],"name":"NotEnoughEthAttachedError","type":"error"},{"inputs":[],"name":"OnlyConstructorError","type":"error"},{"inputs":[],"name":"OnlyDelegateCallError","type":"error"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"OpenseaOrderStillActiveError","type":"error"},{"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"PreciousLostError","type":"error"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"},{"internalType":"uint256","name":"currentInProgressProposalId","type":"uint256"}],"name":"ProposalExecutionBlockedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"ProposalNotInProgressError","type":"error"},{"inputs":[{"internalType":"bytes32","name":"actualProgressDataHash","type":"bytes32"},{"internalType":"bytes32","name":"expectedProgressDataHash","type":"bytes32"}],"name":"ProposalProgressDataInvalidError","type":"error"},{"inputs":[{"internalType":"uint256","name":"u256","type":"uint256"}],"name":"Uint256ToUint40CastOutOfRangeError","type":"error"},{"inputs":[{"internalType":"uint256","name":"idx","type":"uint256"},{"internalType":"bytes32","name":"resultHash","type":"bytes32"},{"internalType":"bytes32","name":"expectedResultHash","type":"bytes32"}],"name":"UnexpectedCallResultHashError","type":"error"},{"inputs":[{"internalType":"contract IProposalExecutionEngine","name":"actualImpl","type":"address"},{"internalType":"contract IProposalExecutionEngine","name":"expectedImpl","type":"address"}],"name":"UnexpectedProposalEngineImplementationError","type":"error"},{"inputs":[{"internalType":"uint32","name":"proposalType","type":"uint32"}],"name":"UnsupportedProposalTypeError","type":"error"},{"inputs":[],"name":"ZeroProposalIdError","type":"error"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"uint40","name":"expiry","type":"uint40"}],"name":"ZoraListingNotExpired","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"idx","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"ArbitraryCallExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC721","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"vault","type":"address"}],"name":"FractionalV1VaultCreated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum IOpenseaExchange.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct IOpenseaExchange.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum IOpenseaExchange.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct IOpenseaExchange.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum IOpenseaExchange.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"indexed":false,"internalType":"struct IOpenseaExchange.OrderParameters","name":"orderParams","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"OpenseaAdvancedOrderListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endPrice","type":"uint256"}],"name":"OpenseaAdvancedOrderSold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"OpenseaOrderExpired","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum IOpenseaExchange.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct IOpenseaExchange.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum IOpenseaExchange.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct IOpenseaExchange.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum IOpenseaExchange.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"indexed":false,"internalType":"struct IOpenseaExchange.OrderParameters","name":"orderParams","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"contract IERC721","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"listPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"OpenseaOrderListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"contract IERC721","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"listPrice","type":"uint256"}],"name":"OpenseaOrderSold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldImpl","type":"address"},{"indexed":false,"internalType":"address","name":"newImpl","type":"address"}],"name":"ProposalEngineImplementationUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":false,"internalType":"contract IERC721","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingPrice","type":"uint256"},{"indexed":false,"internalType":"uint40","name":"duration","type":"uint40"},{"indexed":false,"internalType":"uint40","name":"timeoutTime","type":"uint40"}],"name":"ZoraAuctionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"ZoraAuctionExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ZoraAuctionFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ZoraAuctionSold","type":"event"},{"inputs":[],"name":"CONDUIT_CONTROLLER","outputs":[{"internalType":"contract IOpenseaConduitController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IMPL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SEAPORT","outputs":[{"internalType":"contract IOpenseaExchange","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_FACTORY","outputs":[{"internalType":"contract IFractionalV1VaultFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ZORA","outputs":[{"internalType":"contract IZoraAuctionHouse","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"cancelProposal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"proposalId","type":"uint256"},{"internalType":"bytes","name":"proposalData","type":"bytes"},{"internalType":"bytes","name":"progressData","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"contract IERC721[]","name":"preciousTokens","type":"address[]"},{"internalType":"uint256[]","name":"preciousTokenIds","type":"uint256[]"}],"internalType":"struct IProposalExecutionEngine.ExecuteProposalParams","name":"params","type":"tuple"}],"name":"executeProposal","outputs":[{"internalType":"bytes","name":"nextProgressData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCurrentInProgressProposalId","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oldImpl","type":"address"},{"internalType":"bytes","name":"initializeData","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101a06040523480156200001257600080fd5b50604051620046e0380380620046e0833981016040819052620000359162000091565b306080526001600160a01b0393841660a05291831660c05292821660e0819052928216610100819052610120849052911661014052610160526101805262000111565b6001600160a01b03811681146200008e57600080fd5b50565b600080600080600060a08688031215620000aa57600080fd5b8551620000b78162000078565b6020870151909550620000ca8162000078565b6040870151909450620000dd8162000078565b6060870151909350620000f08162000078565b6080870151909250620001038162000078565b809150509295509295909350565b60805160a05160c05160e05161010051610120516101405161016051610180516144c56200021b60003960006112ee01526000612ebb015260008181609d01528181610be401528181610c650152610df70152600081816107fc0152818161089301526109910152600081816101a6015281816119a901528181611a6501528181611ba00152611cb40152600081816114b00152818161154701528181611723015281816117ba015281816121ef015261248701526000818161015f01526122730152600081816101110152818161287c015281816129f40152612fb2015260008181610138015281816101fc015281816103db0152818161042901526113b801526144c56000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c80639b0f5c41116100665780639b0f5c411461015a578063a7c8a3f914610181578063c4e525bf146101a1578063d1f57894146101c8578063e0a8f6f5146101dd57600080fd5b8063103f2907146100985780631d117029146100dc578063387b66291461010c57806356973ee514610133575b600080fd5b6100bf7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b7f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e23546040519081526020016100d3565b6100bf7f000000000000000000000000000000000000000000000000000000000000000081565b6100bf7f000000000000000000000000000000000000000000000000000000000000000081565b6100bf7f000000000000000000000000000000000000000000000000000000000000000081565b61019461018f366004613400565b6101f0565b6040516100d39190613560565b6100bf7f000000000000000000000000000000000000000000000000000000000000000081565b6101db6101d6366004613573565b6103d1565b005b6101db6101eb3660046135f7565b61041f565b60606001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016300361023b5760405163ea2cbbd560e01b815260040160405180910390fd5b815160000361025d5760405163eaeba8a760e01b815260040160405180910390fd5b7f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e23547f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e229060008190036102b657835160018301556102e9565b835181146102e957835160405163432e608d60e11b81526004810191909152602481018290526044015b60405180910390fd5b8154600081900361034357811561030257610302613610565b6040850151511561033e578460400151805190602001208160405163070bdb0360e11b81526004016102e0929190918252602082015260400190565b61037c565b6040850151805160209091012081811461037a5760405163070bdb0360e11b815260048101829052602481018390526044016102e0565b505b5060001982556020840151600090610393906104ff565b602087015290506103a48186610581565b935083516000036103bf5760006001840181905583556103c9565b8351602085012083555b505050919050565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016300361041a5760405163ea2cbbd560e01b815260040160405180910390fd5b505050565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001630036104685760405163ea2cbbd560e01b815260040160405180910390fd5b806000036104895760405163eaeba8a760e01b815260040160405180910390fd5b7f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e23547f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e22908281146104f057604051633ae4fbbd60e11b8152600481018490526024016102e0565b50600060018201819055905550565b6000606060048351101561052657604051632fc4a01d60e21b815260040160405180910390fd5b5050600481018051825160031901825263ffffffff1690600082600681111561055157610551613626565b0361055b57600080fd5b6006828181111561056e5761056e613626565b60ff16111561057c57600080fd5b915091565b6060600183600681111561059757610597613626565b036105ac576105a582610699565b9050610693565b60068360068111156105c0576105c0613626565b036105ce576105a582610753565b60028360068111156105e2576105e2613626565b036105f0576105a58261077b565b600383600681111561060457610604613626565b03610612576105a582610b40565b600483600681111561062657610626613626565b03610634576105a582611019565b600583600681111561064857610648613626565b0361065f5761065a82602001516112b1565b610693565b82600681111561067157610671613626565b60405163efdc945f60e01b815263ffffffff90911660048201526024016102e0565b92915050565b6060600082602001518060200190518101906106b59190613738565b905061074c836040518061012001604052808460000151815260200184600001518152602001846020015164ffffffffff168152602001600060018111156106ff576106ff613626565b815260200184604001516001600160a01b0316815260200184606001518152602001846080015181526020018460a0015181526020018460c001516001600160e01b031916815250611413565b9392505050565b60606000826020015180602001905181019061076f9190613827565b905061074c8382611413565b606060008260200151806020019051810190610797919061391f565b905060008360400151516000146107c55783604001518060200190518101906107c091906139a2565b6107c8565b60005b905060008160018111156107de576107de613626565b03610acd57604051631106aeeb60e21b8152601060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063441abbac90602401602060405180830381865afa15801561084b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086f91906139bf565b604051631106aeeb60e21b8152601160048201529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063441abbac90602401602060405180830381865afa1580156108da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108fe91906139bf565b905064ffffffffff82161580159061092757508164ffffffffff16846040015164ffffffffff16105b1561093e5764ffffffffff82166040850152610978565b64ffffffffff81161580159061096557508064ffffffffff16846040015164ffffffffff16115b156109785764ffffffffff811660408501525b604051631106aeeb60e21b8152601260048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063441abbac90602401602060405180830381865afa1580156109e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a0491906139bf565b905064ffffffffff811615801590610a2d57508064ffffffffff16856020015164ffffffffff16115b15610a405764ffffffffff811660208601525b5050506000610a6683600001518460200151856040015186606001518760800151611992565b905060016040518060400160405280838152602001610a9a866020015164ffffffffff1642610a9591906139ee565b611b58565b64ffffffffff169052604051610ab4929190602001613a11565b6040516020818303038152906040529350505050919050565b6001816001811115610ae157610ae1613626565b14610aee57610aee613610565b60008460400151806020019051810190610b089190613a95565b915050610b278160000151826020015185606001518660800151611b87565b5050604080516020810190915260008152949350505050565b606060008260200151806020019051810190610b5c9190613acb565b90506000306001600160a01b031663b20240ee6040518163ffffffff1660e01b8152600401608060405180830381865afa158015610b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc29190613b22565b606001518251602084015160405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260248201929092526bffffffffffffffffffffffff9093169350169063095ea7b390604401600060405180830381600087803b158015610c4957600080fd5b505af1158015610c5d573d6000803e3d6000fd5b5050505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bdc01110306001600160a01b03166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa158015610cd0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cf89190810190613bea565b306001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d36573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d5e9190810190613bea565b865160208801516040516001600160e01b031960e087901b168152610d90949392919089906000908190600401613c32565b6020604051808303816000875af1158015610daf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd391906139bf565b604051634632752560e11b8152600481018290529091506000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638c64ea4a90602401602060405180830381865afa158015610e3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e629190613c8b565b6040516370a0823160e01b815230600482015290915083906001600160a01b038316906370a0823190602401602060405180830381865afa158015610eab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ecf91906139bf565b14610edc57610edc613610565b604051630c6a62dd60e01b8152600160048201526001600160a01b03821690630c6a62dd90602401600060405180830381600087803b158015610f1e57600080fd5b505af1158015610f32573d6000803e3d6000fd5b50505050836020015184600001516001600160a01b03167fd252747f4bdd4d75edc8c1d9cf1446e23083a5ea163955dfb64fe82a66fc3c998484604051610f8c9291909182526001600160a01b0316602082015260400190565b60405180910390a3604051637d1eeb0f60e11b8152309063fa3dd61e90610fbc9060019085908790600401613ca8565b60e0604051808303816000875af1158015610fdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fff9190613cf2565b505060408051602081019091526000815295945050505050565b6060600082602001518060200190518101906110359190613dc2565b9050600060018085608001511614905060008460c00151516001600160401b03811115611064576110646131cb565b60405190808252806020026020018201604052801561108d578160200160208202803683370190505b509050816111195760005b8151811015611117576110e58660a0015182815181106110ba576110ba613ed2565b60200260200101518760c0015183815181106110d8576110d8613ed2565b6020026020010151611e36565b8282815181106110f7576110f7613ed2565b9115156020928302919091019091015261111081613ee8565b9050611098565b505b3460005b84518110156111ba5761113c81868960a001518a60c001518887611e5d565b84818151811061114e5761114e613ed2565b602002602001015160200151826111659190613f01565b8751865160408051928352602083018590528201529092507fb7ff498e3c62b4b7e752fe0641134387db1cc5096d26b462a2c949bfb8484a9a9060600160405180910390a16111b381613ee8565b905061111d565b50826112a15760005b825181101561129f578281815181106111de576111de613ed2565b60200260200101511561128f576112228760a00151828151811061120457611204613ed2565b60200260200101518860c0015183815181106110d8576110d8613ed2565b61128f578660a00151818151811061123c5761123c613ed2565b60200260200101518760c00151828151811061125a5761125a613ed2565b6020908102919091010151604051634baa335b60e11b81526001600160a01b03909216600483015260248201526044016102e0565b61129881613ee8565b90506111c3565b505b8015610b2757610b273382611fcd565b600080828060200190518101906112c89190613f14565b604051635c9fcd8560e11b81526002600482015291935091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b93f9b0a90602401602060405180830381865afa158015611335573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113599190613c8b565b9050806001600160a01b0316836001600160a01b0316146113a057604051630987f3d960e11b81526001600160a01b038083166004830152841660248201526044016102e0565b6113aa8183612059565b604080516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081168252851660208201527f171f44298b88c4e0b1c126daf5a75eabcd04d9dd623209ed50e7c98622e067d6910160405180910390a150505050565b60606000600180856080015116149050600084604001515160001461144f57846040015180602001905181019061144a9190613f64565b611452565b60005b9050600081600381111561146857611468613626565b036116435781158015611492575061149284608001518560a001518760a001518860c00151612142565b1561163f57604051631106aeeb60e21b8152600660048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063441abbac90602401602060405180830381865afa1580156114ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061152391906139bf565b604051631106aeeb60e21b8152600760048201529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063441abbac90602401602060405180830381865afa15801561158e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b291906139bf565b905064ffffffffff82161561163c5760006115dc876000015184848a608001518b60a00151611992565b9050600160405180604001604052808381526020016116078664ffffffffff1642610a9591906139ee565b64ffffffffff169052604051611621929190602001613f95565b60405160208183030381529060405295505050505050610693565b50505b5060025b600181600381111561165757611657613626565b036116f157600085604001518060200190518101906116769190613fb3565b91505060006116978260000151836020015188608001518960a00151611b87565b905060008160028111156116ad576116ad613626565b14806116ca575060028160028111156116c8576116c8613626565b145b156116ea5760405180602001604052806000815250945050505050610693565b6002925050505b600281600381111561170557611705613626565b0361191d57604051631106aeeb60e21b8152601360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063441abbac90602401602060405180830381865afa158015611772573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179691906139bf565b604051631106aeeb60e21b8152601460048201529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063441abbac90602401602060405180830381865afa158015611801573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182591906139bf565b905064ffffffffff82161580159061184e57508164ffffffffff16866040015164ffffffffff16105b156118655764ffffffffff8216604087015261189f565b64ffffffffff81161580159061188c57508064ffffffffff16866040015164ffffffffff16115b1561189f5764ffffffffff811660408701525b50506000846040015164ffffffffff16426118ba91906139ee565b90506000806118d687608001518860a0015189606001516121cc565b9150915060006118e78883866123d3565b905060038184866040516020016119019493929190613fcf565b6040516020818303038152906040529650505050505050610693565b600381600381111561193157611931613626565b1461193e5761193e613610565b600085604001518060200190518101906119589190614001565b915050610b2781600001518260200151836040015164ffffffffff1688608001518960a001518a606001518b600001518c602001516129db565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390526000919084169063095ea7b390604401600060405180830381600087803b158015611a0057600080fd5b505af1158015611a14573d6000803e3d6000fd5b50506040516375e9249f60e01b8152600481018590526001600160a01b03868116602483015264ffffffffff88166044830152606482018a905260006084830181905260a4830181905260c48301527f00000000000000000000000000000000000000000000000000000000000000001692506375e9249f915060e4016020604051808303816000875af1158015611ab0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ad491906139bf565b90507f92160616bc72fa0e7854d06cb31ed32d6900a787a287808c4677802fb57a22548184848988611b0d64ffffffffff8c16426139ee565b604080519687526001600160a01b03909516602087015293850192909252606084015264ffffffffff90811660808401521660a082015260c00160405180910390a195945050505050565b600064ffffffffff821115611b8357604051633ce0460960e21b8152600481018390526024016102e0565b5090565b604051635cd16f1d60e11b8152600481018590526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b9a2de3a90602401600060405180830381600087803b158015611bec57600080fd5b505af1925050508015611bfd575060015b611d9a573d808015611c2b576040519150601f19603f3d011682016040523d82523d6000602084013e611c30565b606091505b50805160208201207fab5ac877486bd286449032bffed3a1798df77c605e9f81e0dbaaa711cab16a9b8101611d63574264ffffffffff168664ffffffffff161115611c9e57604051633d9251e560e11b81526004810188905264ffffffffff871660248201526044016102e0565b6040516396b5a75560e01b8152600481018890527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906396b5a75590602401600060405180830381600087803b158015611d0057600080fd5b505af1158015611d14573d6000803e3d6000fd5b5050604080518a815264ffffffffff8a1660208201527f2b5e0f760dfa65f0b373807db413cde4fb501792f679c6c9fb2f810f6fa1f4ad935001905060405180910390a1600192505050611e2e565b7f474ba0184a7cd5de777156a56f3859150719340a6974b6ee50f05c58139f4dc28114611d9357611d9382612cb2565b5050611df7565b30611dae6001600160a01b03851684612cba565b6001600160a01b031603611df7576040518581527f95df91b4115069deb8a25fad93b97ea21cdda12fa1842726adb91f847bbd0f099060200160405180910390a1506002611e2e565b6040518581527f5f773413a86a1bb5ab358fb19f7d47a4cc78ab91102af8103505f05362214c819060200160405180910390a15060005b949350505050565b600030611e4c6001600160a01b03851684612cba565b6001600160a01b0316149392505050565b6000858781518110611e7157611e71613ed2565b60200260200101519050611e8a81848989518989612da3565b611eb057805160408083015190516309a30f6560e01b81526102e092919060040161408e565b8060200151821015611ee557602081015160405163465352eb60e01b81526004810191909152602481018390526044016102e0565b60008082600001516001600160a01b031683602001518460400151604051611f0d91906140b2565b60006040518083038185875af1925050503d8060008114611f4a576040519150601f19603f3d011682016040523d82523d6000602084013e611f4f565b606091505b509150915081611f7457806040516308a16a4360e41b81526004016102e09190613560565b606083015115611fc2578051602082012060608401518114611fc05760608401516040516395fcd9af60e01b8152600481018c90526024810183905260448101919091526064016102e0565b505b505050505050505050565b80600003611fd9575050565b600080836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612027576040519150601f19603f3d011682016040523d82523d6000602084013e61202c565b606091505b50915091508161205357838160405163354db69760e01b81526004016102e092919061408e565b50505050565b7feb054550c406db3b89dc7016369a66aff0ce40188133281942b92e6188c6b1ef80546001600160a01b031981166001600160a01b03858116918217845560405192169160009182916120b2908590889060240161408e565b60408051601f198184030181529181526020820180516001600160e01b031663347d5e2560e21b179052516120e791906140b2565b600060405180830381855af49150503d8060008114612122576040519150601f19603f3d011682016040523d82523d6000602084013e612127565b606091505b50915091508161213a5761213a81612cb2565b505050505050565b6000805b83518110156121c05783818151811061216157612161613ed2565b60200260200101516001600160a01b0316866001600160a01b03161480156121a1575082818151811061219657612196613ed2565b602002602001015185145b156121b0576001915050611e2e565b6121b981613ee8565b9050612146565b50600095945050505050565b6040516324dfc9d760e01b8152600d600482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906324dfc9d790602401602060405180830381865afa158015612236573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225a91906139bf565b604051636e9bfd9f60e01b8152600481018290529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636e9bfd9f906024016040805180830381865afa1580156122c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e591906140de565b50915060008360018111156122fc576122fc613626565b036123685760405163095ea7b360e01b81526001600160a01b0383811660048301526024820186905286169063095ea7b390604401600060405180830381600087803b15801561234b57600080fd5b505af115801561235f573d6000803e3d6000fd5b505050506123cb565b60405163a22cb46560e01b81526001600160a01b0383811660048301526001602483015286169063a22cb46590604401600060405180830381600087803b1580156123b257600080fd5b505af11580156123c6573d6000803e3d6000fd5b505050505b935093915050565b60008360e00151518460c0015151146123ff57604051631b1cf91760e21b815260040160405180910390fd5b604080516001808252818301909252600091816020015b61241e61312e565b81526020019060019003908161241657905050905060008160008151811061244857612448613ed2565b602090810291909101015180513081524260a082015260c08101869052604051635c9fcd8560e11b8152600e6004820152919250906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b93f9b0a90602401602060405180830381865afa1580156124ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f29190613c8b565b6001600160a01b0316602082018190521561250e576002612511565b60005b8160800190600381111561252757612527613626565b9081600381111561253a5761253a613626565b905250610100808801516001600160e01b03191690820152610120810186905260c08701515161256b9060016139ee565b61014082015260408051600180825281830190925290816020015b6125c26040805160a08101909152806000815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b8152602001906001900390816125865750506040820181905280516000919082906125ef576125ef613ed2565b602002602001015190506000600181111561260c5761260c613626565b8860600151600181111561262257612622613626565b1461262e576003612631565b60025b8190600581111561264457612644613626565b9081600581111561265757612657613626565b9052506080808901516001600160a01b0316602083015260a0890151604083015260016060830181905290820181905260c089015151612696916139ee565b6001600160401b038111156126ad576126ad6131cb565b60405190808252806020026020018201604052801561270d57816020015b6040805160c08101825260008082526020808301829052928201819052606082018190526080820181905260a082015282526000199092019101816126cb5790505b5060608301819052805160009190829061272957612729613ed2565b6020908102919091018101516000808252818301819052604082018190528b516060830152918b015160808201523060a082015291505b8960c001515181101561285857606084015161277d8260016139ee565b8151811061278d5761278d613ed2565b6020908102919091010151915060008281905250600060208301819052604083015260c08a01518051829081106127c6576127c6613ed2565b602002602001015182606001818152505089600001518a602001518b60c0015183815181106127f7576127f7613ed2565b6020026020010151612809919061410a565b6128139190614121565b608083015260e08a015180518290811061282f5761282f613ed2565b60209081029190910101516001600160a01b031660a083015261285181613ee8565b9050612760565b5061286283612f83565b60405163440a3b9960e11b81529096506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906388147732906128b1908890600401614307565b6020604051808303816000875af11580156128d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f4919061438c565b61290057612900613610565b60008960600151600181111561291857612918613626565b148015612929575060208901518951145b1561297b57608089015160a08a01518a516040517f8bc0df75fcb1089ed0bc3c5aaeee9eb67fd03058afcce4f505c3876a170f6d639361296e9388938c938e906143a7565b60405180910390a16129cf565b7fa7a458fb56579e1739b7d7be315c8376100992f6cc2a7bed18e57de1ec54781083878b608001518c60a001518d600001518e602001518d6040516129c697969594939291906143ec565b60405180910390a15b50505050509392505050565b6040516346423aa760e01b8152600481018990526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906346423aa790602401608060405180830381865afa158015612a43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a679190614439565b509250505080600014612b43576000846001811115612a8857612a88613626565b148015612a9457508183145b15612aef57604080518a81526001600160a01b0388166020820152908101869052606081018490527f3e99b32c69260aa9463ba7c7f903dceed3e9cdc2709c00c858c887fa9bc5b9e5906080015b60405180910390a1611fc2565b604080518a81526001600160a01b038816602082015290810186905260608101849052608081018390527f496e331a08ff376957a7e057d214af9c47e131093374097b5fb5c005819b331c9060a001612ae2565b428711612c79576000846001811115612b5e57612b5e613626565b03612bc95760405163095ea7b360e01b815260006004820152602481018690526001600160a01b0387169063095ea7b390604401600060405180830381600087803b158015612bac57600080fd5b505af1158015612bc0573d6000803e3d6000fd5b50505050612c2c565b60405163a22cb46560e01b81526001600160a01b0389811660048301526000602483015287169063a22cb46590604401600060405180830381600087803b158015612c1357600080fd5b505af1158015612c27573d6000803e3d6000fd5b505050505b604080518a81526001600160a01b0388166020820152908101869052606081018890527f2045cfa9b677ebd0cb44d8ba225aaa166881e63d27a156115c76cafb3f0b395d90608001612ae2565b60405163079be7e360e41b8152600481018a90526001600160a01b038716602482015260448101869052606481018890526084016102e0565b805160208201fd5b6000806000846001600160a01b0316856001600160a01b0316636352211e86604051602401612ceb91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b939093179092529051612d2392506140b2565b600060405180830381855afa9150503d8060008114612d5e576040519150601f19603f3d011682016040523d82523d6000602084013e612d63565b606091505b5091509150811580612d76575060208151105b15612d8657600092505050610693565b80806020019051810190612d9a9190613c8b565b95945050505050565b6000306001600160a01b031687600001516001600160a01b031603612dca57506000612f79565b600487604001515110612f75576040870151602001516001600160e01b03191686612ef85763f6a1584d60e01b6001600160e01b0319821601612e4b57600080612e178a60400151613039565b90925090506001600160a01b03821615612e44578951612e3990828888612142565b159350505050612f79565b5050612ef8565b635dd34b9b60e01b6001600160e01b0319821601612e97576000612e728960400151613080565b9150508015612e91578851612e8790866130cb565b1592505050612f79565b50612ef8565b63694a58ab60e01b6001600160e01b0319821601612ef85787516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911603612ef85784612eef8760016139ee565b14915050612f79565b6001600160e01b03198116630a85bd0160e11b1480612f2757506001600160e01b0319811663f23a6e6160e01b145b80612f4257506001600160e01b0319811663bc197c8160e01b145b15612f51576000915050612f79565b633bf5c46760e11b6001600160e01b0319821601612f73576000915050612f79565b505b5060015b9695505050505050565b61014081018051600091829052612f9861314e565b506040516379df72bd60e01b815283906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906379df72bd90612fe790849060040161447c565b602060405180830381865afa158015613004573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061302891906139bf565b610140909401919091525090919050565b60008060448351101561306457825160405163252e3c3d60e01b81526004016102e091815260200190565b6001600160a01b03602484015116915060448301519050915091565b6000806044835110156130ab57825160405163252e3c3d60e01b81526004016102e091815260200190565b6001600160a01b0360248401511691506001604484015115189050915091565b6000805b8251811015613124578281815181106130ea576130ea613ed2565b60200260200101516001600160a01b0316846001600160a01b031603613114576001915050610693565b61311d81613ee8565b90506130cf565b5060009392505050565b604051806040016040528061314161314e565b8152602001606081525090565b60405180610160016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081526020016000600381111561319b5761319b613626565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613203576132036131cb565b60405290565b60405161012081016001600160401b0381118282101715613203576132036131cb565b604051608081016001600160401b0381118282101715613203576132036131cb565b604051601f8201601f191681016001600160401b0381118282101715613276576132766131cb565b604052919050565b60006001600160401b03821115613297576132976131cb565b50601f01601f191660200190565b600082601f8301126132b657600080fd5b81356132c96132c48261327e565b61324e565b8181528460208386010111156132de57600080fd5b816020850160208301376000918101602001919091529392505050565b60006001600160401b03821115613314576133146131cb565b5060051b60200190565b6001600160a01b038116811461333357600080fd5b50565b600082601f83011261334757600080fd5b813560206133576132c4836132fb565b82815260059290921b8401810191818101908684111561337657600080fd5b8286015b8481101561339a57803561338d8161331e565b835291830191830161337a565b509695505050505050565b600082601f8301126133b657600080fd5b813560206133c66132c4836132fb565b82815260059290921b840181019181810190868411156133e557600080fd5b8286015b8481101561339a57803583529183019183016133e9565b60006020828403121561341257600080fd5b81356001600160401b038082111561342957600080fd5b9083019060e0828603121561343d57600080fd5b6134456131e1565b8235815260208301358281111561345b57600080fd5b613467878286016132a5565b60208301525060408301358281111561347f57600080fd5b61348b878286016132a5565b6040830152506060830135828111156134a357600080fd5b6134af878286016132a5565b6060830152506080830135608082015260a0830135828111156134d157600080fd5b6134dd87828601613336565b60a08301525060c0830135828111156134f557600080fd5b613501878286016133a5565b60c08301525095945050505050565b60005b8381101561352b578181015183820152602001613513565b50506000910152565b6000815180845261354c816020860160208601613510565b601f01601f19169290920160200192915050565b60208152600061074c6020830184613534565b60008060006040848603121561358857600080fd5b83356135938161331e565b925060208401356001600160401b03808211156135af57600080fd5b818601915086601f8301126135c357600080fd5b8135818111156135d257600080fd5b8760208285010111156135e457600080fd5b6020830194508093505050509250925092565b60006020828403121561360957600080fd5b5035919050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b805164ffffffffff8116811461365157600080fd5b919050565b80516136518161331e565b600082601f83011261367257600080fd5b815160206136826132c4836132fb565b82815260059290921b840181019181810190868411156136a157600080fd5b8286015b8481101561339a57805183529183019183016136a5565b600082601f8301126136cd57600080fd5b815160206136dd6132c4836132fb565b82815260059290921b840181019181810190868411156136fc57600080fd5b8286015b8481101561339a5780516137138161331e565b8352918301918301613700565b80516001600160e01b03198116811461365157600080fd5b60006020828403121561374a57600080fd5b81516001600160401b038082111561376157600080fd5b9083019060e0828603121561377557600080fd5b61377d6131e1565b8251815261378d6020840161363c565b602082015261379e60408401613656565b6040820152606083015160608201526080830151828111156137bf57600080fd5b6137cb87828601613661565b60808301525060a0830151828111156137e357600080fd5b6137ef878286016136bc565b60a08301525061380160c08401613720565b60c082015295945050505050565b6002811061333357600080fd5b80516136518161380f565b60006020828403121561383957600080fd5b81516001600160401b038082111561385057600080fd5b90830190610120828603121561386557600080fd5b61386d613209565b82518152602083015160208201526138876040840161363c565b60408201526138986060840161381c565b60608201526138a960808401613656565b608082015260a083015160a082015260c0830151828111156138ca57600080fd5b6138d687828601613661565b60c08301525060e0830151828111156138ee57600080fd5b6138fa878286016136bc565b60e0830152506101009150613910828401613720565b91810191909152949350505050565b600060a0828403121561393157600080fd5b60405160a081018181106001600160401b0382111715613953576139536131cb565b604052825181526139666020840161363c565b60208201526139776040840161363c565b6040820152606083015161398a8161331e565b60608201526080928301519281019290925250919050565b6000602082840312156139b457600080fd5b815161074c8161380f565b6000602082840312156139d157600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610693576106936139d8565b6002811061333357613333613626565b60608101613a1e84613a01565b83825261074c60208301848051825260209081015164ffffffffff16910152565b600060408284031215613a5157600080fd5b604051604081018181106001600160401b0382111715613a7357613a736131cb565b60405282518152905080613a896020840161363c565b60208201525092915050565b60008060608385031215613aa857600080fd5b8251613ab38161380f565b9150613ac28460208501613a3f565b90509250929050565b600060408284031215613add57600080fd5b604051604081018181106001600160401b0382111715613aff57613aff6131cb565b6040528251613b0d8161331e565b81526020928301519281019290925250919050565b600060808284031215613b3457600080fd5b604051608081018181106001600160401b0382111715613b5657613b566131cb565b604052613b628361363c565b8152613b706020840161363c565b6020820152604083015161ffff81168114613b8a57600080fd5b604082015260608301516bffffffffffffffffffffffff81168114613bae57600080fd5b60608201529392505050565b6000613bc86132c48461327e565b9050828152838383011115613bdc57600080fd5b61074c836020830184613510565b600060208284031215613bfc57600080fd5b81516001600160401b03811115613c1257600080fd5b8201601f81018413613c2357600080fd5b611e2e84825160208401613bba565b60e081526000613c4560e083018a613534565b8281036020840152613c57818a613534565b6001600160a01b0398909816604084015250506060810194909452608084019290925260a083015260c09091015292915050565b600060208284031215613c9d57600080fd5b815161074c8161331e565b60608101613cb585613a01565b9381526001600160a01b0392909216602083015260409091015290565b80516fffffffffffffffffffffffffffffffff8116811461365157600080fd5b600060e08284031215613d0457600080fd5b60405160e081018181106001600160401b0382111715613d2657613d266131cb565b6040528251613d348161380f565b8152602083810151908201526040830151613d4e8161331e565b60408201526060830151613d618161331e565b60608201526080830151613d748161331e565b6080820152613d8560a08401613cd2565b60a0820152613d9660c08401613cd2565b60c08201529392505050565b600082601f830112613db357600080fd5b61074c83835160208501613bba565b60006020808385031215613dd557600080fd5b82516001600160401b0380821115613dec57600080fd5b818501915085601f830112613e0057600080fd5b8151613e0e6132c4826132fb565b81815260059190911b83018401908481019088831115613e2d57600080fd5b8585015b83811015613ec557805185811115613e4857600080fd5b86016080818c03601f19011215613e5f5760008081fd5b613e6761322c565b88820151613e748161331e565b81526040828101518a83015260608084015189811115613e945760008081fd5b613ea28f8d83880101613da2565b928401929092526080939093015192820192909252845250918601918601613e31565b5098975050505050505050565b634e487b7160e01b600052603260045260246000fd5b600060018201613efa57613efa6139d8565b5060010190565b81810381811115610693576106936139d8565b60008060408385031215613f2757600080fd5b8251613f328161331e565b60208401519092506001600160401b03811115613f4e57600080fd5b613f5a85828601613da2565b9150509250929050565b600060208284031215613f7657600080fd5b81516004811061074c57600080fd5b6004811061333357613333613626565b60608101613a1e84613f85565b805160ff8116811461365157600080fd5b60008060608385031215613fc657600080fd5b613ab383613fa2565b60808101613fdc86613f85565b94815260208101939093526001600160a01b0391909116604083015260609091015290565b600080828403608081121561401557600080fd5b61401e84613fa2565b92506060601f198201121561403257600080fd5b50604051606081018181106001600160401b0382111715614055576140556131cb565b60409081526020850151825284015161406d8161331e565b602082015261407e6060850161363c565b6040820152809150509250929050565b6001600160a01b0383168152604060208201819052600090611e2e90830184613534565b600082516140c4818460208701613510565b9190910192915050565b8051801515811461365157600080fd5b600080604083850312156140f157600080fd5b82516140fc8161331e565b9150613ac2602084016140ce565b8082028115828204841417610693576106936139d8565b60008261413e57634e487b7160e01b600052601260045260246000fd5b500490565b6006811061415357614153613626565b9052565b600081518084526020808501945080840160005b838110156141c1578151614180888251614143565b838101516001600160a01b03168885015260408082015190890152606080820151908901526080908101519088015260a0909601959082019060010161416b565b509495945050505050565b600081518084526020808501945080840160005b838110156141c15781516141f5888251614143565b808401516001600160a01b0390811689860152604080830151908a0152606080830151908a0152608080830151908a015260a091820151169088015260c090960195908201906001016141e0565b61415381613f85565b80516001600160a01b031682526000610160602083015161427860208601826001600160a01b03169052565b50604083015181604086015261429082860182614157565b915050606083015184820360608601526142aa82826141cc565b91505060808301516142bf6080860182614243565b5060a0838101519085015260c0808401519085015260e08084015190850152610100808401519085015261012080840151908501526101409283015192909301919091525090565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561437e57888303603f19018552815180518785526143528886018261424c565b91890151858303868b015291905061436a8183613534565b96890196945050509086019060010161432e565b509098975050505050505050565b60006020828403121561439e57600080fd5b61074c826140ce565b60c0815260006143ba60c083018961424c565b6020830197909752506001600160a01b039490941660408501526060840192909252608083015260a090910152919050565b60e0815260006143ff60e083018a61424c565b6020830198909852506001600160a01b039590951660408601526060850193909352608084019190915260a083015260c090910152919050565b6000806000806080858703121561444f57600080fd5b614458856140ce565b9350614466602086016140ce565b6040860151606090960151949790965092505050565b60208152600061074c602083018461424c56fea2646970667358221220b8dd4cd5ba3f14e7fa395b99a1d5709ab631e49e032eae33fc17d59bfa3d4e1464736f6c634300081100330000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade00000000000000000000000000000000000001ad428e4906ae43d8f9852d0dd600000000000000000000000000000000f9490004c11cef243f5400493c00ad63000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad67300000000000000000000000085aa7f78bdb2de8f3e0c0010d99ad5853ffcfc63

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100935760003560e01c80639b0f5c41116100665780639b0f5c411461015a578063a7c8a3f914610181578063c4e525bf146101a1578063d1f57894146101c8578063e0a8f6f5146101dd57600080fd5b8063103f2907146100985780631d117029146100dc578063387b66291461010c57806356973ee514610133575b600080fd5b6100bf7f00000000000000000000000085aa7f78bdb2de8f3e0c0010d99ad5853ffcfc6381565b6040516001600160a01b0390911681526020015b60405180910390f35b7f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e23546040519081526020016100d3565b6100bf7f00000000000000000000000000000000000001ad428e4906ae43d8f9852d0dd681565b6100bf7f000000000000000000000000731db043762729ea2dae790a1c4a6ad78b86d67c81565b6100bf7f00000000000000000000000000000000f9490004c11cef243f5400493c00ad6381565b61019461018f366004613400565b6101f0565b6040516100d39190613560565b6100bf7f000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad67381565b6101db6101d6366004613573565b6103d1565b005b6101db6101eb3660046135f7565b61041f565b60606001600160a01b037f000000000000000000000000731db043762729ea2dae790a1c4a6ad78b86d67c16300361023b5760405163ea2cbbd560e01b815260040160405180910390fd5b815160000361025d5760405163eaeba8a760e01b815260040160405180910390fd5b7f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e23547f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e229060008190036102b657835160018301556102e9565b835181146102e957835160405163432e608d60e11b81526004810191909152602481018290526044015b60405180910390fd5b8154600081900361034357811561030257610302613610565b6040850151511561033e578460400151805190602001208160405163070bdb0360e11b81526004016102e0929190918252602082015260400190565b61037c565b6040850151805160209091012081811461037a5760405163070bdb0360e11b815260048101829052602481018390526044016102e0565b505b5060001982556020840151600090610393906104ff565b602087015290506103a48186610581565b935083516000036103bf5760006001840181905583556103c9565b8351602085012083555b505050919050565b6001600160a01b037f000000000000000000000000731db043762729ea2dae790a1c4a6ad78b86d67c16300361041a5760405163ea2cbbd560e01b815260040160405180910390fd5b505050565b6001600160a01b037f000000000000000000000000731db043762729ea2dae790a1c4a6ad78b86d67c1630036104685760405163ea2cbbd560e01b815260040160405180910390fd5b806000036104895760405163eaeba8a760e01b815260040160405180910390fd5b7f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e23547f33a3c164d6872de9f70b5a8072fe8ec5d3a6af6642df9b59e4bb0c4c5ce80e22908281146104f057604051633ae4fbbd60e11b8152600481018490526024016102e0565b50600060018201819055905550565b6000606060048351101561052657604051632fc4a01d60e21b815260040160405180910390fd5b5050600481018051825160031901825263ffffffff1690600082600681111561055157610551613626565b0361055b57600080fd5b6006828181111561056e5761056e613626565b60ff16111561057c57600080fd5b915091565b6060600183600681111561059757610597613626565b036105ac576105a582610699565b9050610693565b60068360068111156105c0576105c0613626565b036105ce576105a582610753565b60028360068111156105e2576105e2613626565b036105f0576105a58261077b565b600383600681111561060457610604613626565b03610612576105a582610b40565b600483600681111561062657610626613626565b03610634576105a582611019565b600583600681111561064857610648613626565b0361065f5761065a82602001516112b1565b610693565b82600681111561067157610671613626565b60405163efdc945f60e01b815263ffffffff90911660048201526024016102e0565b92915050565b6060600082602001518060200190518101906106b59190613738565b905061074c836040518061012001604052808460000151815260200184600001518152602001846020015164ffffffffff168152602001600060018111156106ff576106ff613626565b815260200184604001516001600160a01b0316815260200184606001518152602001846080015181526020018460a0015181526020018460c001516001600160e01b031916815250611413565b9392505050565b60606000826020015180602001905181019061076f9190613827565b905061074c8382611413565b606060008260200151806020019051810190610797919061391f565b905060008360400151516000146107c55783604001518060200190518101906107c091906139a2565b6107c8565b60005b905060008160018111156107de576107de613626565b03610acd57604051631106aeeb60e21b8152601060048201526000907f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade6001600160a01b03169063441abbac90602401602060405180830381865afa15801561084b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086f91906139bf565b604051631106aeeb60e21b8152601160048201529091506000906001600160a01b037f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade169063441abbac90602401602060405180830381865afa1580156108da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108fe91906139bf565b905064ffffffffff82161580159061092757508164ffffffffff16846040015164ffffffffff16105b1561093e5764ffffffffff82166040850152610978565b64ffffffffff81161580159061096557508064ffffffffff16846040015164ffffffffff16115b156109785764ffffffffff811660408501525b604051631106aeeb60e21b8152601260048201526000907f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade6001600160a01b03169063441abbac90602401602060405180830381865afa1580156109e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a0491906139bf565b905064ffffffffff811615801590610a2d57508064ffffffffff16856020015164ffffffffff16115b15610a405764ffffffffff811660208601525b5050506000610a6683600001518460200151856040015186606001518760800151611992565b905060016040518060400160405280838152602001610a9a866020015164ffffffffff1642610a9591906139ee565b611b58565b64ffffffffff169052604051610ab4929190602001613a11565b6040516020818303038152906040529350505050919050565b6001816001811115610ae157610ae1613626565b14610aee57610aee613610565b60008460400151806020019051810190610b089190613a95565b915050610b278160000151826020015185606001518660800151611b87565b5050604080516020810190915260008152949350505050565b606060008260200151806020019051810190610b5c9190613acb565b90506000306001600160a01b031663b20240ee6040518163ffffffff1660e01b8152600401608060405180830381865afa158015610b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc29190613b22565b606001518251602084015160405163095ea7b360e01b81526001600160a01b037f00000000000000000000000085aa7f78bdb2de8f3e0c0010d99ad5853ffcfc638116600483015260248201929092526bffffffffffffffffffffffff9093169350169063095ea7b390604401600060405180830381600087803b158015610c4957600080fd5b505af1158015610c5d573d6000803e3d6000fd5b5050505060007f00000000000000000000000085aa7f78bdb2de8f3e0c0010d99ad5853ffcfc636001600160a01b031663bdc01110306001600160a01b03166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa158015610cd0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cf89190810190613bea565b306001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d36573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d5e9190810190613bea565b865160208801516040516001600160e01b031960e087901b168152610d90949392919089906000908190600401613c32565b6020604051808303816000875af1158015610daf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd391906139bf565b604051634632752560e11b8152600481018290529091506000906001600160a01b037f00000000000000000000000085aa7f78bdb2de8f3e0c0010d99ad5853ffcfc631690638c64ea4a90602401602060405180830381865afa158015610e3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e629190613c8b565b6040516370a0823160e01b815230600482015290915083906001600160a01b038316906370a0823190602401602060405180830381865afa158015610eab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ecf91906139bf565b14610edc57610edc613610565b604051630c6a62dd60e01b8152600160048201526001600160a01b03821690630c6a62dd90602401600060405180830381600087803b158015610f1e57600080fd5b505af1158015610f32573d6000803e3d6000fd5b50505050836020015184600001516001600160a01b03167fd252747f4bdd4d75edc8c1d9cf1446e23083a5ea163955dfb64fe82a66fc3c998484604051610f8c9291909182526001600160a01b0316602082015260400190565b60405180910390a3604051637d1eeb0f60e11b8152309063fa3dd61e90610fbc9060019085908790600401613ca8565b60e0604051808303816000875af1158015610fdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fff9190613cf2565b505060408051602081019091526000815295945050505050565b6060600082602001518060200190518101906110359190613dc2565b9050600060018085608001511614905060008460c00151516001600160401b03811115611064576110646131cb565b60405190808252806020026020018201604052801561108d578160200160208202803683370190505b509050816111195760005b8151811015611117576110e58660a0015182815181106110ba576110ba613ed2565b60200260200101518760c0015183815181106110d8576110d8613ed2565b6020026020010151611e36565b8282815181106110f7576110f7613ed2565b9115156020928302919091019091015261111081613ee8565b9050611098565b505b3460005b84518110156111ba5761113c81868960a001518a60c001518887611e5d565b84818151811061114e5761114e613ed2565b602002602001015160200151826111659190613f01565b8751865160408051928352602083018590528201529092507fb7ff498e3c62b4b7e752fe0641134387db1cc5096d26b462a2c949bfb8484a9a9060600160405180910390a16111b381613ee8565b905061111d565b50826112a15760005b825181101561129f578281815181106111de576111de613ed2565b60200260200101511561128f576112228760a00151828151811061120457611204613ed2565b60200260200101518860c0015183815181106110d8576110d8613ed2565b61128f578660a00151818151811061123c5761123c613ed2565b60200260200101518760c00151828151811061125a5761125a613ed2565b6020908102919091010151604051634baa335b60e11b81526001600160a01b03909216600483015260248201526044016102e0565b61129881613ee8565b90506111c3565b505b8015610b2757610b273382611fcd565b600080828060200190518101906112c89190613f14565b604051635c9fcd8560e11b81526002600482015291935091506000906001600160a01b037f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade169063b93f9b0a90602401602060405180830381865afa158015611335573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113599190613c8b565b9050806001600160a01b0316836001600160a01b0316146113a057604051630987f3d960e11b81526001600160a01b038083166004830152841660248201526044016102e0565b6113aa8183612059565b604080516001600160a01b037f000000000000000000000000731db043762729ea2dae790a1c4a6ad78b86d67c81168252851660208201527f171f44298b88c4e0b1c126daf5a75eabcd04d9dd623209ed50e7c98622e067d6910160405180910390a150505050565b60606000600180856080015116149050600084604001515160001461144f57846040015180602001905181019061144a9190613f64565b611452565b60005b9050600081600381111561146857611468613626565b036116435781158015611492575061149284608001518560a001518760a001518860c00151612142565b1561163f57604051631106aeeb60e21b8152600660048201526000907f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade6001600160a01b03169063441abbac90602401602060405180830381865afa1580156114ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061152391906139bf565b604051631106aeeb60e21b8152600760048201529091506000906001600160a01b037f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade169063441abbac90602401602060405180830381865afa15801561158e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b291906139bf565b905064ffffffffff82161561163c5760006115dc876000015184848a608001518b60a00151611992565b9050600160405180604001604052808381526020016116078664ffffffffff1642610a9591906139ee565b64ffffffffff169052604051611621929190602001613f95565b60405160208183030381529060405295505050505050610693565b50505b5060025b600181600381111561165757611657613626565b036116f157600085604001518060200190518101906116769190613fb3565b91505060006116978260000151836020015188608001518960a00151611b87565b905060008160028111156116ad576116ad613626565b14806116ca575060028160028111156116c8576116c8613626565b145b156116ea5760405180602001604052806000815250945050505050610693565b6002925050505b600281600381111561170557611705613626565b0361191d57604051631106aeeb60e21b8152601360048201526000907f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade6001600160a01b03169063441abbac90602401602060405180830381865afa158015611772573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179691906139bf565b604051631106aeeb60e21b8152601460048201529091506000906001600160a01b037f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade169063441abbac90602401602060405180830381865afa158015611801573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182591906139bf565b905064ffffffffff82161580159061184e57508164ffffffffff16866040015164ffffffffff16105b156118655764ffffffffff8216604087015261189f565b64ffffffffff81161580159061188c57508064ffffffffff16866040015164ffffffffff16115b1561189f5764ffffffffff811660408701525b50506000846040015164ffffffffff16426118ba91906139ee565b90506000806118d687608001518860a0015189606001516121cc565b9150915060006118e78883866123d3565b905060038184866040516020016119019493929190613fcf565b6040516020818303038152906040529650505050505050610693565b600381600381111561193157611931613626565b1461193e5761193e613610565b600085604001518060200190518101906119589190614001565b915050610b2781600001518260200151836040015164ffffffffff1688608001518960a001518a606001518b600001518c602001516129db565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad67381166004830152602482018390526000919084169063095ea7b390604401600060405180830381600087803b158015611a0057600080fd5b505af1158015611a14573d6000803e3d6000fd5b50506040516375e9249f60e01b8152600481018590526001600160a01b03868116602483015264ffffffffff88166044830152606482018a905260006084830181905260a4830181905260c48301527f000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad6731692506375e9249f915060e4016020604051808303816000875af1158015611ab0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ad491906139bf565b90507f92160616bc72fa0e7854d06cb31ed32d6900a787a287808c4677802fb57a22548184848988611b0d64ffffffffff8c16426139ee565b604080519687526001600160a01b03909516602087015293850192909252606084015264ffffffffff90811660808401521660a082015260c00160405180910390a195945050505050565b600064ffffffffff821115611b8357604051633ce0460960e21b8152600481018390526024016102e0565b5090565b604051635cd16f1d60e11b8152600481018590526000907f000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad6736001600160a01b03169063b9a2de3a90602401600060405180830381600087803b158015611bec57600080fd5b505af1925050508015611bfd575060015b611d9a573d808015611c2b576040519150601f19603f3d011682016040523d82523d6000602084013e611c30565b606091505b50805160208201207fab5ac877486bd286449032bffed3a1798df77c605e9f81e0dbaaa711cab16a9b8101611d63574264ffffffffff168664ffffffffff161115611c9e57604051633d9251e560e11b81526004810188905264ffffffffff871660248201526044016102e0565b6040516396b5a75560e01b8152600481018890527f000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad6736001600160a01b0316906396b5a75590602401600060405180830381600087803b158015611d0057600080fd5b505af1158015611d14573d6000803e3d6000fd5b5050604080518a815264ffffffffff8a1660208201527f2b5e0f760dfa65f0b373807db413cde4fb501792f679c6c9fb2f810f6fa1f4ad935001905060405180910390a1600192505050611e2e565b7f474ba0184a7cd5de777156a56f3859150719340a6974b6ee50f05c58139f4dc28114611d9357611d9382612cb2565b5050611df7565b30611dae6001600160a01b03851684612cba565b6001600160a01b031603611df7576040518581527f95df91b4115069deb8a25fad93b97ea21cdda12fa1842726adb91f847bbd0f099060200160405180910390a1506002611e2e565b6040518581527f5f773413a86a1bb5ab358fb19f7d47a4cc78ab91102af8103505f05362214c819060200160405180910390a15060005b949350505050565b600030611e4c6001600160a01b03851684612cba565b6001600160a01b0316149392505050565b6000858781518110611e7157611e71613ed2565b60200260200101519050611e8a81848989518989612da3565b611eb057805160408083015190516309a30f6560e01b81526102e092919060040161408e565b8060200151821015611ee557602081015160405163465352eb60e01b81526004810191909152602481018390526044016102e0565b60008082600001516001600160a01b031683602001518460400151604051611f0d91906140b2565b60006040518083038185875af1925050503d8060008114611f4a576040519150601f19603f3d011682016040523d82523d6000602084013e611f4f565b606091505b509150915081611f7457806040516308a16a4360e41b81526004016102e09190613560565b606083015115611fc2578051602082012060608401518114611fc05760608401516040516395fcd9af60e01b8152600481018c90526024810183905260448101919091526064016102e0565b505b505050505050505050565b80600003611fd9575050565b600080836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612027576040519150601f19603f3d011682016040523d82523d6000602084013e61202c565b606091505b50915091508161205357838160405163354db69760e01b81526004016102e092919061408e565b50505050565b7feb054550c406db3b89dc7016369a66aff0ce40188133281942b92e6188c6b1ef80546001600160a01b031981166001600160a01b03858116918217845560405192169160009182916120b2908590889060240161408e565b60408051601f198184030181529181526020820180516001600160e01b031663347d5e2560e21b179052516120e791906140b2565b600060405180830381855af49150503d8060008114612122576040519150601f19603f3d011682016040523d82523d6000602084013e612127565b606091505b50915091508161213a5761213a81612cb2565b505050505050565b6000805b83518110156121c05783818151811061216157612161613ed2565b60200260200101516001600160a01b0316866001600160a01b03161480156121a1575082818151811061219657612196613ed2565b602002602001015185145b156121b0576001915050611e2e565b6121b981613ee8565b9050612146565b50600095945050505050565b6040516324dfc9d760e01b8152600d600482015260009081906001600160a01b037f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade16906324dfc9d790602401602060405180830381865afa158015612236573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225a91906139bf565b604051636e9bfd9f60e01b8152600481018290529091507f00000000000000000000000000000000f9490004c11cef243f5400493c00ad636001600160a01b031690636e9bfd9f906024016040805180830381865afa1580156122c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e591906140de565b50915060008360018111156122fc576122fc613626565b036123685760405163095ea7b360e01b81526001600160a01b0383811660048301526024820186905286169063095ea7b390604401600060405180830381600087803b15801561234b57600080fd5b505af115801561235f573d6000803e3d6000fd5b505050506123cb565b60405163a22cb46560e01b81526001600160a01b0383811660048301526001602483015286169063a22cb46590604401600060405180830381600087803b1580156123b257600080fd5b505af11580156123c6573d6000803e3d6000fd5b505050505b935093915050565b60008360e00151518460c0015151146123ff57604051631b1cf91760e21b815260040160405180910390fd5b604080516001808252818301909252600091816020015b61241e61312e565b81526020019060019003908161241657905050905060008160008151811061244857612448613ed2565b602090810291909101015180513081524260a082015260c08101869052604051635c9fcd8560e11b8152600e6004820152919250906001600160a01b037f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade169063b93f9b0a90602401602060405180830381865afa1580156124ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f29190613c8b565b6001600160a01b0316602082018190521561250e576002612511565b60005b8160800190600381111561252757612527613626565b9081600381111561253a5761253a613626565b905250610100808801516001600160e01b03191690820152610120810186905260c08701515161256b9060016139ee565b61014082015260408051600180825281830190925290816020015b6125c26040805160a08101909152806000815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b8152602001906001900390816125865750506040820181905280516000919082906125ef576125ef613ed2565b602002602001015190506000600181111561260c5761260c613626565b8860600151600181111561262257612622613626565b1461262e576003612631565b60025b8190600581111561264457612644613626565b9081600581111561265757612657613626565b9052506080808901516001600160a01b0316602083015260a0890151604083015260016060830181905290820181905260c089015151612696916139ee565b6001600160401b038111156126ad576126ad6131cb565b60405190808252806020026020018201604052801561270d57816020015b6040805160c08101825260008082526020808301829052928201819052606082018190526080820181905260a082015282526000199092019101816126cb5790505b5060608301819052805160009190829061272957612729613ed2565b6020908102919091018101516000808252818301819052604082018190528b516060830152918b015160808201523060a082015291505b8960c001515181101561285857606084015161277d8260016139ee565b8151811061278d5761278d613ed2565b6020908102919091010151915060008281905250600060208301819052604083015260c08a01518051829081106127c6576127c6613ed2565b602002602001015182606001818152505089600001518a602001518b60c0015183815181106127f7576127f7613ed2565b6020026020010151612809919061410a565b6128139190614121565b608083015260e08a015180518290811061282f5761282f613ed2565b60209081029190910101516001600160a01b031660a083015261285181613ee8565b9050612760565b5061286283612f83565b60405163440a3b9960e11b81529096506001600160a01b037f00000000000000000000000000000000000001ad428e4906ae43d8f9852d0dd616906388147732906128b1908890600401614307565b6020604051808303816000875af11580156128d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f4919061438c565b61290057612900613610565b60008960600151600181111561291857612918613626565b148015612929575060208901518951145b1561297b57608089015160a08a01518a516040517f8bc0df75fcb1089ed0bc3c5aaeee9eb67fd03058afcce4f505c3876a170f6d639361296e9388938c938e906143a7565b60405180910390a16129cf565b7fa7a458fb56579e1739b7d7be315c8376100992f6cc2a7bed18e57de1ec54781083878b608001518c60a001518d600001518e602001518d6040516129c697969594939291906143ec565b60405180910390a15b50505050509392505050565b6040516346423aa760e01b8152600481018990526000907f00000000000000000000000000000000000001ad428e4906ae43d8f9852d0dd66001600160a01b0316906346423aa790602401608060405180830381865afa158015612a43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a679190614439565b509250505080600014612b43576000846001811115612a8857612a88613626565b148015612a9457508183145b15612aef57604080518a81526001600160a01b0388166020820152908101869052606081018490527f3e99b32c69260aa9463ba7c7f903dceed3e9cdc2709c00c858c887fa9bc5b9e5906080015b60405180910390a1611fc2565b604080518a81526001600160a01b038816602082015290810186905260608101849052608081018390527f496e331a08ff376957a7e057d214af9c47e131093374097b5fb5c005819b331c9060a001612ae2565b428711612c79576000846001811115612b5e57612b5e613626565b03612bc95760405163095ea7b360e01b815260006004820152602481018690526001600160a01b0387169063095ea7b390604401600060405180830381600087803b158015612bac57600080fd5b505af1158015612bc0573d6000803e3d6000fd5b50505050612c2c565b60405163a22cb46560e01b81526001600160a01b0389811660048301526000602483015287169063a22cb46590604401600060405180830381600087803b158015612c1357600080fd5b505af1158015612c27573d6000803e3d6000fd5b505050505b604080518a81526001600160a01b0388166020820152908101869052606081018890527f2045cfa9b677ebd0cb44d8ba225aaa166881e63d27a156115c76cafb3f0b395d90608001612ae2565b60405163079be7e360e41b8152600481018a90526001600160a01b038716602482015260448101869052606481018890526084016102e0565b805160208201fd5b6000806000846001600160a01b0316856001600160a01b0316636352211e86604051602401612ceb91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b939093179092529051612d2392506140b2565b600060405180830381855afa9150503d8060008114612d5e576040519150601f19603f3d011682016040523d82523d6000602084013e612d63565b606091505b5091509150811580612d76575060208151105b15612d8657600092505050610693565b80806020019051810190612d9a9190613c8b565b95945050505050565b6000306001600160a01b031687600001516001600160a01b031603612dca57506000612f79565b600487604001515110612f75576040870151602001516001600160e01b03191686612ef85763f6a1584d60e01b6001600160e01b0319821601612e4b57600080612e178a60400151613039565b90925090506001600160a01b03821615612e44578951612e3990828888612142565b159350505050612f79565b5050612ef8565b635dd34b9b60e01b6001600160e01b0319821601612e97576000612e728960400151613080565b9150508015612e91578851612e8790866130cb565b1592505050612f79565b50612ef8565b63694a58ab60e01b6001600160e01b0319821601612ef85787516001600160a01b037f000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad6738116911603612ef85784612eef8760016139ee565b14915050612f79565b6001600160e01b03198116630a85bd0160e11b1480612f2757506001600160e01b0319811663f23a6e6160e01b145b80612f4257506001600160e01b0319811663bc197c8160e01b145b15612f51576000915050612f79565b633bf5c46760e11b6001600160e01b0319821601612f73576000915050612f79565b505b5060015b9695505050505050565b61014081018051600091829052612f9861314e565b506040516379df72bd60e01b815283906001600160a01b037f00000000000000000000000000000000000001ad428e4906ae43d8f9852d0dd616906379df72bd90612fe790849060040161447c565b602060405180830381865afa158015613004573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061302891906139bf565b610140909401919091525090919050565b60008060448351101561306457825160405163252e3c3d60e01b81526004016102e091815260200190565b6001600160a01b03602484015116915060448301519050915091565b6000806044835110156130ab57825160405163252e3c3d60e01b81526004016102e091815260200190565b6001600160a01b0360248401511691506001604484015115189050915091565b6000805b8251811015613124578281815181106130ea576130ea613ed2565b60200260200101516001600160a01b0316846001600160a01b031603613114576001915050610693565b61311d81613ee8565b90506130cf565b5060009392505050565b604051806040016040528061314161314e565b8152602001606081525090565b60405180610160016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081526020016000600381111561319b5761319b613626565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613203576132036131cb565b60405290565b60405161012081016001600160401b0381118282101715613203576132036131cb565b604051608081016001600160401b0381118282101715613203576132036131cb565b604051601f8201601f191681016001600160401b0381118282101715613276576132766131cb565b604052919050565b60006001600160401b03821115613297576132976131cb565b50601f01601f191660200190565b600082601f8301126132b657600080fd5b81356132c96132c48261327e565b61324e565b8181528460208386010111156132de57600080fd5b816020850160208301376000918101602001919091529392505050565b60006001600160401b03821115613314576133146131cb565b5060051b60200190565b6001600160a01b038116811461333357600080fd5b50565b600082601f83011261334757600080fd5b813560206133576132c4836132fb565b82815260059290921b8401810191818101908684111561337657600080fd5b8286015b8481101561339a57803561338d8161331e565b835291830191830161337a565b509695505050505050565b600082601f8301126133b657600080fd5b813560206133c66132c4836132fb565b82815260059290921b840181019181810190868411156133e557600080fd5b8286015b8481101561339a57803583529183019183016133e9565b60006020828403121561341257600080fd5b81356001600160401b038082111561342957600080fd5b9083019060e0828603121561343d57600080fd5b6134456131e1565b8235815260208301358281111561345b57600080fd5b613467878286016132a5565b60208301525060408301358281111561347f57600080fd5b61348b878286016132a5565b6040830152506060830135828111156134a357600080fd5b6134af878286016132a5565b6060830152506080830135608082015260a0830135828111156134d157600080fd5b6134dd87828601613336565b60a08301525060c0830135828111156134f557600080fd5b613501878286016133a5565b60c08301525095945050505050565b60005b8381101561352b578181015183820152602001613513565b50506000910152565b6000815180845261354c816020860160208601613510565b601f01601f19169290920160200192915050565b60208152600061074c6020830184613534565b60008060006040848603121561358857600080fd5b83356135938161331e565b925060208401356001600160401b03808211156135af57600080fd5b818601915086601f8301126135c357600080fd5b8135818111156135d257600080fd5b8760208285010111156135e457600080fd5b6020830194508093505050509250925092565b60006020828403121561360957600080fd5b5035919050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b805164ffffffffff8116811461365157600080fd5b919050565b80516136518161331e565b600082601f83011261367257600080fd5b815160206136826132c4836132fb565b82815260059290921b840181019181810190868411156136a157600080fd5b8286015b8481101561339a57805183529183019183016136a5565b600082601f8301126136cd57600080fd5b815160206136dd6132c4836132fb565b82815260059290921b840181019181810190868411156136fc57600080fd5b8286015b8481101561339a5780516137138161331e565b8352918301918301613700565b80516001600160e01b03198116811461365157600080fd5b60006020828403121561374a57600080fd5b81516001600160401b038082111561376157600080fd5b9083019060e0828603121561377557600080fd5b61377d6131e1565b8251815261378d6020840161363c565b602082015261379e60408401613656565b6040820152606083015160608201526080830151828111156137bf57600080fd5b6137cb87828601613661565b60808301525060a0830151828111156137e357600080fd5b6137ef878286016136bc565b60a08301525061380160c08401613720565b60c082015295945050505050565b6002811061333357600080fd5b80516136518161380f565b60006020828403121561383957600080fd5b81516001600160401b038082111561385057600080fd5b90830190610120828603121561386557600080fd5b61386d613209565b82518152602083015160208201526138876040840161363c565b60408201526138986060840161381c565b60608201526138a960808401613656565b608082015260a083015160a082015260c0830151828111156138ca57600080fd5b6138d687828601613661565b60c08301525060e0830151828111156138ee57600080fd5b6138fa878286016136bc565b60e0830152506101009150613910828401613720565b91810191909152949350505050565b600060a0828403121561393157600080fd5b60405160a081018181106001600160401b0382111715613953576139536131cb565b604052825181526139666020840161363c565b60208201526139776040840161363c565b6040820152606083015161398a8161331e565b60608201526080928301519281019290925250919050565b6000602082840312156139b457600080fd5b815161074c8161380f565b6000602082840312156139d157600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610693576106936139d8565b6002811061333357613333613626565b60608101613a1e84613a01565b83825261074c60208301848051825260209081015164ffffffffff16910152565b600060408284031215613a5157600080fd5b604051604081018181106001600160401b0382111715613a7357613a736131cb565b60405282518152905080613a896020840161363c565b60208201525092915050565b60008060608385031215613aa857600080fd5b8251613ab38161380f565b9150613ac28460208501613a3f565b90509250929050565b600060408284031215613add57600080fd5b604051604081018181106001600160401b0382111715613aff57613aff6131cb565b6040528251613b0d8161331e565b81526020928301519281019290925250919050565b600060808284031215613b3457600080fd5b604051608081018181106001600160401b0382111715613b5657613b566131cb565b604052613b628361363c565b8152613b706020840161363c565b6020820152604083015161ffff81168114613b8a57600080fd5b604082015260608301516bffffffffffffffffffffffff81168114613bae57600080fd5b60608201529392505050565b6000613bc86132c48461327e565b9050828152838383011115613bdc57600080fd5b61074c836020830184613510565b600060208284031215613bfc57600080fd5b81516001600160401b03811115613c1257600080fd5b8201601f81018413613c2357600080fd5b611e2e84825160208401613bba565b60e081526000613c4560e083018a613534565b8281036020840152613c57818a613534565b6001600160a01b0398909816604084015250506060810194909452608084019290925260a083015260c09091015292915050565b600060208284031215613c9d57600080fd5b815161074c8161331e565b60608101613cb585613a01565b9381526001600160a01b0392909216602083015260409091015290565b80516fffffffffffffffffffffffffffffffff8116811461365157600080fd5b600060e08284031215613d0457600080fd5b60405160e081018181106001600160401b0382111715613d2657613d266131cb565b6040528251613d348161380f565b8152602083810151908201526040830151613d4e8161331e565b60408201526060830151613d618161331e565b60608201526080830151613d748161331e565b6080820152613d8560a08401613cd2565b60a0820152613d9660c08401613cd2565b60c08201529392505050565b600082601f830112613db357600080fd5b61074c83835160208501613bba565b60006020808385031215613dd557600080fd5b82516001600160401b0380821115613dec57600080fd5b818501915085601f830112613e0057600080fd5b8151613e0e6132c4826132fb565b81815260059190911b83018401908481019088831115613e2d57600080fd5b8585015b83811015613ec557805185811115613e4857600080fd5b86016080818c03601f19011215613e5f5760008081fd5b613e6761322c565b88820151613e748161331e565b81526040828101518a83015260608084015189811115613e945760008081fd5b613ea28f8d83880101613da2565b928401929092526080939093015192820192909252845250918601918601613e31565b5098975050505050505050565b634e487b7160e01b600052603260045260246000fd5b600060018201613efa57613efa6139d8565b5060010190565b81810381811115610693576106936139d8565b60008060408385031215613f2757600080fd5b8251613f328161331e565b60208401519092506001600160401b03811115613f4e57600080fd5b613f5a85828601613da2565b9150509250929050565b600060208284031215613f7657600080fd5b81516004811061074c57600080fd5b6004811061333357613333613626565b60608101613a1e84613f85565b805160ff8116811461365157600080fd5b60008060608385031215613fc657600080fd5b613ab383613fa2565b60808101613fdc86613f85565b94815260208101939093526001600160a01b0391909116604083015260609091015290565b600080828403608081121561401557600080fd5b61401e84613fa2565b92506060601f198201121561403257600080fd5b50604051606081018181106001600160401b0382111715614055576140556131cb565b60409081526020850151825284015161406d8161331e565b602082015261407e6060850161363c565b6040820152809150509250929050565b6001600160a01b0383168152604060208201819052600090611e2e90830184613534565b600082516140c4818460208701613510565b9190910192915050565b8051801515811461365157600080fd5b600080604083850312156140f157600080fd5b82516140fc8161331e565b9150613ac2602084016140ce565b8082028115828204841417610693576106936139d8565b60008261413e57634e487b7160e01b600052601260045260246000fd5b500490565b6006811061415357614153613626565b9052565b600081518084526020808501945080840160005b838110156141c1578151614180888251614143565b838101516001600160a01b03168885015260408082015190890152606080820151908901526080908101519088015260a0909601959082019060010161416b565b509495945050505050565b600081518084526020808501945080840160005b838110156141c15781516141f5888251614143565b808401516001600160a01b0390811689860152604080830151908a0152606080830151908a0152608080830151908a015260a091820151169088015260c090960195908201906001016141e0565b61415381613f85565b80516001600160a01b031682526000610160602083015161427860208601826001600160a01b03169052565b50604083015181604086015261429082860182614157565b915050606083015184820360608601526142aa82826141cc565b91505060808301516142bf6080860182614243565b5060a0838101519085015260c0808401519085015260e08084015190850152610100808401519085015261012080840151908501526101409283015192909301919091525090565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561437e57888303603f19018552815180518785526143528886018261424c565b91890151858303868b015291905061436a8183613534565b96890196945050509086019060010161432e565b509098975050505050505050565b60006020828403121561439e57600080fd5b61074c826140ce565b60c0815260006143ba60c083018961424c565b6020830197909752506001600160a01b039490941660408501526060840192909252608083015260a090910152919050565b60e0815260006143ff60e083018a61424c565b6020830198909852506001600160a01b039590951660408601526060850193909352608084019190915260a083015260c090910152919050565b6000806000806080858703121561444f57600080fd5b614458856140ce565b9350614466602086016140ce565b6040860151606090960151949790965092505050565b60208152600061074c602083018461424c56fea2646970667358221220b8dd4cd5ba3f14e7fa395b99a1d5709ab631e49e032eae33fc17d59bfa3d4e1464736f6c63430008110033

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

0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade00000000000000000000000000000000000001ad428e4906ae43d8f9852d0dd600000000000000000000000000000000f9490004c11cef243f5400493c00ad63000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad67300000000000000000000000085aa7f78bdb2de8f3e0c0010d99ad5853ffcfc63

-----Decoded View---------------
Arg [0] : globals (address): 0x1cA20040cE6aD406bC2A6c89976388829E7fbAde
Arg [1] : seaport (address): 0x00000000000001ad428e4906aE43D8F9852d0dD6
Arg [2] : seaportConduitController (address): 0x00000000F9490004C11Cef243f5400493c00Ad63
Arg [3] : zoraAuctionHouse (address): 0xE468cE99444174Bd3bBBEd09209577d25D1ad673
Arg [4] : fractionalVaultFactory (address): 0x85Aa7f78BdB2DE8F3e0c0010d99AD5853fFcfC63

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade
Arg [1] : 00000000000000000000000000000000000001ad428e4906ae43d8f9852d0dd6
Arg [2] : 00000000000000000000000000000000f9490004c11cef243f5400493c00ad63
Arg [3] : 000000000000000000000000e468ce99444174bd3bbbed09209577d25d1ad673
Arg [4] : 00000000000000000000000085aa7f78bdb2de8f3e0c0010d99ad5853ffcfc63


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.