Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
CollectionBatchBuyCrowdfund
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Beta Software // http://ipfs.io/ipfs/QmbGX2MFCaMAsMNMugRFND6DtYygRkwkvrqEyTKhTdBLo5 pragma solidity 0.8.17; import "../tokens/IERC721.sol"; import "../party/Party.sol"; import "../utils/Implementation.sol"; import "../utils/LibSafeERC721.sol"; import "../globals/IGlobals.sol"; import "../gatekeepers/IGateKeeper.sol"; import "openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import "./BuyCrowdfundBase.sol"; contract CollectionBatchBuyCrowdfund is BuyCrowdfundBase { using LibSafeERC721 for IERC721; using LibSafeCast for uint256; using LibRawResult for bytes; struct CollectionBatchBuyCrowdfundOptions { // The name of the crowdfund. // This will also carry over to the governance party. string name; // The token symbol for both the crowdfund and the governance NFTs. string symbol; // Customization preset ID to use for the crowdfund and governance NFTs. uint256 customizationPresetId; // The ERC721 contract of the NFTs being bought. IERC721 nftContract; // The merkle root of the token IDs that can be bought. If null, any // token ID in the collection can be bought. bytes32 nftTokenIdsMerkleRoot; // How long this crowdfund has to buy the NFTs, in seconds. uint40 duration; // Maximum amount this crowdfund will pay for an NFT. uint96 maximumPrice; // An address that receives a portion of the final voting power // when the party transitions into governance. address payable splitRecipient; // What percentage (in bps) of the final total voting power `splitRecipient` // receives. uint16 splitBps; // If ETH is attached during deployment, it will be interpreted // as a contribution. This is who gets credit for that contribution. address initialContributor; // If there is an initial contribution, this is who they will delegate their // voting power to when the crowdfund transitions to governance. address initialDelegate; // Minimum amount of ETH that can be contributed to this crowdfund per address. uint96 minContribution; // Maximum amount of ETH that can be contributed to this crowdfund per address. uint96 maxContribution; // The gatekeeper contract to use (if non-null) to restrict who can // contribute to this crowdfund. IGateKeeper gateKeeper; // The gate ID within the gateKeeper contract to use. bytes12 gateKeeperId; // Fixed governance options (i.e. cannot be changed) that the governance // `Party` will be created with if the crowdfund succeeds. FixedGovernanceOpts governanceOpts; } struct BatchBuyArgs { uint256[] tokenIds; address payable[] callTargets; uint96[] callValues; bytes[] callDatas; bytes32[][] proofs; uint256 minTokensBought; uint256 minTotalEthUsed; FixedGovernanceOpts governanceOpts; uint256 hostIndex; } error NothingBoughtError(); error InvalidMinTokensBoughtError(uint256 minTokensBought); error InvalidTokenIdError(); error ContributionsSpentForFailedBuyError(); error NotEnoughTokensBoughtError(uint256 tokensBought, uint256 minTokensBought); error NotEnoughEthUsedError(uint256 ethUsed, uint256 minTotalEthUsed); error MismatchedCallArgLengthsError(); /// @notice The contract of NFTs to buy. IERC721 public nftContract; /// @notice The merkle root of the token IDs that can be bought. If null, /// allow any token ID in the collection can be bought. bytes32 public nftTokenIdsMerkleRoot; // Set the `Globals` contract. constructor(IGlobals globals) BuyCrowdfundBase(globals) {} /// @notice Initializer to be delegatecalled by `Proxy` constructor. Will /// revert if called outside the constructor. /// @param opts Options used to initialize the crowdfund. These are fixed /// and cannot be changed later. function initialize( CollectionBatchBuyCrowdfundOptions memory opts ) external payable onlyConstructor { if (opts.governanceOpts.hosts.length == 0) { revert MissingHostsError(); } BuyCrowdfundBase._initialize( BuyCrowdfundBaseOptions({ name: opts.name, symbol: opts.symbol, customizationPresetId: opts.customizationPresetId, duration: opts.duration, maximumPrice: opts.maximumPrice, splitRecipient: opts.splitRecipient, splitBps: opts.splitBps, initialContributor: opts.initialContributor, initialDelegate: opts.initialDelegate, minContribution: opts.minContribution, maxContribution: opts.maxContribution, gateKeeper: opts.gateKeeper, gateKeeperId: opts.gateKeeperId, governanceOpts: opts.governanceOpts }) ); nftContract = opts.nftContract; nftTokenIdsMerkleRoot = opts.nftTokenIdsMerkleRoot; } /// @notice Execute arbitrary calldata to perform a batch buy, creating a party /// if it successfully buys the NFT. Only a host may call this. /// @param args Arguments for the batch buy. /// @return party_ Address of the `Party` instance created after its bought. function batchBuy(BatchBuyArgs memory args) external onlyDelegateCall returns (Party party_) { // This function is restricted to hosts. _assertIsHost(msg.sender, args.governanceOpts, args.hostIndex); { // Ensure that the crowdfund is still active. CrowdfundLifecycle lc = getCrowdfundLifecycle(); if (lc != CrowdfundLifecycle.Active) { revert WrongLifecycleError(lc); } } if (args.minTokensBought == 0) { // Must buy at least one token. revert InvalidMinTokensBoughtError(0); } // Check length of all arg arrays. if ( args.tokenIds.length != args.callTargets.length || args.tokenIds.length != args.callValues.length || args.tokenIds.length != args.callDatas.length || args.tokenIds.length != args.proofs.length ) { revert MismatchedCallArgLengthsError(); } // Temporarily set to non-zero as a reentrancy guard. settledPrice = type(uint96).max; uint96 totalEthUsed; uint256 tokensBought; IERC721[] memory tokens = new IERC721[](args.tokenIds.length); IERC721 token = nftContract; bytes32 root = nftTokenIdsMerkleRoot; for (uint256 i; i < args.tokenIds.length; ++i) { if (root != bytes32(0)) { // Verify the token ID is in the merkle tree. _verifyTokenId(args.tokenIds[i], root, args.proofs[i]); } // Used to ensure no ETH is spent if the call fails. uint256 balanceBefore = address(this).balance; // Execute the call to buy the NFT. (bool success, bytes memory revertData) = _buy( token, args.tokenIds[i], args.callTargets[i], args.callValues[i], args.callDatas[i] ); if (!success) { if (args.minTokensBought >= args.tokenIds.length) { // If the call failed with revert data, revert with that data. if (revertData.length > 0) { revertData.rawRevert(); } else { revert FailedToBuyNFTError(token, args.tokenIds[i]); } } else { // If the call failed, ensure no ETH was spent and skip this NFT. if (address(this).balance != balanceBefore) { revert ContributionsSpentForFailedBuyError(); } continue; } } totalEthUsed += args.callValues[i]; ++tokensBought; tokens[tokensBought - 1] = token; args.tokenIds[tokensBought - 1] = args.tokenIds[i]; } // This is to prevent this crowdfund from finalizing a loss if nothing // was attempted to be bought (ie. `tokenIds` is empty) or all NFTs were // bought for free. if (totalEthUsed == 0) revert NothingBoughtError(); // Check number of tokens bought is not less than the minimum. if (tokensBought < args.minTokensBought) { revert NotEnoughTokensBoughtError(tokensBought, args.minTokensBought); } // Check total ETH used is not less than the minimum. if (totalEthUsed < args.minTotalEthUsed) { revert NotEnoughEthUsedError(totalEthUsed, args.minTotalEthUsed); } assembly { // Update length of `tokens` mstore(tokens, tokensBought) // Update length of `tokenIds` mstore(0x1A0, tokensBought) } return _finalize( tokens, args.tokenIds, totalEthUsed, args.governanceOpts, // If `_assertIsHost()` succeeded, the governance opts were validated. true ); } function _verifyTokenId(uint256 tokenId, bytes32 root, bytes32[] memory proof) private pure { bytes32 leaf; assembly { mstore(0x00, tokenId) leaf := keccak256(0x00, 0x20) } if (!MerkleProof.verify(proof, root, leaf)) revert InvalidTokenIdError(); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.17; import "../tokens/IERC721.sol"; import "../party/Party.sol"; import "../utils/Implementation.sol"; import "../utils/LibSafeERC721.sol"; import "../utils/LibRawResult.sol"; import "../globals/IGlobals.sol"; import "../gatekeepers/IGateKeeper.sol"; import "./Crowdfund.sol"; // Base for BuyCrowdfund and CollectionBuyCrowdfund abstract contract BuyCrowdfundBase is Crowdfund { using LibSafeERC721 for IERC721; using LibSafeCast for uint256; struct BuyCrowdfundBaseOptions { // The name of the crowdfund. // This will also carry over to the governance party. string name; // The token symbol for both the crowdfund and the governance NFTs. string symbol; // Customization preset ID to use for the crowdfund and governance NFTs. uint256 customizationPresetId; // How long this crowdfund has to buy the NFT, in seconds. uint40 duration; // Maximum amount this crowdfund will pay for the NFT. uint96 maximumPrice; // An address that receives an extra share of the final voting power // when the party transitions into governance. address payable splitRecipient; // What percentage (in bps) of the final total voting power `splitRecipient` // receives. uint16 splitBps; // If ETH is attached during deployment, it will be interpreted // as a contribution. This is who gets credit for that contribution. address initialContributor; // If there is an initial contribution, this is who they will delegate their // voting power to when the crowdfund transitions to governance. address initialDelegate; // Minimum amount of ETH that can be contributed to this crowdfund per address. uint96 minContribution; // Maximum amount of ETH that can be contributed to this crowdfund per address. uint96 maxContribution; // The gatekeeper contract to use (if non-null) to restrict who can // contribute to this crowdfund. IGateKeeper gateKeeper; // The gatekeeper contract to use (if non-null). bytes12 gateKeeperId; // Governance options. FixedGovernanceOpts governanceOpts; } event Won(Party party, IERC721[] tokens, uint256[] tokenIds, uint256 settledPrice); event Lost(); error MaximumPriceError(uint96 callValue, uint96 maximumPrice); error NoContributionsError(); error CallProhibitedError(address target, bytes data); error FailedToBuyNFTError(IERC721 token, uint256 tokenId); /// @notice When this crowdfund expires. uint40 public expiry; /// @notice Maximum amount this crowdfund will pay for the NFT. uint96 public maximumPrice; /// @notice What the NFT was actually bought for. uint96 public settledPrice; // Set the `Globals` contract. constructor(IGlobals globals) Crowdfund(globals) {} // Initialize storage for proxy contracts. function _initialize(BuyCrowdfundBaseOptions memory opts) internal { expiry = uint40(opts.duration + block.timestamp); maximumPrice = opts.maximumPrice; Crowdfund._initialize( CrowdfundOptions({ name: opts.name, symbol: opts.symbol, customizationPresetId: opts.customizationPresetId, splitRecipient: opts.splitRecipient, splitBps: opts.splitBps, initialContributor: opts.initialContributor, initialDelegate: opts.initialDelegate, minContribution: opts.minContribution, maxContribution: opts.maxContribution, gateKeeper: opts.gateKeeper, gateKeeperId: opts.gateKeeperId, governanceOpts: opts.governanceOpts }) ); } // Execute arbitrary calldata to perform a buy, creating a party // if it successfully buys the NFT. function _buy( IERC721 token, uint256 tokenId, address payable callTarget, uint96 callValue, bytes memory callData ) internal returns (bool success, bytes memory revertData) { // Check that the call is not prohibited. if (!_isCallAllowed(callTarget, callData, token)) { revert CallProhibitedError(callTarget, callData); } // Check that the call value is under the maximum price. { uint96 maximumPrice_ = maximumPrice; if (callValue > maximumPrice_) { revert MaximumPriceError(callValue, maximumPrice_); } } // Execute the call to buy the NFT. (bool s, bytes memory r) = callTarget.call{ value: callValue }(callData); if (!s) { return (false, r); } // Return whether the NFT was successfully bought. return (token.safeOwnerOf(tokenId) == address(this), ""); } function _finalize( IERC721[] memory tokens, uint256[] memory tokenIds, uint96 totalEthUsed, FixedGovernanceOpts memory governanceOpts, bool isValidatedGovernanceOpts ) internal returns (Party party_) { { // Prevent unaccounted ETH from being used to inflate the price and // create "ghost shares" in voting power. uint96 totalContributions_ = totalContributions; if (totalEthUsed > totalContributions_) { revert ExceedsTotalContributionsError(totalEthUsed, totalContributions_); } } if (totalEthUsed != 0) { // Create a party around the newly bought NFTs and finalize a win. settledPrice = totalEthUsed; party_ = _createParty(governanceOpts, isValidatedGovernanceOpts, tokens, tokenIds); emit Won(party_, tokens, tokenIds, totalEthUsed); } else { // If all NFTs were purchased for free or were all "gifted" to us, // refund all contributors by finalizing a loss. settledPrice = 0; expiry = uint40(block.timestamp); emit Lost(); } } function _finalize( IERC721 token, uint256 tokenId, uint96 totalEthUsed, FixedGovernanceOpts memory governanceOpts, bool isValidatedGovernanceOpts ) internal returns (Party party_) { IERC721[] memory tokens = new IERC721[](1); tokens[0] = token; uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = tokenId; return _finalize(tokens, tokenIds, totalEthUsed, governanceOpts, isValidatedGovernanceOpts); } /// @inheritdoc Crowdfund function getCrowdfundLifecycle() public view override returns (CrowdfundLifecycle) { // If there is a settled price then we tried to buy the NFT. if (settledPrice != 0) { return address(party) != address(0) ? CrowdfundLifecycle.Won // If we have a party, then we succeeded buying the NFT. : CrowdfundLifecycle.Busy; // Otherwise we're in the middle of the `buy()`. } if (block.timestamp >= expiry) { // Expired, but nothing to do so skip straight to lost, or NFT was // acquired for free so refund contributors and trigger lost. return CrowdfundLifecycle.Lost; } return CrowdfundLifecycle.Active; } function _getFinalPrice() internal view override returns (uint256) { return settledPrice; } function _isCallAllowed( address payable callTarget, bytes memory callData, IERC721 token ) private view returns (bool isAllowed) { // Ensure the call target isn't trying to reenter if (callTarget == address(this)) { return false; } if (callTarget == address(token) && callData.length >= 4) { // Get the function selector of the call (first 4 bytes of calldata). bytes4 selector; assembly { selector := and( mload(add(callData, 32)), 0xffffffff00000000000000000000000000000000000000000000000000000000 ) } // Prevent approving the NFT to be transferred out from the crowdfund. if ( selector == IERC721.approve.selector || selector == IERC721.setApprovalForAll.selector ) { return false; } } // All other calls are allowed. return true; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.17; import "../utils/LibAddress.sol"; import "../utils/LibRawResult.sol"; import "../utils/LibSafeCast.sol"; import "../tokens/ERC721Receiver.sol"; import "../party/Party.sol"; import "../globals/IGlobals.sol"; import "../gatekeepers/IGateKeeper.sol"; import "../renderers/RendererStorage.sol"; import "./CrowdfundNFT.sol"; // Base contract for AuctionCrowdfund/BuyCrowdfund. // Holds post-win/loss logic. E.g., burning contribution NFTs and creating a // party after winning. abstract contract Crowdfund is Implementation, ERC721Receiver, CrowdfundNFT { using LibRawResult for bytes; using LibSafeCast for uint256; using LibAddress for address payable; enum CrowdfundLifecycle { Invalid, Active, Expired, Busy, // Temporary. mid-settlement state Lost, Won } // PartyGovernance options that must be known and fixed at crowdfund creation. // This is a subset of PartyGovernance.GovernanceOpts. struct FixedGovernanceOpts { // 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; // Fee bps for governance distributions. uint16 feeBps; // Fee recipeint for governance distributions. address payable feeRecipient; } // Options to be passed into `_initialize()` when the crowdfund is created. struct CrowdfundOptions { string name; string symbol; uint256 customizationPresetId; address payable splitRecipient; uint16 splitBps; address initialContributor; address initialDelegate; uint96 minContribution; uint96 maxContribution; IGateKeeper gateKeeper; bytes12 gateKeeperId; FixedGovernanceOpts governanceOpts; } // A record of a single contribution made by a user. // Stored in `_contributionsByContributor`. struct Contribution { // The value of `Crowdfund.totalContributions` when this contribution was made. uint96 previousTotalContributions; // How much was this contribution. uint96 amount; } // A record of the refund and governance NFT owed to a contributor if it // could not be received by them from `burn()`. struct Claim { uint256 refund; uint256 governanceTokenId; } error PartyAlreadyExistsError(Party party); error WrongLifecycleError(CrowdfundLifecycle lc); error InvalidGovernanceOptionsError(); error InvalidDelegateError(); error InvalidContributorError(); error NoPartyError(); error NotAllowedByGateKeeperError( address contributor, IGateKeeper gateKeeper, bytes12 gateKeeperId, bytes gateData ); error SplitRecipientAlreadyBurnedError(); error InvalidBpsError(uint16 bps); error ExceedsTotalContributionsError(uint96 value, uint96 totalContributions); error NothingToClaimError(); error OnlyPartyHostError(); error OnlyContributorError(); error MissingHostsError(); error OnlyPartyDaoError(address notDao); error OnlyPartyDaoOrHostError(address notDao); error OnlyWhenEmergencyActionsAllowedError(); error BelowMinimumContributionsError(uint96 contributions, uint96 minContributions); error AboveMaximumContributionsError(uint96 contributions, uint96 maxContributions); event Burned(address contributor, uint256 ethUsed, uint256 ethOwed, uint256 votingPower); event Contributed( address sender, address contributor, uint256 amount, address delegate, uint256 previousTotalContributions ); event EmergencyExecute(address target, bytes data, uint256 amountEth); event EmergencyExecuteDisabled(); // The `Globals` contract storing global configuration values. This contract // is immutable and it’s address will never change. IGlobals private immutable _GLOBALS; /// @notice The party instance created by `_createParty()`, if any after a /// successful crowdfund. Party public party; /// @notice The total (recorded) ETH contributed to this crowdfund. uint96 public totalContributions; /// @notice The gatekeeper contract to use (if non-null) to restrict who can /// contribute to the party. IGateKeeper public gateKeeper; /// @notice The ID of the gatekeeper strategy to use. bytes12 public gateKeeperId; /// @notice Who will receive a reserved portion of governance power when /// the governance party is created. address payable public splitRecipient; /// @notice How much governance power to reserve for `splitRecipient`, /// in bps, where 10,000 = 100%. uint16 public splitBps; // Whether the share for split recipient has been claimed through `burn()`. bool private _splitRecipientHasBurned; /// @notice Hash of party governance options passed into `initialize()`. /// Used to check whether the `GovernanceOpts` passed into /// `_createParty()` matches. bytes32 public governanceOptsHash; /// @notice Who a contributor last delegated to. mapping(address => address) public delegationsByContributor; // Array of contributions by a contributor. // One is created for every nonzero contribution made. // `internal` for testing purposes only. mapping(address => Contribution[]) internal _contributionsByContributor; /// @notice Stores the amount of ETH owed back to a contributor and governance NFT /// that should be minted to them if it could not be transferred to /// them with `burn()`. mapping(address => Claim) public claims; /// @notice Minimum amount of ETH that can be contributed to this crowdfund per address. uint96 public minContribution; /// @notice Maximum amount of ETH that can be contributed to this crowdfund per address. uint96 public maxContribution; /// @notice Whether the DAO has emergency powers for this party. bool public emergencyExecuteDisabled; // Set the `Globals` contract. constructor(IGlobals globals) CrowdfundNFT(globals) { _GLOBALS = globals; } // Initialize storage for proxy contracts, credit initial contribution (if // any), and setup gatekeeper. function _initialize(CrowdfundOptions memory opts) internal { CrowdfundNFT._initialize(opts.name, opts.symbol, opts.customizationPresetId); // Check that BPS values do not exceed the max. if (opts.governanceOpts.feeBps > 1e4) { revert InvalidBpsError(opts.governanceOpts.feeBps); } if (opts.governanceOpts.passThresholdBps > 1e4) { revert InvalidBpsError(opts.governanceOpts.passThresholdBps); } if (opts.splitBps > 1e4) { revert InvalidBpsError(opts.splitBps); } governanceOptsHash = _hashFixedGovernanceOpts(opts.governanceOpts); splitRecipient = opts.splitRecipient; splitBps = opts.splitBps; // Set the minimum and maximum contribution amounts. minContribution = opts.minContribution; maxContribution = opts.maxContribution; // If the deployer passed in some ETH during deployment, credit them // for the initial contribution. uint96 initialContribution = msg.value.safeCastUint256ToUint96(); if (initialContribution > 0) { _setDelegate(opts.initialContributor, opts.initialDelegate); // If this ETH is passed in, credit it to the `initialContributor`. _contribute(opts.initialContributor, opts.initialDelegate, initialContribution, 0, ""); } // Set up gatekeeper after initial contribution (initial always gets in). gateKeeper = opts.gateKeeper; gateKeeperId = opts.gateKeeperId; } /// @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 onlyDelegateCall { // Must be called by the DAO. if (!_isPartyDao(msg.sender)) { revert OnlyPartyDaoError(msg.sender); } // Must not be disabled by DAO or host. if (emergencyExecuteDisabled) { revert OnlyWhenEmergencyActionsAllowedError(); } (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. /// @param governanceOpts The fixed governance opts the crowdfund was created with. /// @param hostIndex The index of the party host (caller). function disableEmergencyExecute( FixedGovernanceOpts memory governanceOpts, uint256 hostIndex ) external onlyDelegateCall { // Only the DAO or a host can call this. if (!_isHost(msg.sender, governanceOpts, hostIndex) && !_isPartyDao(msg.sender)) { revert OnlyPartyDaoOrHostError(msg.sender); } emergencyExecuteDisabled = true; emit EmergencyExecuteDisabled(); } /// @notice Burn the participation NFT for `contributor`, potentially /// minting voting power and/or refunding unused ETH. `contributor` /// may also be the split recipient, regardless of whether they are /// also a contributor or not. This can be called by anyone on a /// contributor's behalf to unlock their voting power in the /// governance stage ensuring delegates receive their voting /// power and governance is not stalled. /// @param contributor The contributor whose NFT to burn for. function burn(address payable contributor) public { return _burn(contributor, getCrowdfundLifecycle(), party); } /// @dev Alias for `burn()`. function activateOrRefund(address payable contributor) external { burn(contributor); } /// @notice `burn()` in batch form. /// Will not revert if any individual burn fails. /// @param contributors The contributors whose NFT to burn for. /// @param revertOnFailure If true, revert if any burn fails. function batchBurn(address payable[] calldata contributors, bool revertOnFailure) public { for (uint256 i = 0; i < contributors.length; ++i) { (bool s, bytes memory r) = address(this).delegatecall( abi.encodeCall(this.burn, (contributors[i])) ); if (revertOnFailure && !s) { r.rawRevert(); } } } /// @dev Alias for `batchBurn()`. function batchActivateOrRefund( address payable[] calldata contributors, bool revertOnFailure ) external { batchBurn(contributors, revertOnFailure); } /// @notice Claim a governance NFT or refund that is owed back but could not be /// given due to error in `_burn()` (eg. a contract that does not /// implement `onERC721Received()` or cannot receive ETH). Only call /// this if refund and governance NFT minting could not be returned /// with `burn()`. /// @param receiver The address to receive the NFT or refund. function claim(address payable receiver) external { Claim memory claimInfo = claims[msg.sender]; delete claims[msg.sender]; if (claimInfo.refund == 0 && claimInfo.governanceTokenId == 0) { revert NothingToClaimError(); } if (claimInfo.refund != 0) { receiver.transferEth(claimInfo.refund); } if (claimInfo.governanceTokenId != 0) { party.safeTransferFrom(address(this), receiver, claimInfo.governanceTokenId); } } /// @notice Contribute to this crowdfund and/or update your delegation for the /// governance phase should the crowdfund succeed. /// For restricted crowdfunds, `gateData` can be provided to prove /// membership to the gatekeeper. /// @param delegate The address to delegate to for the governance phase. /// @param gateData Data to pass to the gatekeeper to prove eligibility. function contribute(address delegate, bytes memory gateData) external payable onlyDelegateCall { _setDelegate(msg.sender, delegate); _contribute( msg.sender, delegate, msg.value.safeCastUint256ToUint96(), // We cannot use `address(this).balance - msg.value` as the previous // total contributions in case someone forces (suicides) ETH into this // contract. This wouldn't be such a big deal for open crowdfunds // but private ones (protected by a gatekeeper) could be griefed // because it would ultimately result in governance power that // is unattributed/unclaimable, meaning that party will never be // able to reach 100% consensus. totalContributions, gateData ); } /// @notice Contribute to this crowdfund on behalf of another address. /// @param recipient The address to record the contribution under. /// @param initialDelegate The address to delegate to for the governance phase if recipient hasn't delegated. /// @param gateData Data to pass to the gatekeeper to prove eligibility. function contributeFor( address recipient, address initialDelegate, bytes memory gateData ) external payable onlyDelegateCall { _setDelegate(recipient, initialDelegate); _contribute( recipient, initialDelegate, msg.value.safeCastUint256ToUint96(), totalContributions, gateData ); } /// @notice `contributeFor()` in batch form. /// May not revert if any individual contribution fails. /// @param recipients The addresses to record the contributions under. /// @param initialDelegates The addresses to delegate to for each recipient. /// @param values The ETH to contribute for each recipient. /// @param gateDatas Data to pass to the gatekeeper to prove eligibility. /// @param revertOnFailure If true, revert if any contribution fails. function batchContributeFor( address[] memory recipients, address[] memory initialDelegates, uint256[] memory values, bytes[] memory gateDatas, bool revertOnFailure ) external payable { for (uint256 i; i < recipients.length; ++i) { (bool s, bytes memory r) = address(this).call{ value: values[i] }( abi.encodeCall( this.contributeFor, (recipients[i], initialDelegates[i], gateDatas[i]) ) ); if (revertOnFailure && !s) { r.rawRevert(); } } } /// @inheritdoc EIP165 function supportsInterface( bytes4 interfaceId ) public pure override(ERC721Receiver, CrowdfundNFT) returns (bool) { return ERC721Receiver.supportsInterface(interfaceId) || CrowdfundNFT.supportsInterface(interfaceId); } /// @notice Retrieve info about a participant's contributions. /// @dev This will only be called off-chain so doesn't have to be optimal. /// @param contributor The contributor to retrieve contributions for. /// @return ethContributed The total ETH contributed by `contributor`. /// @return ethUsed The total ETH used by `contributor` to acquire the NFT. /// @return ethOwed The total ETH refunded back to `contributor`. /// @return votingPower The total voting power minted to `contributor`. function getContributorInfo( address contributor ) external view returns (uint256 ethContributed, uint256 ethUsed, uint256 ethOwed, uint256 votingPower) { CrowdfundLifecycle lc = getCrowdfundLifecycle(); if (lc == CrowdfundLifecycle.Won || lc == CrowdfundLifecycle.Lost) { (ethUsed, ethOwed, votingPower) = _getFinalContribution(contributor); ethContributed = ethUsed + ethOwed; } else { Contribution[] memory contributions = _contributionsByContributor[contributor]; uint256 numContributions = contributions.length; for (uint256 i; i < numContributions; ++i) { ethContributed += contributions[i].amount; } } } /// @notice Get the current lifecycle of the crowdfund. function getCrowdfundLifecycle() public view virtual returns (CrowdfundLifecycle lifecycle); // Get the final sale price of the bought assets. This will also be the total // voting power of the governance party. function _getFinalPrice() internal view virtual returns (uint256); // Assert that `who` is a host at `governanceOpts.hosts[hostIndex]` and, // if so, assert that the governance opts is the same as the crowdfund // was created with. // Return true if `governanceOpts` was validated in the process. function _assertIsHost( address who, FixedGovernanceOpts memory governanceOpts, uint256 hostIndex ) internal view { if (!_isHost(who, governanceOpts, hostIndex)) { revert OnlyPartyHostError(); } } // Check if `who` is a host at `hostIndex` index. Validates governance opts if so. function _isHost( address who, FixedGovernanceOpts memory governanceOpts, uint256 hostIndex ) private view returns (bool isHost) { if (hostIndex < governanceOpts.hosts.length && who == governanceOpts.hosts[hostIndex]) { // Validate governance opts if the host was found. _assertValidGovernanceOpts(governanceOpts); return true; } return false; } function _isPartyDao(address who) private view returns (bool isPartyDao) { return who == _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET); } // Assert that `who` is a contributor to the crowdfund. function _assertIsContributor(address who) internal view { if (_contributionsByContributor[who].length == 0) { revert OnlyContributorError(); } } // Can be called after a party has won. // Deploys and initializes a `Party` instance via the `PartyFactory` // and transfers the bought NFT to it. // After calling this, anyone can burn CF tokens on a contributor's behalf // with the `burn()` function. function _createParty( FixedGovernanceOpts memory governanceOpts, bool governanceOptsAlreadyValidated, IERC721[] memory preciousTokens, uint256[] memory preciousTokenIds ) internal returns (Party party_) { if (party != Party(payable(0))) { revert PartyAlreadyExistsError(party); } // If the governance opts haven't already been validated, make sure that // it hasn't been tampered with. if (!governanceOptsAlreadyValidated) { _assertValidGovernanceOpts(governanceOpts); } // Create a party. party = party_ = _getPartyFactory().createParty( address(this), Party.PartyOptions({ name: name, symbol: symbol, // Indicates to the party to use the same customization preset as the crowdfund. customizationPresetId: 0, governance: PartyGovernance.GovernanceOpts({ hosts: governanceOpts.hosts, voteDuration: governanceOpts.voteDuration, executionDelay: governanceOpts.executionDelay, passThresholdBps: governanceOpts.passThresholdBps, totalVotingPower: _getFinalPrice().safeCastUint256ToUint96(), feeBps: governanceOpts.feeBps, feeRecipient: governanceOpts.feeRecipient }) }), preciousTokens, preciousTokenIds ); // Transfer the acquired NFTs to the new party. for (uint256 i; i < preciousTokens.length; ++i) { preciousTokens[i].transferFrom(address(this), address(party_), preciousTokenIds[i]); } } // Overloaded single token wrapper for _createParty() function _createParty( FixedGovernanceOpts memory governanceOpts, bool governanceOptsAlreadyValidated, IERC721 preciousToken, uint256 preciousTokenId ) internal returns (Party party_) { IERC721[] memory tokens = new IERC721[](1); tokens[0] = preciousToken; uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = preciousTokenId; return _createParty(governanceOpts, governanceOptsAlreadyValidated, tokens, tokenIds); } // Assert that the hash of `opts` matches the hash this crowdfund was initialized with. function _assertValidGovernanceOpts(FixedGovernanceOpts memory governanceOpts) private view { bytes32 governanceOptsHash_ = _hashFixedGovernanceOpts(governanceOpts); if (governanceOptsHash_ != governanceOptsHash) { revert InvalidGovernanceOptionsError(); } } function _hashFixedGovernanceOpts( FixedGovernanceOpts memory opts ) internal pure returns (bytes32 h) { // Hash in place. assembly { // Replace the address[] hosts field with its hash temporarily. let oldHostsFieldValue := mload(opts) mstore( opts, keccak256(add(oldHostsFieldValue, 0x20), mul(mload(oldHostsFieldValue), 32)) ) // Hash the entire struct. h := keccak256(opts, 0xC0) // Restore old hosts field value. mstore(opts, oldHostsFieldValue) } } function _getFinalContribution( address contributor ) internal view returns (uint256 ethUsed, uint256 ethOwed, uint256 votingPower) { uint256 totalEthUsed = _getFinalPrice(); { Contribution[] memory contributions = _contributionsByContributor[contributor]; uint256 numContributions = contributions.length; for (uint256 i; i < numContributions; ++i) { Contribution memory c = contributions[i]; if (c.previousTotalContributions >= totalEthUsed) { // This entire contribution was not used. ethOwed += c.amount; } else if (c.previousTotalContributions + c.amount <= totalEthUsed) { // This entire contribution was used. ethUsed += c.amount; } else { // This contribution was partially used. uint256 partialEthUsed = totalEthUsed - c.previousTotalContributions; ethUsed += partialEthUsed; ethOwed = c.amount - partialEthUsed; } } } // one SLOAD with optimizer on address splitRecipient_ = splitRecipient; uint256 splitBps_ = splitBps; if (splitRecipient_ == address(0)) { splitBps_ = 0; } votingPower = ((1e4 - splitBps_) * ethUsed) / 1e4; if (splitRecipient_ == contributor) { // Split recipient is also the contributor so just add the split // voting power. votingPower += (splitBps_ * totalEthUsed + (1e4 - 1)) / 1e4; // round up } } function _setDelegate(address contributor, address delegate) private { if (delegate == address(0)) revert InvalidDelegateError(); // Only need to update delegate if there was a change. address oldDelegate = delegationsByContributor[contributor]; if (oldDelegate == delegate) return; // Only allow setting delegate on another's behalf if the delegate is unset. if (msg.sender != contributor && oldDelegate != address(0)) return; // Update delegate. delegationsByContributor[contributor] = delegate; } /// @dev While it is not necessary to pass in `delegate` to this because the /// function does not require it, it is here to emit in the /// `Contribute` event so that the PartyBid frontend can access it more /// easily. function _contribute( address contributor, address delegate, uint96 amount, uint96 previousTotalContributions, bytes memory gateData ) private { if (contributor == address(this)) revert InvalidContributorError(); if (amount == 0) return; // Must not be blocked by gatekeeper. { IGateKeeper _gateKeeper = gateKeeper; if (_gateKeeper != IGateKeeper(address(0))) { if (!_gateKeeper.isAllowed(msg.sender, gateKeeperId, gateData)) { revert NotAllowedByGateKeeperError( msg.sender, _gateKeeper, gateKeeperId, gateData ); } } } // Only allow contributions while the crowdfund is active. { CrowdfundLifecycle lc = getCrowdfundLifecycle(); if (lc != CrowdfundLifecycle.Active) { revert WrongLifecycleError(lc); } } // Increase total contributions. totalContributions += amount; // Create contributions entry for this contributor. Contribution[] storage contributions = _contributionsByContributor[contributor]; uint256 numContributions = contributions.length; uint96 ethContributed; for (uint256 i; i < numContributions; ++i) { ethContributed += contributions[i].amount; } // Check contribution is greater than minimum contribution. if (ethContributed + amount < minContribution) { revert BelowMinimumContributionsError(ethContributed + amount, minContribution); } // Check contribution is less than maximum contribution. if (ethContributed + amount > maxContribution) { revert AboveMaximumContributionsError(ethContributed + amount, maxContribution); } emit Contributed(msg.sender, contributor, amount, delegate, previousTotalContributions); if (numContributions >= 1) { Contribution memory lastContribution = contributions[numContributions - 1]; // If no one else (other than this contributor) has contributed since, // we can just reuse this contributor's last entry. uint256 totalContributionsAmountForReuse = lastContribution.previousTotalContributions + lastContribution.amount; if (totalContributionsAmountForReuse == previousTotalContributions) { lastContribution.amount += amount; contributions[numContributions - 1] = lastContribution; return; } } // Add a new contribution entry. contributions.push( Contribution({ previousTotalContributions: previousTotalContributions, amount: amount }) ); // Mint a participation NFT if this is their first contribution. if (numContributions == 0) { _mint(contributor); } } function _burn(address payable contributor, CrowdfundLifecycle lc, Party party_) private { // If the CF has won, a party must have been created prior. if (lc == CrowdfundLifecycle.Won) { if (party_ == Party(payable(0))) { revert NoPartyError(); } } else if (lc != CrowdfundLifecycle.Lost) { // Otherwise it must have lost. revert WrongLifecycleError(lc); } // Split recipient can burn even if they don't have a token. { address splitRecipient_ = splitRecipient; if (contributor == splitRecipient_) { if (_splitRecipientHasBurned) { revert SplitRecipientAlreadyBurnedError(); } _splitRecipientHasBurned = true; } // Revert if already burned or does not exist. if (splitRecipient_ != contributor || _doesTokenExistFor(contributor)) { CrowdfundNFT._burn(contributor); } } // Compute the contributions used and owed to the contributor, along // with the voting power they'll have in the governance stage. (uint256 ethUsed, uint256 ethOwed, uint256 votingPower) = _getFinalContribution( contributor ); if (votingPower > 0) { // Get the address to delegate voting power to. If null, delegate to self. address delegate = delegationsByContributor[contributor]; if (delegate == address(0)) { // Delegate can be unset for the split recipient if they never // contribute. Self-delegate if this occurs. delegate = contributor; } // Mint governance NFT for the contributor. try party_.mint(contributor, votingPower, delegate) returns (uint256) { // OK } catch { // Mint to the crowdfund itself to escrow for contributor to // come claim later on. uint256 tokenId = party_.mint(address(this), votingPower, delegate); claims[contributor].governanceTokenId = tokenId; } } // Refund any ETH owed back to the contributor. (bool s, ) = contributor.call{ value: ethOwed }(""); if (!s) { // If the transfer fails, the contributor can still come claim it // from the crowdfund. claims[contributor].refund = ethOwed; } emit Burned(contributor, ethUsed, ethOwed, votingPower); } function _getPartyFactory() internal view returns (IPartyFactory) { return IPartyFactory(_GLOBALS.getAddress(LibGlobals.GLOBAL_PARTY_FACTORY)); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.17; import "../tokens/IERC721.sol"; import "../utils/ReadOnlyDelegateCall.sol"; import "../utils/EIP165.sol"; import "../globals/IGlobals.sol"; import "../globals/LibGlobals.sol"; import "../renderers/RendererStorage.sol"; /// @notice NFT functionality for crowdfund types. This NFT is soulbound and read-only. contract CrowdfundNFT is IERC721, EIP165, ReadOnlyDelegateCall { error AlreadyMintedError(address owner, uint256 tokenId); error AlreadyBurnedError(address owner, uint256 tokenId); error InvalidTokenError(uint256 tokenId); error InvalidAddressError(); // The `Globals` contract storing global configuration values. This contract // is immutable and it’s address will never change. IGlobals private immutable _GLOBALS; /// @notice The name of the crowdfund. This will also carry over to the /// governance party. string public name; /// @notice The token symbol for the crowdfund. This will also carry over to /// the governance party. string public symbol; mapping(uint256 => address) private _owners; modifier alwaysRevert() { revert("ALWAYS FAILING"); _; // Compiler requires this. } // Set the `Globals` contract. constructor(IGlobals globals) { _GLOBALS = globals; } // Initialize name and symbol for crowdfund. function _initialize( string memory name_, string memory symbol_, uint256 customizationPresetId ) internal virtual { name = name_; symbol = symbol_; if (customizationPresetId != 0) { RendererStorage(_GLOBALS.getAddress(LibGlobals.GLOBAL_RENDERER_STORAGE)) .useCustomizationPreset(customizationPresetId); } } /// @notice DO NOT CALL. This is a soulbound NFT and cannot be transferred. /// Attempting to call this function will always fail. function transferFrom(address, address, uint256) external pure alwaysRevert {} /// @notice DO NOT CALL. This is a soulbound NFT and cannot be transferred. /// Attempting to call this function will always fail. function safeTransferFrom(address, address, uint256) external pure alwaysRevert {} /// @notice DO NOT CALL. This is a soulbound NFT and cannot be transferred. /// Attempting to call this function will always fail. function safeTransferFrom( address, address, uint256, bytes calldata ) external pure alwaysRevert {} /// @notice DO NOT CALL. This is a soulbound NFT and cannot be transferred. /// Attempting to call this function will always fail. function approve(address, uint256) external pure alwaysRevert {} /// @notice DO NOT CALL. This is a soulbound NFT and cannot be transferred. /// Attempting to call this function will always fail. function setApprovalForAll(address, bool) external pure alwaysRevert {} /// @notice This is a soulbound NFT and cannot be transferred. /// Attempting to call this function will always return null. function getApproved(uint256) external pure returns (address) { return address(0); } /// @notice This is a soulbound NFT and cannot be transferred. /// Attempting to call this function will always return false. function isApprovedForAll(address, address) external pure returns (bool) { return false; } /// @inheritdoc EIP165 function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { return super.supportsInterface(interfaceId) || // ERC721 interface ID interfaceId == 0x80ac58cd; } /// @notice Returns a URI to render the NFT. function tokenURI(uint256) external view returns (string memory) { return _delegateToRenderer(); } /// @notice Returns a URI for the storefront-level metadata for your contract. function contractURI() external view returns (string memory) { return _delegateToRenderer(); } /// @inheritdoc IERC721 function ownerOf(uint256 tokenId) external view returns (address owner) { owner = _owners[tokenId]; if (owner == address(0)) { revert InvalidTokenError(tokenId); } } /// @inheritdoc IERC721 function balanceOf(address owner) external view returns (uint256 numTokens) { return _doesTokenExistFor(owner) ? 1 : 0; } function _doesTokenExistFor(address owner) internal view returns (bool) { return _owners[uint256(uint160(owner))] != address(0); } function _mint(address owner) internal returns (uint256 tokenId) { if (owner == address(0)) revert InvalidAddressError(); tokenId = uint256(uint160(owner)); if (_owners[tokenId] != owner) { _owners[tokenId] = owner; emit Transfer(address(0), owner, tokenId); } else { revert AlreadyMintedError(owner, tokenId); } } function _burn(address owner) internal { uint256 tokenId = uint256(uint160(owner)); if (_owners[tokenId] == owner) { _owners[tokenId] = address(0); emit Transfer(owner, address(0), tokenId); return; } revert AlreadyBurnedError(owner, tokenId); } function _delegateToRenderer() private view returns (string memory) { _readOnlyDelegateCall( // Instance of IERC721Renderer. _GLOBALS.getAddress(LibGlobals.GLOBAL_CF_NFT_RENDER_IMPL), msg.data ); assert(false); // Will not be reached. return ""; } }
// 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); }
// 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); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.17; // Interface for a gatekeeper contract used for private crowdfund instances. interface IGateKeeper { /// @notice Check if a participant is eligible to participate in a crowdfund. /// @param participant The address of the participant. /// @param id The ID of the gate to eligibility against. /// @param userData The data used to check eligibility. /// @return `true` if the participant is allowed to participate, `false` otherwise. function isAllowed( address participant, bytes12 id, bytes memory userData ) external view returns (bool); }
// 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 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; }
// 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; }
// 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); }
// 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 {} }
// 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); } } }
// 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. } }
// 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; }
// 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; } }
// 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 } } }
// 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; } }
// 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; } }
// 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; } }
// 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); }
// 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); }
// 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); }
// 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); }
// 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; } }
// 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(); } _; } }
// 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); } } }
// 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); } }
// 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)) } } }
// 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); } }
// 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)); } }
// 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(); } } } }
// 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(); } } }
// 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; } }
// 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; } }
// 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} * * _Available since v4.7._ */ function verifyCalldata( bytes32[] calldata proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} * * _Available since v4.7._ */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * _Available since v4.7._ */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { return hashes[totalHashes - 1]; } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { return hashes[totalHashes - 1]; } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// 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); }
// 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) } } }
{ "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/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
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IGlobals","name":"globals","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint96","name":"contributions","type":"uint96"},{"internalType":"uint96","name":"maxContributions","type":"uint96"}],"name":"AboveMaximumContributionsError","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AlreadyBurnedError","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AlreadyMintedError","type":"error"},{"inputs":[{"internalType":"uint96","name":"contributions","type":"uint96"},{"internalType":"uint96","name":"minContributions","type":"uint96"}],"name":"BelowMinimumContributionsError","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"CallProhibitedError","type":"error"},{"inputs":[],"name":"ContributionsSpentForFailedBuyError","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"errData","type":"bytes"}],"name":"EthTransferFailed","type":"error"},{"inputs":[{"internalType":"uint96","name":"value","type":"uint96"},{"internalType":"uint96","name":"totalContributions","type":"uint96"}],"name":"ExceedsTotalContributionsError","type":"error"},{"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"FailedToBuyNFTError","type":"error"},{"inputs":[],"name":"InvalidAddressError","type":"error"},{"inputs":[{"internalType":"uint16","name":"bps","type":"uint16"}],"name":"InvalidBpsError","type":"error"},{"inputs":[],"name":"InvalidContributorError","type":"error"},{"inputs":[],"name":"InvalidDelegateError","type":"error"},{"inputs":[],"name":"InvalidGovernanceOptionsError","type":"error"},{"inputs":[{"internalType":"uint256","name":"minTokensBought","type":"uint256"}],"name":"InvalidMinTokensBoughtError","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"InvalidTokenError","type":"error"},{"inputs":[],"name":"InvalidTokenIdError","type":"error"},{"inputs":[{"internalType":"uint96","name":"callValue","type":"uint96"},{"internalType":"uint96","name":"maximumPrice","type":"uint96"}],"name":"MaximumPriceError","type":"error"},{"inputs":[],"name":"MismatchedCallArgLengthsError","type":"error"},{"inputs":[],"name":"MissingHostsError","type":"error"},{"inputs":[],"name":"NoContributionsError","type":"error"},{"inputs":[],"name":"NoPartyError","type":"error"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"},{"internalType":"contract IGateKeeper","name":"gateKeeper","type":"address"},{"internalType":"bytes12","name":"gateKeeperId","type":"bytes12"},{"internalType":"bytes","name":"gateData","type":"bytes"}],"name":"NotAllowedByGateKeeperError","type":"error"},{"inputs":[{"internalType":"uint256","name":"ethUsed","type":"uint256"},{"internalType":"uint256","name":"minTotalEthUsed","type":"uint256"}],"name":"NotEnoughEthUsedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokensBought","type":"uint256"},{"internalType":"uint256","name":"minTokensBought","type":"uint256"}],"name":"NotEnoughTokensBoughtError","type":"error"},{"inputs":[],"name":"NothingBoughtError","type":"error"},{"inputs":[],"name":"NothingToClaimError","type":"error"},{"inputs":[],"name":"OnlyConstructorError","type":"error"},{"inputs":[],"name":"OnlyContributorError","type":"error"},{"inputs":[],"name":"OnlyDelegateCallError","type":"error"},{"inputs":[{"internalType":"address","name":"notDao","type":"address"}],"name":"OnlyPartyDaoError","type":"error"},{"inputs":[{"internalType":"address","name":"notDao","type":"address"}],"name":"OnlyPartyDaoOrHostError","type":"error"},{"inputs":[],"name":"OnlyPartyHostError","type":"error"},{"inputs":[],"name":"OnlyWhenEmergencyActionsAllowedError","type":"error"},{"inputs":[{"internalType":"contract Party","name":"party","type":"address"}],"name":"PartyAlreadyExistsError","type":"error"},{"inputs":[],"name":"SplitRecipientAlreadyBurnedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"v","type":"uint256"}],"name":"Uint256ToUint96CastOutOfRange","type":"error"},{"inputs":[{"internalType":"enum Crowdfund.CrowdfundLifecycle","name":"lc","type":"uint8"}],"name":"WrongLifecycleError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethOwed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"votingPower","type":"uint256"}],"name":"Burned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousTotalContributions","type":"uint256"}],"name":"Contributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"amountEth","type":"uint256"}],"name":"EmergencyExecute","type":"event"},{"anonymous":false,"inputs":[],"name":"EmergencyExecuteDisabled","type":"event"},{"anonymous":false,"inputs":[],"name":"Lost","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract Party","name":"party","type":"address"},{"indexed":false,"internalType":"contract IERC721[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"settledPrice","type":"uint256"}],"name":"Won","type":"event"},{"inputs":[],"name":"IMPL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"contributor","type":"address"}],"name":"activateOrRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"numTokens","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable[]","name":"contributors","type":"address[]"},{"internalType":"bool","name":"revertOnFailure","type":"bool"}],"name":"batchActivateOrRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable[]","name":"contributors","type":"address[]"},{"internalType":"bool","name":"revertOnFailure","type":"bool"}],"name":"batchBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address payable[]","name":"callTargets","type":"address[]"},{"internalType":"uint96[]","name":"callValues","type":"uint96[]"},{"internalType":"bytes[]","name":"callDatas","type":"bytes[]"},{"internalType":"bytes32[][]","name":"proofs","type":"bytes32[][]"},{"internalType":"uint256","name":"minTokensBought","type":"uint256"},{"internalType":"uint256","name":"minTotalEthUsed","type":"uint256"},{"components":[{"internalType":"address[]","name":"hosts","type":"address[]"},{"internalType":"uint40","name":"voteDuration","type":"uint40"},{"internalType":"uint40","name":"executionDelay","type":"uint40"},{"internalType":"uint16","name":"passThresholdBps","type":"uint16"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"address payable","name":"feeRecipient","type":"address"}],"internalType":"struct Crowdfund.FixedGovernanceOpts","name":"governanceOpts","type":"tuple"},{"internalType":"uint256","name":"hostIndex","type":"uint256"}],"internalType":"struct CollectionBatchBuyCrowdfund.BatchBuyArgs","name":"args","type":"tuple"}],"name":"batchBuy","outputs":[{"internalType":"contract Party","name":"party_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"address[]","name":"initialDelegates","type":"address[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes[]","name":"gateDatas","type":"bytes[]"},{"internalType":"bool","name":"revertOnFailure","type":"bool"}],"name":"batchContributeFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address payable","name":"contributor","type":"address"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"receiver","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claims","outputs":[{"internalType":"uint256","name":"refund","type":"uint256"},{"internalType":"uint256","name":"governanceTokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegate","type":"address"},{"internalType":"bytes","name":"gateData","type":"bytes"}],"name":"contribute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"initialDelegate","type":"address"},{"internalType":"bytes","name":"gateData","type":"bytes"}],"name":"contributeFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"impl","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"name":"delegateCallAndRevert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegationsByContributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"hosts","type":"address[]"},{"internalType":"uint40","name":"voteDuration","type":"uint40"},{"internalType":"uint40","name":"executionDelay","type":"uint40"},{"internalType":"uint16","name":"passThresholdBps","type":"uint16"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"address payable","name":"feeRecipient","type":"address"}],"internalType":"struct Crowdfund.FixedGovernanceOpts","name":"governanceOpts","type":"tuple"},{"internalType":"uint256","name":"hostIndex","type":"uint256"}],"name":"disableEmergencyExecute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"targetCallData","type":"bytes"},{"internalType":"uint256","name":"amountEth","type":"uint256"}],"name":"emergencyExecute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"emergencyExecuteDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expiry","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gateKeeper","outputs":[{"internalType":"contract IGateKeeper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gateKeeperId","outputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"getContributorInfo","outputs":[{"internalType":"uint256","name":"ethContributed","type":"uint256"},{"internalType":"uint256","name":"ethUsed","type":"uint256"},{"internalType":"uint256","name":"ethOwed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCrowdfundLifecycle","outputs":[{"internalType":"enum Crowdfund.CrowdfundLifecycle","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceOptsHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"internalType":"contract IERC721","name":"nftContract","type":"address"},{"internalType":"bytes32","name":"nftTokenIdsMerkleRoot","type":"bytes32"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"uint96","name":"maximumPrice","type":"uint96"},{"internalType":"address payable","name":"splitRecipient","type":"address"},{"internalType":"uint16","name":"splitBps","type":"uint16"},{"internalType":"address","name":"initialContributor","type":"address"},{"internalType":"address","name":"initialDelegate","type":"address"},{"internalType":"uint96","name":"minContribution","type":"uint96"},{"internalType":"uint96","name":"maxContribution","type":"uint96"},{"internalType":"contract IGateKeeper","name":"gateKeeper","type":"address"},{"internalType":"bytes12","name":"gateKeeperId","type":"bytes12"},{"components":[{"internalType":"address[]","name":"hosts","type":"address[]"},{"internalType":"uint40","name":"voteDuration","type":"uint40"},{"internalType":"uint40","name":"executionDelay","type":"uint40"},{"internalType":"uint16","name":"passThresholdBps","type":"uint16"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"address payable","name":"feeRecipient","type":"address"}],"internalType":"struct Crowdfund.FixedGovernanceOpts","name":"governanceOpts","type":"tuple"}],"internalType":"struct CollectionBatchBuyCrowdfund.CollectionBatchBuyCrowdfundOptions","name":"opts","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"maxContribution","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumPrice","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minContribution","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftContract","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftTokenIdsMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"party","outputs":[{"internalType":"contract Party","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"settledPrice","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"splitBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"splitRecipient","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalContributions","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"pure","type":"function"}]
Contract Creation Code
60e06040523480156200001157600080fd5b5060405162004c3f38038062004c3f83398101604081905262000034916200004f565b306080526001600160a01b031660a081905260c05262000081565b6000602082840312156200006257600080fd5b81516001600160a01b03811681146200007a57600080fd5b9392505050565b60805160a05160c051614b5d620000e260003960008181612268015261353e015260008181612ac101526135e701526000818161062701528181610b9501528181610d1301528181610e670152818161102801526115220152614b5d6000f3fe6080604052600436106102935760003560e01c80636352211e1161015a578063bf1bdcf6116100c1578063e43816501161007a578063e4381650146108a6578063e6f26d9a146108bc578063e8a3d485146108dd578063e985e9c5146108f2578063ec6d3f3714610915578063f401a8b61461093c57600080fd5b8063bf1bdcf6146107a2578063c2c6a183146107c2578063c6788bdd146107e2578063c87b56dd1461082b578063d56d229d1461084b578063e184c9be1461086b57600080fd5b80638d3d6576116101135780638d3d6576146106fd57806395d89b4114610724578063a22cb46514610739578063aaffadf314610754578063ae1dd09914610774578063b88d4fde1461078757600080fd5b80636352211e1461064957806367a5e4261461066957806370a082311461067c5780637ad21b2e146106aa5780638433e7f3146106bd57806389afcb44146106dd57600080fd5b806323e45f62116101fe5780633e4011b9116101b75780633e4011b914610595578063419f1992146105b557806342842e0e146104a65780634367a029146105d557806345d61ded146105f557806356973ee51461061557600080fd5b806323e45f62146104c1578063305a0721146104f957806332156f1e1461050c578063354284f21461052c57806337c089231461054c5780633d2f3caa1461057357600080fd5b806314b182f91161025057806314b182f9146103bf578063150b7a02146103f55780631840f5c2146104395780631a8ae87d1461044c5780631e83409a1461048657806323b872dd146104a657600080fd5b806301ffc9a714610298578063048d372c146102cd57806306fdde0314610302578063081812fc14610324578063095ea7b31461035d5780630bce8cdf1461037f575b600080fd5b3480156102a457600080fd5b506102b86102b33660046136f2565b610952565b60405190151581526020015b60405180910390f35b3480156102d957600080fd5b506005546102ef90600160a01b900461ffff1681565b60405161ffff90911681526020016102c4565b34801561030e57600080fd5b50610317610972565b6040516102c4919061376c565b34801561033057600080fd5b5061034561033f36600461377f565b50600090565b6040516001600160a01b0390911681526020016102c4565b34801561036957600080fd5b5061037d6103783660046137b8565b610a00565b005b34801561038b57600080fd5b5061039f61039a3660046137e4565b610a3e565b6040805194855260208501939093529183015260608201526080016102c4565b3480156103cb57600080fd5b506103456103da3660046137e4565b6007602052600090815260409020546001600160a01b031681565b34801561040157600080fd5b50610420610410366004613910565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020016102c4565b61037d61044736600461397b565b610b8b565b34801561045857600080fd5b5060045461046d90600160a01b900460a01b81565b6040516001600160a01b031990911681526020016102c4565b34801561049257600080fd5b5061037d6104a13660046137e4565b610c09565b3480156104b257600080fd5b5061037d6103783660046139ca565b3480156104cd57600080fd5b50600b546104e1906001600160601b031681565b6040516001600160601b0390911681526020016102c4565b61037d610507366004613a53565b610d09565b34801561051857600080fd5b5061037d610527366004613c23565b610e5d565b34801561053857600080fd5b50600354610345906001600160a01b031681565b34801561055857600080fd5b506003546104e190600160a01b90046001600160601b031681565b34801561057f57600080fd5b50610588610f24565b6040516102c49190613c7d565b3480156105a157600080fd5b5061037d6105b036600461397b565b610f80565b3480156105c157600080fd5b506103456105d0366004613f3c565b61101c565b3480156105e157600080fd5b50600554610345906001600160a01b031681565b34801561060157600080fd5b50600454610345906001600160a01b031681565b34801561062157600080fd5b506103457f000000000000000000000000000000000000000000000000000000000000000081565b34801561065557600080fd5b5061034561066436600461377f565b6114df565b61037d61067736600461407e565b611518565b34801561068857600080fd5b5061069c6106973660046137e4565b61157e565b6040519081526020016102c4565b61037d6106b83660046140ed565b6115b2565b3480156106c957600080fd5b5061037d6106d83660046137e4565b6116ff565b3480156106e957600080fd5b5061037d6106f83660046137e4565b61170b565b34801561070957600080fd5b50600a546104e190600160601b90046001600160601b031681565b34801561073057600080fd5b50610317611728565b34801561074557600080fd5b5061037d6103783660046141ae565b34801561076057600080fd5b50600a546104e1906001600160601b031681565b61037d6107823660046141ff565b611735565b34801561079357600080fd5b5061037d61037836600461438e565b3480156107ae57600080fd5b5061037d6107bd366004614400565b6118a4565b3480156107ce57600080fd5b5061037d6107dd366004614400565b61199f565b3480156107ee57600080fd5b506108166107fd3660046137e4565b6009602052600090815260409020805460019091015482565b604080519283526020830191909152016102c4565b34801561083757600080fd5b5061031761084636600461377f565b6119aa565b34801561085757600080fd5b50600c54610345906001600160a01b031681565b34801561087757600080fd5b50600a5461089090600160c81b900464ffffffffff1681565b60405164ffffffffff90911681526020016102c4565b3480156108b257600080fd5b5061069c600d5481565b3480156108c857600080fd5b50600a546102b890600160c01b900460ff1681565b3480156108e957600080fd5b506103176119b4565b3480156108fe57600080fd5b506102b861090d366004614485565b600092915050565b34801561092157600080fd5b50600b546104e190600160601b90046001600160601b031681565b34801561094857600080fd5b5061069c60065481565b600061095d826119be565b8061096c575061096c826119f4565b92915050565b6000805461097f906144b3565b80601f01602080910402602001604051908101604052809291908181526020018280546109ab906144b3565b80156109f85780601f106109cd576101008083540402835291602001916109f8565b820191906000526020600020905b8154815290600101906020018083116109db57829003601f168201915b505050505081565b60405162461bcd60e51b815260206004820152600e60248201526d414c57415953204641494c494e4760901b60448201526064015b60405180910390fd5b6000806000806000610a4e610f24565b90506005816005811115610a6457610a64613c67565b1480610a8157506004816005811115610a7f57610a7f613c67565b145b15610aa757610a8f86611a1c565b91955093509150610aa08385614503565b9450610b83565b6001600160a01b038616600090815260086020908152604080832080548251818502810185019093528083529192909190849084015b82821015610b2657600084815260209081902060408051808201909152908401546001600160601b038082168352600160601b9091041681830152825260019092019101610add565b5050825192935060009150505b81811015610b7f57828181518110610b4d57610b4d614516565b6020026020010151602001516001600160601b031688610b6d9190614503565b9750610b788161452c565b9050610b33565b5050505b509193509193565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003610bd45760405163ea2cbbd560e01b815260040160405180910390fd5b610bde3383611c53565b610c053383610bec34611cfe565b600354600160a01b90046001600160601b031685611d2f565b5050565b33600081815260096020818152604080842081518083019092528054825260018101805483850152958552929091529082905591558051158015610c4f57506020810151155b15610c6d5760405163f76ce33f60e01b815260040160405180910390fd5b805115610c8a578051610c8a906001600160a01b038416906121c9565b602081015115610c05576003546020820151604051632142170760e11b81523060048201526001600160a01b03858116602483015260448201929092529116906342842e0e90606401600060405180830381600087803b158015610ced57600080fd5b505af1158015610d01573d6000803e3d6000fd5b505050505050565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003610d525760405163ea2cbbd560e01b815260040160405180910390fd5b610d5b3361224f565b610d7a57604051639f3df03d60e01b8152336004820152602401610a35565b600a54600160c01b900460ff1615610da5576040516304e71fd360e31b815260040160405180910390fd5b600080856001600160a01b0316838686604051610dc3929190614545565b60006040518083038185875af1925050503d8060008114610e00576040519150601f19603f3d011682016040523d82523d6000602084013e610e05565b606091505b509150915081610e1857610e18816122f6565b7f66cf5924183d7d44caa75d9268a169d7c1422fef43848743d5e1ba32d5833acf86868686604051610e4d9493929190614555565b60405180910390a1505050505050565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003610ea65760405163ea2cbbd560e01b815260040160405180910390fd5b610eb13383836122fe565b158015610ec45750610ec23361224f565b155b15610ee457604051636b7bc65960e01b8152336004820152602401610a35565b600a805460ff60c01b1916600160c01b1790556040517f447e75484d6bdb571b4a92ae14018db7b6dd41f3f02360690c554e1d48f46f8790600090a15050565b600b54600090600160601b90046001600160601b031615610f5d576003546001600160a01b0316610f555750600390565b60055b905090565b600a54600160c81b900464ffffffffff164210610f7a5750600490565b50600190565b333014610f8c57600080fd5b600080836001600160a01b031683604051610fa7919061459f565b600060405180830381855af49150503d8060008114610fe2576040519150601f19603f3d011682016040523d82523d6000602084013e610fe7565b606091505b509150915061101682826040516020016110029291906145bb565b6040516020818303038152906040526122f6565b50505050565b60006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001630036110675760405163ea2cbbd560e01b815260040160405180910390fd5b61107b338360e0015184610100015161235f565b6000611085610f24565b9050600181600581111561109b5761109b613c67565b146110bb5780604051638847aaed60e01b8152600401610a359190613c7d565b508160a001516000036110e457604051635182e6c160e01b815260006004820152602401610a35565b602082015151825151141580611101575060408201515182515114155b80611113575060608201515182515114155b80611125575060808201515182515114155b1561114357604051636bec2bf560e11b815260040160405180910390fd5b600b8054600160601b600160c01b0319166bffffffffffffffffffffffff60601b179055815151600090819081906001600160401b0381111561118857611188613801565b6040519080825280602002602001820160405280156111b1578160200160208202803683370190505b50600c54600d549192506001600160a01b03169060005b87515181101561140e57811561121d5761121d886000015182815181106111f1576111f1614516565b6020026020010151838a60800151848151811061121057611210614516565b6020026020010151612387565b60004790506000806112a6868c60000151868151811061123f5761123f614516565b60200260200101518d60200151878151811061125d5761125d614516565b60200260200101518e60400151888151811061127b5761127b614516565b60200260200101518f60600151898151811061129957611299614516565b60200260200101516123b8565b9150915081611345578a515160a08c01511061131d578051156112d1576112cc816122f6565b611345565b858b6000015185815181106112e8576112e8614516565b602090810291909101015160405163ae38c3b560e01b81526001600160a01b0390921660048301526024820152604401610a35565b82471461133d57604051639cf3222560e01b815260040160405180910390fd5b5050506113fe565b8a60400151848151811061135b5761135b614516565b60200260200101518961136e91906145de565b98506113798861452c565b9750858761138860018b614605565b8151811061139857611398614516565b6001600160a01b03909216602092830291909101909101528a518051859081106113c4576113c4614516565b60200260200101518b6000015160018a6113de9190614605565b815181106113ee576113ee614516565b6020026020010181815250505050505b6114078161452c565b90506111c8565b50846001600160601b031660000361143957604051632219363960e11b815260040160405180910390fd5b8660a0015184101561146e5760a0870151604051635731934f60e01b8152610a35918691600401918252602082015260400190565b8660c00151856001600160601b031610156114b45760c087015160405163780edab360e11b81526001600160601b03871660048201526024810191909152604401610a35565b838352836101a0526114d2838860000151878a60e0015160016124ea565b955050505050505b919050565b6000818152600260205260409020546001600160a01b0316806114da57604051632abfe32760e11b815260048101839052602401610a35565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001630036115615760405163ea2cbbd560e01b815260040160405180910390fd5b61156b8383611c53565b6115798383610bec34611cfe565b505050565b6001600160a01b038082166000908152600260205260408120549091166115a65760006115a9565b60015b60ff1692915050565b60005b8551811015610d0157600080306001600160a01b03168684815181106115dd576115dd614516565b6020026020010151306001600160a01b03166367a5e4268b878151811061160657611606614516565b60200260200101518b888151811061162057611620614516565b60200260200101518a898151811061163a5761163a614516565b602002602001015160405160240161165493929190614618565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b93909317909252905161168c925061459f565b60006040518083038185875af1925050503d80600081146116c9576040519150601f19603f3d011682016040523d82523d6000602084013e6116ce565b606091505b50915091508380156116de575081155b156116ec576116ec816122f6565b5050806116f89061452c565b90506115b5565b6117088161170b565b50565b61170881611717610f24565b6003546001600160a01b0316612622565b6001805461097f906144b3565b303b15611755576040516304a41c4d60e51b815260040160405180910390fd5b6101e0810151515160000361177d576040516362dc091b60e01b815260040160405180910390fd5b611877604051806101c001604052808360000151815260200183602001518152602001836040015181526020018360a0015164ffffffffff1681526020018360c001516001600160601b031681526020018360e001516001600160a01b0316815260200183610100015161ffff1681526020018361012001516001600160a01b031681526020018361014001516001600160a01b031681526020018361016001516001600160601b031681526020018361018001516001600160601b03168152602001836101a001516001600160a01b03168152602001836101c001516001600160a01b0319168152602001836101e00151815250612964565b6060810151600c80546001600160a01b0319166001600160a01b0390921691909117905560800151600d55565b60005b828110156110165760008030806389afcb448888878181106118cb576118cb614516565b90506020020160208101906118e091906137e4565b6040516001600160a01b03909116602482015260440160408051601f198184030181529181526020820180516001600160e01b031660e09490941b93909317909252905161192e925061459f565b600060405180830381855af49150503d8060008114611969576040519150601f19603f3d011682016040523d82523d6000602084013e61196e565b606091505b509150915083801561197e575081155b1561198c5761198c816122f6565b5050806119989061452c565b90506118a7565b6115798383836118a4565b606061096c612a9c565b6060610f58612a9c565b60006301ffc9a760e01b6001600160e01b03198316148061096c57506001600160e01b03198216630a85bd0160e11b1492915050565b60006119ff826119be565b8061096c5750506001600160e01b0319166380ac58cd60e01b1490565b600080600080611a3c600b546001600160601b03600160601b9091041690565b6001600160a01b038616600090815260086020908152604080832080548251818502810185019093528083529495509293909291849084015b82821015611abe57600084815260209081902060408051808201909152908401546001600160601b038082168352600160601b9091041681830152825260019092019101611a75565b5050825192935060009150505b81811015611bb7576000838281518110611ae757611ae7614516565b602002602001015190508481600001516001600160601b031610611b24576020810151611b1d906001600160601b031688614503565b9650611ba6565b602081015181518691611b36916145de565b6001600160601b031611611b63576020810151611b5c906001600160601b031689614503565b9750611ba6565b8051600090611b7b906001600160601b031687614605565b9050611b87818a614503565b98508082602001516001600160601b0316611ba29190614605565b9750505b50611bb08161452c565b9050611acb565b50506005546001600160a01b0381169150600160a01b900461ffff1681611bdc575060005b61271086611bea8383614605565b611bf49190614644565b611bfe919061465b565b9350866001600160a01b0316826001600160a01b031603611c4957612710611c268483614644565b611c329061270f614503565b611c3c919061465b565b611c469085614503565b93505b5050509193909250565b6001600160a01b038116611c7a5760405163e5ec2d2560e01b815260040160405180910390fd5b6001600160a01b0380831660009081526007602052604090205481169082168103611ca457505050565b336001600160a01b03841614801590611cc557506001600160a01b03811615155b15611ccf57505050565b506001600160a01b03918216600090815260076020526040902080546001600160a01b03191691909216179055565b60006001600160601b03821115611d2b57604051636f55e21560e11b815260048101839052602401610a35565b5090565b306001600160a01b03861603611d585760405163ac04f48f60e01b815260040160405180910390fd5b6001600160601b038316156121c2576004546001600160a01b03168015611e27576004805460405163267a0a0560e21b81526001600160a01b038416926399e8281492611db5923392600160a01b90920460a01b9188910161467d565b602060405180830381865afa158015611dd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df691906146b1565b611e275760048054604051633921815160e21b8152610a359233928592600160a01b90910460a01b918791016146ce565b506000611e32610f24565b90506001816005811115611e4857611e48613c67565b14611e685780604051638847aaed60e01b8152600401610a359190613c7d565b5082600360148282829054906101000a90046001600160601b0316611e8d91906145de565b82546001600160601b039182166101009390930a9283029190920219909116179055506001600160a01b038516600090815260086020526040812080549091805b82811015611f2057838181548110611ee857611ee8614516565b600091825260209091200154611f0e90600160601b90046001600160601b0316836145de565b9150611f198161452c565b9050611ece565b50600a546001600160601b0316611f3787836145de565b6001600160601b03161015611f7f57611f5086826145de565b600a54604051630352153b60e01b81526001600160601b03928316600482015291166024820152604401610a35565b600a54600160601b90046001600160601b0316611f9c87836145de565b6001600160601b03161115611fec57611fb586826145de565b600a5460405162f2e14960e61b81526001600160601b039283166004820152600160601b9091049091166024820152604401610a35565b604080513381526001600160a01b038a811660208301526001600160601b0389811683850152908a1660608301528716608082015290517f5982c40d76f99ce7f7cb2138b9af68857259da5d9da4f06ccbc884d861e8f58f9181900360a00190a16001821061215357600083612063600185614605565b8154811061207357612073614516565b60009182526020808320604080518082019091529201546001600160601b03808216808552600160601b909204169183018290529193506120b3916145de565b6001600160601b03169050866001600160601b031681036121505787826020018181516120e091906145de565b6001600160601b031690525081856120f9600187614605565b8154811061210957612109614516565b600091825260209182902083519101805493909201516001600160601b03908116600160601b026001600160c01b0319909416911617919091179055506121c29350505050565b50505b604080518082019091526001600160601b03808716825287811660208084019182528654600181018855600088815291822094519401805492518416600160601b026001600160c01b031990931694909316939093171790558290036121be576121bc88612b84565b505b5050505b5050505050565b806000036121d5575050565b600080836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612223576040519150601f19603f3d011682016040523d82523d6000602084013e612228565b606091505b50915091508161101657838160405163354db69760e01b8152600401610a35929190614714565b604051635c9fcd8560e11b8152600b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b93f9b0a90602401602060405180830381865afa1580156122b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122db9190614738565b6001600160a01b0316826001600160a01b0316149050919050565b805160208201fd5b8151516000908210801561233e5750825180518390811061232157612321614516565b60200260200101516001600160a01b0316846001600160a01b0316145b156123545761234c83612c56565b506001612358565b5060005b9392505050565b61236a8383836122fe565b61157957604051633641391960e21b815260040160405180910390fd5b60008381526020902061239b828483612c98565b6110165760405163d190acef60e01b815260040160405180910390fd5b600060606123c7858489612cae565b6123e85784836040516309a30f6560e01b8152600401610a35929190614714565b600b546001600160601b0390811690851681101561242c576040516308838d7360e21b81526001600160601b03808716600483015282166024820152604401610a35565b50600080866001600160a01b0316866001600160601b031686604051612452919061459f565b60006040518083038185875af1925050503d806000811461248f576040519150601f19603f3d011682016040523d82523d6000602084013e612494565b606091505b5091509150816124ab576000935091506124e09050565b306124bf6001600160a01b038b168a612d3f565b6001600160a01b031614604051806020016040528060008152509350935050505b9550959350505050565b6003546000906001600160601b03600160a01b909104811690851681101561253857604051635723208b60e11b81526001600160601b03808716600483015282166024820152604401610a35565b506001600160601b038416156125bc57600b8054600160601b600160c01b031916600160601b6001600160601b0387160217905561257883838888612e1f565b90507f139324ee0c9d8324da2602ed90fb064f661cd2e2498417dbd964903d19bf3c32818787876040516125af94939291906147c9565b60405180910390a1612619565b600b8054600160601b600160c01b0319169055600a805464ffffffffff60c81b1916600160c81b4264ffffffffff16021790556040517f39b8faea695f02450d8bc947229f0da7141777a2dc02ebbeda20c259acb5936390600090a15b95945050505050565b600582600581111561263657612636613c67565b03612667576001600160a01b03811661266257604051632865539760e21b815260040160405180910390fd5b61269b565b600482600581111561267b5761267b613c67565b1461269b5781604051638847aaed60e01b8152600401610a359190613c7d565b6005546001600160a01b039081169084168190036126f257600554600160b01b900460ff16156126de57604051639b0f501b60e01b815260040160405180910390fd5b6005805460ff60b01b1916600160b01b1790555b836001600160a01b0316816001600160a01b031614158061272c57506001600160a01b038085166000908152600260205260409020541615155b1561273a5761273a8461319e565b50600080600061274986611a1c565b919450925090508015612893576001600160a01b0380871660009081526007602052604090205416806127795750855b604051630d4d151360e01b81526001600160a01b038881166004830152602482018490528281166044830152861690630d4d1513906064016020604051808303816000875af19250505080156127ec575060408051601f3d908101601f191682019092526127e99181019061481a565b60015b61288f57604051630d4d151360e01b8152306004820152602481018390526001600160a01b03828116604483015260009190871690630d4d1513906064016020604051808303816000875af1158015612849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061286d919061481a565b6001600160a01b03891660009081526009602052604090206001015550612891565b505b505b6000866001600160a01b03168360405160006040518083038185875af1925050503d80600081146128e0576040519150601f19603f3d011682016040523d82523d6000602084013e6128e5565b606091505b505090508061290a576001600160a01b03871660009081526009602052604090208390555b604080516001600160a01b038916815260208101869052908101849052606081018390527f4c60206a5c1de41f3376d1d60f0949d96cb682033c90b1c2d9d9a62d4c4120c09060800160405180910390a150505050505050565b42816060015164ffffffffff1661297b9190614503565b600a60196101000a81548164ffffffffff021916908364ffffffffff1602179055508060800151600b60006101000a8154816001600160601b0302191690836001600160601b031602179055506117086040518061018001604052808360000151815260200183602001518152602001836040015181526020018360a001516001600160a01b031681526020018360c0015161ffff1681526020018360e001516001600160a01b031681526020018361010001516001600160a01b031681526020018361012001516001600160601b031681526020018361014001516001600160601b031681526020018361016001516001600160a01b031681526020018361018001516001600160a01b0319168152602001836101a00151815250613242565b604051635c9fcd8560e11b815260056004820152606090612b69906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b93f9b0a90602401602060405180830381865afa158015612b08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2c9190614738565b6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061341c92505050565b612b71614833565b5060408051602081019091526000815290565b60006001600160a01b038216612bad57604051637ac7fa7d60e01b815260040160405180910390fd5b506001600160a01b038082166000818152600260205260409020549091168114612c2b5760008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a4919050565b60405163c343b42b60e01b81526001600160a01b038316600482015260248101829052604401610a35565b6000612c748280518051602090810290820120825260c08220915290565b90506006548114610c0557604051632867dd6360e01b815260040160405180910390fd5b600082612ca585846134d8565b14949350505050565b6000306001600160a01b03851603612cc857506000612358565b816001600160a01b0316846001600160a01b0316148015612ceb57506004835110155b15612d355760208301516001600160e01b03191663095ea7b360e01b811480612d2457506001600160e01b0319811663a22cb46560e01b145b15612d33576000915050612358565b505b5060019392505050565b6000806000846001600160a01b0316856001600160a01b0316636352211e86604051602401612d7091815260200190565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b939093179092529051612da8925061459f565b600060405180830381855afa9150503d8060008114612de3576040519150601f19603f3d011682016040523d82523d6000602084013e612de8565b606091505b5091509150811580612dfb575060208151105b15612e0b5760009250505061096c565b808060200190518101906126199190614738565b6003546000906001600160a01b031615612e5b57600354604051630101411760e31b81526001600160a01b039091166004820152602401610a35565b83612e6957612e6985612c56565b612e71613525565b6001600160a01b031663e579b0fb3060405180608001604052806040518060e001604052808b6000015181526020018b6020015164ffffffffff1681526020018b6040015164ffffffffff1681526020018b6060015161ffff168152602001612ef2612eed600b546001600160601b03600160601b9091041690565b611cfe565b6001600160601b031681526020018b6080015161ffff1681526020018b60a001516001600160a01b0316815250815260200160008054612f31906144b3565b80601f0160208091040260200160405190810160405280929190818152602001828054612f5d906144b3565b8015612faa5780601f10612f7f57610100808354040283529160200191612faa565b820191906000526020600020905b815481529060010190602001808311612f8d57829003601f168201915b5050505050815260200160018054612fc1906144b3565b80601f0160208091040260200160405190810160405280929190818152602001828054612fed906144b3565b801561303a5780601f1061300f5761010080835404028352916020019161303a565b820191906000526020600020905b81548152906001019060200180831161301d57829003601f168201915b50505050508152602001600081525086866040518563ffffffff1660e01b815260040161306a9493929190614849565b6020604051808303816000875af1158015613089573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130ad9190614738565b600380546001600160a01b0319166001600160a01b038316179055905060005b8351811015613195578381815181106130e8576130e8614516565b60200260200101516001600160a01b03166323b872dd308486858151811061311257613112614516565b60209081029190910101516040516001600160e01b031960e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561316c57600080fd5b505af1158015613180573d6000803e3d6000fd5b505050508061318e9061452c565b90506130cd565b50949350505050565b6001600160a01b038082166000818152600260205260409020549091168190036132175760008181526002602052604080822080546001600160a01b0319169055518291906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6040516316bf921960e01b81526001600160a01b038316600482015260248101829052604401610a35565b6132598160000151826020015183604001516135b1565b6127108161016001516080015161ffff16111561329a57610160810151608001516040516323dd0d6d60e11b815261ffff9091166004820152602401610a35565b6127108161016001516060015161ffff1611156132db57610160810151606001516040516323dd0d6d60e11b815261ffff9091166004820152602401610a35565b612710816080015161ffff1611156133125760808101516040516323dd0d6d60e11b815261ffff9091166004820152602401610a35565b61016081015180518051602090810290820120825260c082209152600655606081015160058054608084015161ffff16600160a01b026001600160b01b03199091166001600160a01b039093169290921791909117905560e0810151600a80546101008401516001600160601b03908116600160601b026001600160c01b031990921693169290921791909117905560006133ac34611cfe565b90506001600160601b038116156133f4576133cf8260a001518360c00151611c53565b6133f48260a001518360c0015183600060405180602001604052806000815250611d2f565b506101208101516101409091015160a01c600160a01b026001600160a01b0390911617600455565b604051633e4011b960e01b81523090633e4011b9906134419085908590600401614714565b60006040518083038186803b15801561345957600080fd5b505afa92505050801561346a575060015b6134d0573d808015613498576040519150601f19603f3d011682016040523d82523d6000602084013e61349d565b606091505b50600080828060200190518101906134b59190614996565b91509150816134c7576134c7816122f6565b6121c2816136be565b610c05614833565b600081815b845181101561351d57613509828683815181106134fc576134fc614516565b60200260200101516136c6565b9150806135158161452c565b9150506134dd565b509392505050565b604051635c9fcd8560e11b8152600360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b93f9b0a90602401602060405180830381865afa15801561358d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f589190614738565b60006135bd8482614a68565b5060016135ca8382614a68565b50801561157957604051635c9fcd8560e11b8152601660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b93f9b0a90602401602060405180830381865afa158015613636573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061365a9190614738565b6001600160a01b031663fc9968df826040518263ffffffff1660e01b815260040161368791815260200190565b600060405180830381600087803b1580156136a157600080fd5b505af11580156136b5573d6000803e3d6000fd5b50505050505050565b805160208201f35b60008183106136e2576000828152602084905260409020612358565b5060009182526020526040902090565b60006020828403121561370457600080fd5b81356001600160e01b03198116811461235857600080fd5b60005b8381101561373757818101518382015260200161371f565b50506000910152565b6000815180845261375881602086016020860161371c565b601f01601f19169290920160200192915050565b6020815260006123586020830184613740565b60006020828403121561379157600080fd5b5035919050565b6001600160a01b038116811461170857600080fd5b80356114da81613798565b600080604083850312156137cb57600080fd5b82356137d681613798565b946020939093013593505050565b6000602082840312156137f657600080fd5b813561235881613798565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b038111828210171561383a5761383a613801565b60405290565b60405161020081016001600160401b038111828210171561383a5761383a613801565b604051601f8201601f191681016001600160401b038111828210171561388b5761388b613801565b604052919050565b60006001600160401b038211156138ac576138ac613801565b50601f01601f191660200190565b600082601f8301126138cb57600080fd5b81356138de6138d982613893565b613863565b8181528460208386010111156138f357600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561392657600080fd5b843561393181613798565b9350602085013561394181613798565b92506040850135915060608501356001600160401b0381111561396357600080fd5b61396f878288016138ba565b91505092959194509250565b6000806040838503121561398e57600080fd5b823561399981613798565b915060208301356001600160401b038111156139b457600080fd5b6139c0858286016138ba565b9150509250929050565b6000806000606084860312156139df57600080fd5b83356139ea81613798565b925060208401356139fa81613798565b929592945050506040919091013590565b60008083601f840112613a1d57600080fd5b5081356001600160401b03811115613a3457600080fd5b602083019150836020828501011115613a4c57600080fd5b9250929050565b60008060008060608587031215613a6957600080fd5b8435613a7481613798565b935060208501356001600160401b03811115613a8f57600080fd5b613a9b87828801613a0b565b9598909750949560400135949350505050565b60006001600160401b03821115613ac757613ac7613801565b5060051b60200190565b600082601f830112613ae257600080fd5b81356020613af26138d983613aae565b82815260059290921b84018101918181019086841115613b1157600080fd5b8286015b84811015613b35578035613b2881613798565b8352918301918301613b15565b509695505050505050565b803564ffffffffff811681146114da57600080fd5b803561ffff811681146114da57600080fd5b600060c08284031215613b7957600080fd5b60405160c081016001600160401b038282108183111715613b9c57613b9c613801565b816040528293508435915080821115613bb457600080fd5b50613bc185828601613ad1565b825250613bd060208401613b40565b6020820152613be160408401613b40565b6040820152613bf260608401613b55565b6060820152613c0360808401613b55565b608082015260a0830135613c1681613798565b60a0919091015292915050565b60008060408385031215613c3657600080fd5b82356001600160401b03811115613c4c57600080fd5b613c5885828601613b67565b95602094909401359450505050565b634e487b7160e01b600052602160045260246000fd5b6020810160068310613c9f57634e487b7160e01b600052602160045260246000fd5b91905290565b600082601f830112613cb657600080fd5b81356020613cc66138d983613aae565b82815260059290921b84018101918181019086841115613ce557600080fd5b8286015b84811015613b355780358352918301918301613ce9565b600082601f830112613d1157600080fd5b81356020613d216138d983613aae565b82815260059290921b84018101918181019086841115613d4057600080fd5b8286015b84811015613b35578035613d5781613798565b8352918301918301613d44565b80356001600160601b03811681146114da57600080fd5b600082601f830112613d8c57600080fd5b81356020613d9c6138d983613aae565b82815260059290921b84018101918181019086841115613dbb57600080fd5b8286015b84811015613b3557613dd081613d64565b8352918301918301613dbf565b600082601f830112613dee57600080fd5b81356020613dfe6138d983613aae565b82815260059290921b84018101918181019086841115613e1d57600080fd5b8286015b84811015613b355780356001600160401b03811115613e405760008081fd5b613e4e8986838b01016138ba565b845250918301918301613e21565b600082601f830112613e6d57600080fd5b81356020613e7d6138d983613aae565b828152600592831b8501820192828201919087851115613e9c57600080fd5b8387015b85811015613f2f5780356001600160401b03811115613ebf5760008081fd5b8801603f81018a13613ed15760008081fd5b858101356040613ee36138d983613aae565b82815291851b8301810191888101908d841115613f005760008081fd5b938201935b83851015613f1e57843582529389019390890190613f05565b885250505093850193508401613ea0565b5090979650505050505050565b600060208284031215613f4e57600080fd5b81356001600160401b0380821115613f6557600080fd5b908301906101208286031215613f7a57600080fd5b613f82613817565b823582811115613f9157600080fd5b613f9d87828601613ca5565b825250602083013582811115613fb257600080fd5b613fbe87828601613d00565b602083015250604083013582811115613fd657600080fd5b613fe287828601613d7b565b604083015250606083013582811115613ffa57600080fd5b61400687828601613ddd565b60608301525060808301358281111561401e57600080fd5b61402a87828601613e5c565b60808301525060a083013560a082015260c083013560c082015260e08301358281111561405657600080fd5b61406287828601613b67565b60e0830152506101009283013592810192909252509392505050565b60008060006060848603121561409357600080fd5b833561409e81613798565b925060208401356140ae81613798565b915060408401356001600160401b038111156140c957600080fd5b6140d5868287016138ba565b9150509250925092565b801515811461170857600080fd5b600080600080600060a0868803121561410557600080fd5b85356001600160401b038082111561411c57600080fd5b61412889838a01613ad1565b9650602088013591508082111561413e57600080fd5b61414a89838a01613ad1565b9550604088013591508082111561416057600080fd5b61416c89838a01613ca5565b9450606088013591508082111561418257600080fd5b5061418f88828901613ddd565b92505060808601356141a0816140df565b809150509295509295909350565b600080604083850312156141c157600080fd5b82356141cc81613798565b915060208301356141dc816140df565b809150509250929050565b80356001600160a01b0319811681146114da57600080fd5b60006020828403121561421157600080fd5b81356001600160401b038082111561422857600080fd5b90830190610200828603121561423d57600080fd5b614245613840565b82358281111561425457600080fd5b614260878286016138ba565b82525060208301358281111561427557600080fd5b614281878286016138ba565b6020830152506040830135604082015261429d606084016137ad565b6060820152608083013560808201526142b860a08401613b40565b60a08201526142c960c08401613d64565b60c08201526142da60e084016137ad565b60e08201526101006142ed818501613b55565b908201526101206142ff8482016137ad565b908201526101406143118482016137ad565b90820152610160614323848201613d64565b90820152610180614335848201613d64565b908201526101a06143478482016137ad565b908201526101c06143598482016141e7565b908201526101e0838101358381111561437157600080fd5b61437d88828701613b67565b918301919091525095945050505050565b6000806000806000608086880312156143a657600080fd5b85356143b181613798565b945060208601356143c181613798565b93506040860135925060608601356001600160401b038111156143e357600080fd5b6143ef88828901613a0b565b969995985093965092949392505050565b60008060006040848603121561441557600080fd5b83356001600160401b038082111561442c57600080fd5b818601915086601f83011261444057600080fd5b81358181111561444f57600080fd5b8760208260051b850101111561446457600080fd5b6020928301955093505084013561447a816140df565b809150509250925092565b6000806040838503121561449857600080fd5b82356144a381613798565b915060208301356141dc81613798565b600181811c908216806144c757607f821691505b6020821081036144e757634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082018082111561096c5761096c6144ed565b634e487b7160e01b600052603260045260246000fd5b60006001820161453e5761453e6144ed565b5060010190565b8183823760009101908152919050565b6001600160a01b038516815260606020820181905281018390528284608083013760006080848301015260006080601f19601f860116830101905082604083015295945050505050565b600082516145b181846020870161371c565b9190910192915050565b82151581526040602082015260006145d66040830184613740565b949350505050565b6001600160601b038181168382160190808211156145fe576145fe6144ed565b5092915050565b8181038181111561096c5761096c6144ed565b6001600160a01b0384811682528316602082015260606040820181905260009061261990830184613740565b808202811582820484141761096c5761096c6144ed565b60008261467857634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b03841681526001600160a01b03198316602082015260606040820181905260009061261990830184613740565b6000602082840312156146c357600080fd5b8151612358816140df565b6001600160a01b038581168252841660208201526001600160a01b03198316604082015260806060820181905260009061470a90830184613740565b9695505050505050565b6001600160a01b03831681526040602082018190526000906145d690830184613740565b60006020828403121561474a57600080fd5b815161235881613798565b600081518084526020808501945080840160005b8381101561478e5781516001600160a01b031687529582019590820190600101614769565b509495945050505050565b600081518084526020808501945080840160005b8381101561478e578151875295820195908201906001016147ad565b6001600160a01b03851681526080602082018190526000906147ed90830186614755565b82810360408401526147ff8186614799565b9150506001600160601b038316606083015295945050505050565b60006020828403121561482c57600080fd5b5051919050565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b03858116825260806020808401829052865182850192909252815160e061010086015280516101e08601819052600094939183019085906102008801905b808310156148b05783518716825292850192600192909201919085019061488e565b508484015164ffffffffff9081166101208a0152604085015116610140890152606084015161ffff9081166101608a015260808501516001600160601b03166101808a015260a0808601519091166101a08a015260c08501516001600160a01b03166101c08a0152948b0151888203607f19908101968a019690965295506149388187613740565b9550505050506040870151818584030160c08601526149578382613740565b92505050606086015160e084015282810360408401526149778186614755565b9050828103606084015261498b8185614799565b979650505050505050565b600080604083850312156149a957600080fd5b82516149b4816140df565b60208401519092506001600160401b038111156149d057600080fd5b8301601f810185136149e157600080fd5b80516149ef6138d982613893565b818152866020838501011115614a0457600080fd5b614a1582602083016020860161371c565b8093505050509250929050565b601f82111561157957600081815260208120601f850160051c81016020861015614a495750805b601f850160051c820191505b81811015610d0157828155600101614a55565b81516001600160401b03811115614a8157614a81613801565b614a9581614a8f84546144b3565b84614a22565b602080601f831160018114614aca5760008415614ab25750858301515b600019600386901b1c1916600185901b178555610d01565b600085815260208120601f198616915b82811015614af957888601518255948401946001909101908401614ada565b5085821015614b175787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220ca2323c5941329c194f1ea75f8fd04848dc5cee229578a877dce3da817993d6564736f6c634300081100330000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade
Deployed Bytecode
0x6080604052600436106102935760003560e01c80636352211e1161015a578063bf1bdcf6116100c1578063e43816501161007a578063e4381650146108a6578063e6f26d9a146108bc578063e8a3d485146108dd578063e985e9c5146108f2578063ec6d3f3714610915578063f401a8b61461093c57600080fd5b8063bf1bdcf6146107a2578063c2c6a183146107c2578063c6788bdd146107e2578063c87b56dd1461082b578063d56d229d1461084b578063e184c9be1461086b57600080fd5b80638d3d6576116101135780638d3d6576146106fd57806395d89b4114610724578063a22cb46514610739578063aaffadf314610754578063ae1dd09914610774578063b88d4fde1461078757600080fd5b80636352211e1461064957806367a5e4261461066957806370a082311461067c5780637ad21b2e146106aa5780638433e7f3146106bd57806389afcb44146106dd57600080fd5b806323e45f62116101fe5780633e4011b9116101b75780633e4011b914610595578063419f1992146105b557806342842e0e146104a65780634367a029146105d557806345d61ded146105f557806356973ee51461061557600080fd5b806323e45f62146104c1578063305a0721146104f957806332156f1e1461050c578063354284f21461052c57806337c089231461054c5780633d2f3caa1461057357600080fd5b806314b182f91161025057806314b182f9146103bf578063150b7a02146103f55780631840f5c2146104395780631a8ae87d1461044c5780631e83409a1461048657806323b872dd146104a657600080fd5b806301ffc9a714610298578063048d372c146102cd57806306fdde0314610302578063081812fc14610324578063095ea7b31461035d5780630bce8cdf1461037f575b600080fd5b3480156102a457600080fd5b506102b86102b33660046136f2565b610952565b60405190151581526020015b60405180910390f35b3480156102d957600080fd5b506005546102ef90600160a01b900461ffff1681565b60405161ffff90911681526020016102c4565b34801561030e57600080fd5b50610317610972565b6040516102c4919061376c565b34801561033057600080fd5b5061034561033f36600461377f565b50600090565b6040516001600160a01b0390911681526020016102c4565b34801561036957600080fd5b5061037d6103783660046137b8565b610a00565b005b34801561038b57600080fd5b5061039f61039a3660046137e4565b610a3e565b6040805194855260208501939093529183015260608201526080016102c4565b3480156103cb57600080fd5b506103456103da3660046137e4565b6007602052600090815260409020546001600160a01b031681565b34801561040157600080fd5b50610420610410366004613910565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020016102c4565b61037d61044736600461397b565b610b8b565b34801561045857600080fd5b5060045461046d90600160a01b900460a01b81565b6040516001600160a01b031990911681526020016102c4565b34801561049257600080fd5b5061037d6104a13660046137e4565b610c09565b3480156104b257600080fd5b5061037d6103783660046139ca565b3480156104cd57600080fd5b50600b546104e1906001600160601b031681565b6040516001600160601b0390911681526020016102c4565b61037d610507366004613a53565b610d09565b34801561051857600080fd5b5061037d610527366004613c23565b610e5d565b34801561053857600080fd5b50600354610345906001600160a01b031681565b34801561055857600080fd5b506003546104e190600160a01b90046001600160601b031681565b34801561057f57600080fd5b50610588610f24565b6040516102c49190613c7d565b3480156105a157600080fd5b5061037d6105b036600461397b565b610f80565b3480156105c157600080fd5b506103456105d0366004613f3c565b61101c565b3480156105e157600080fd5b50600554610345906001600160a01b031681565b34801561060157600080fd5b50600454610345906001600160a01b031681565b34801561062157600080fd5b506103457f0000000000000000000000008e357490dc8e94e9594ae910ba261163631a6a3a81565b34801561065557600080fd5b5061034561066436600461377f565b6114df565b61037d61067736600461407e565b611518565b34801561068857600080fd5b5061069c6106973660046137e4565b61157e565b6040519081526020016102c4565b61037d6106b83660046140ed565b6115b2565b3480156106c957600080fd5b5061037d6106d83660046137e4565b6116ff565b3480156106e957600080fd5b5061037d6106f83660046137e4565b61170b565b34801561070957600080fd5b50600a546104e190600160601b90046001600160601b031681565b34801561073057600080fd5b50610317611728565b34801561074557600080fd5b5061037d6103783660046141ae565b34801561076057600080fd5b50600a546104e1906001600160601b031681565b61037d6107823660046141ff565b611735565b34801561079357600080fd5b5061037d61037836600461438e565b3480156107ae57600080fd5b5061037d6107bd366004614400565b6118a4565b3480156107ce57600080fd5b5061037d6107dd366004614400565b61199f565b3480156107ee57600080fd5b506108166107fd3660046137e4565b6009602052600090815260409020805460019091015482565b604080519283526020830191909152016102c4565b34801561083757600080fd5b5061031761084636600461377f565b6119aa565b34801561085757600080fd5b50600c54610345906001600160a01b031681565b34801561087757600080fd5b50600a5461089090600160c81b900464ffffffffff1681565b60405164ffffffffff90911681526020016102c4565b3480156108b257600080fd5b5061069c600d5481565b3480156108c857600080fd5b50600a546102b890600160c01b900460ff1681565b3480156108e957600080fd5b506103176119b4565b3480156108fe57600080fd5b506102b861090d366004614485565b600092915050565b34801561092157600080fd5b50600b546104e190600160601b90046001600160601b031681565b34801561094857600080fd5b5061069c60065481565b600061095d826119be565b8061096c575061096c826119f4565b92915050565b6000805461097f906144b3565b80601f01602080910402602001604051908101604052809291908181526020018280546109ab906144b3565b80156109f85780601f106109cd576101008083540402835291602001916109f8565b820191906000526020600020905b8154815290600101906020018083116109db57829003601f168201915b505050505081565b60405162461bcd60e51b815260206004820152600e60248201526d414c57415953204641494c494e4760901b60448201526064015b60405180910390fd5b6000806000806000610a4e610f24565b90506005816005811115610a6457610a64613c67565b1480610a8157506004816005811115610a7f57610a7f613c67565b145b15610aa757610a8f86611a1c565b91955093509150610aa08385614503565b9450610b83565b6001600160a01b038616600090815260086020908152604080832080548251818502810185019093528083529192909190849084015b82821015610b2657600084815260209081902060408051808201909152908401546001600160601b038082168352600160601b9091041681830152825260019092019101610add565b5050825192935060009150505b81811015610b7f57828181518110610b4d57610b4d614516565b6020026020010151602001516001600160601b031688610b6d9190614503565b9750610b788161452c565b9050610b33565b5050505b509193509193565b6001600160a01b037f0000000000000000000000008e357490dc8e94e9594ae910ba261163631a6a3a163003610bd45760405163ea2cbbd560e01b815260040160405180910390fd5b610bde3383611c53565b610c053383610bec34611cfe565b600354600160a01b90046001600160601b031685611d2f565b5050565b33600081815260096020818152604080842081518083019092528054825260018101805483850152958552929091529082905591558051158015610c4f57506020810151155b15610c6d5760405163f76ce33f60e01b815260040160405180910390fd5b805115610c8a578051610c8a906001600160a01b038416906121c9565b602081015115610c05576003546020820151604051632142170760e11b81523060048201526001600160a01b03858116602483015260448201929092529116906342842e0e90606401600060405180830381600087803b158015610ced57600080fd5b505af1158015610d01573d6000803e3d6000fd5b505050505050565b6001600160a01b037f0000000000000000000000008e357490dc8e94e9594ae910ba261163631a6a3a163003610d525760405163ea2cbbd560e01b815260040160405180910390fd5b610d5b3361224f565b610d7a57604051639f3df03d60e01b8152336004820152602401610a35565b600a54600160c01b900460ff1615610da5576040516304e71fd360e31b815260040160405180910390fd5b600080856001600160a01b0316838686604051610dc3929190614545565b60006040518083038185875af1925050503d8060008114610e00576040519150601f19603f3d011682016040523d82523d6000602084013e610e05565b606091505b509150915081610e1857610e18816122f6565b7f66cf5924183d7d44caa75d9268a169d7c1422fef43848743d5e1ba32d5833acf86868686604051610e4d9493929190614555565b60405180910390a1505050505050565b6001600160a01b037f0000000000000000000000008e357490dc8e94e9594ae910ba261163631a6a3a163003610ea65760405163ea2cbbd560e01b815260040160405180910390fd5b610eb13383836122fe565b158015610ec45750610ec23361224f565b155b15610ee457604051636b7bc65960e01b8152336004820152602401610a35565b600a805460ff60c01b1916600160c01b1790556040517f447e75484d6bdb571b4a92ae14018db7b6dd41f3f02360690c554e1d48f46f8790600090a15050565b600b54600090600160601b90046001600160601b031615610f5d576003546001600160a01b0316610f555750600390565b60055b905090565b600a54600160c81b900464ffffffffff164210610f7a5750600490565b50600190565b333014610f8c57600080fd5b600080836001600160a01b031683604051610fa7919061459f565b600060405180830381855af49150503d8060008114610fe2576040519150601f19603f3d011682016040523d82523d6000602084013e610fe7565b606091505b509150915061101682826040516020016110029291906145bb565b6040516020818303038152906040526122f6565b50505050565b60006001600160a01b037f0000000000000000000000008e357490dc8e94e9594ae910ba261163631a6a3a1630036110675760405163ea2cbbd560e01b815260040160405180910390fd5b61107b338360e0015184610100015161235f565b6000611085610f24565b9050600181600581111561109b5761109b613c67565b146110bb5780604051638847aaed60e01b8152600401610a359190613c7d565b508160a001516000036110e457604051635182e6c160e01b815260006004820152602401610a35565b602082015151825151141580611101575060408201515182515114155b80611113575060608201515182515114155b80611125575060808201515182515114155b1561114357604051636bec2bf560e11b815260040160405180910390fd5b600b8054600160601b600160c01b0319166bffffffffffffffffffffffff60601b179055815151600090819081906001600160401b0381111561118857611188613801565b6040519080825280602002602001820160405280156111b1578160200160208202803683370190505b50600c54600d549192506001600160a01b03169060005b87515181101561140e57811561121d5761121d886000015182815181106111f1576111f1614516565b6020026020010151838a60800151848151811061121057611210614516565b6020026020010151612387565b60004790506000806112a6868c60000151868151811061123f5761123f614516565b60200260200101518d60200151878151811061125d5761125d614516565b60200260200101518e60400151888151811061127b5761127b614516565b60200260200101518f60600151898151811061129957611299614516565b60200260200101516123b8565b9150915081611345578a515160a08c01511061131d578051156112d1576112cc816122f6565b611345565b858b6000015185815181106112e8576112e8614516565b602090810291909101015160405163ae38c3b560e01b81526001600160a01b0390921660048301526024820152604401610a35565b82471461133d57604051639cf3222560e01b815260040160405180910390fd5b5050506113fe565b8a60400151848151811061135b5761135b614516565b60200260200101518961136e91906145de565b98506113798861452c565b9750858761138860018b614605565b8151811061139857611398614516565b6001600160a01b03909216602092830291909101909101528a518051859081106113c4576113c4614516565b60200260200101518b6000015160018a6113de9190614605565b815181106113ee576113ee614516565b6020026020010181815250505050505b6114078161452c565b90506111c8565b50846001600160601b031660000361143957604051632219363960e11b815260040160405180910390fd5b8660a0015184101561146e5760a0870151604051635731934f60e01b8152610a35918691600401918252602082015260400190565b8660c00151856001600160601b031610156114b45760c087015160405163780edab360e11b81526001600160601b03871660048201526024810191909152604401610a35565b838352836101a0526114d2838860000151878a60e0015160016124ea565b955050505050505b919050565b6000818152600260205260409020546001600160a01b0316806114da57604051632abfe32760e11b815260048101839052602401610a35565b6001600160a01b037f0000000000000000000000008e357490dc8e94e9594ae910ba261163631a6a3a1630036115615760405163ea2cbbd560e01b815260040160405180910390fd5b61156b8383611c53565b6115798383610bec34611cfe565b505050565b6001600160a01b038082166000908152600260205260408120549091166115a65760006115a9565b60015b60ff1692915050565b60005b8551811015610d0157600080306001600160a01b03168684815181106115dd576115dd614516565b6020026020010151306001600160a01b03166367a5e4268b878151811061160657611606614516565b60200260200101518b888151811061162057611620614516565b60200260200101518a898151811061163a5761163a614516565b602002602001015160405160240161165493929190614618565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b93909317909252905161168c925061459f565b60006040518083038185875af1925050503d80600081146116c9576040519150601f19603f3d011682016040523d82523d6000602084013e6116ce565b606091505b50915091508380156116de575081155b156116ec576116ec816122f6565b5050806116f89061452c565b90506115b5565b6117088161170b565b50565b61170881611717610f24565b6003546001600160a01b0316612622565b6001805461097f906144b3565b303b15611755576040516304a41c4d60e51b815260040160405180910390fd5b6101e0810151515160000361177d576040516362dc091b60e01b815260040160405180910390fd5b611877604051806101c001604052808360000151815260200183602001518152602001836040015181526020018360a0015164ffffffffff1681526020018360c001516001600160601b031681526020018360e001516001600160a01b0316815260200183610100015161ffff1681526020018361012001516001600160a01b031681526020018361014001516001600160a01b031681526020018361016001516001600160601b031681526020018361018001516001600160601b03168152602001836101a001516001600160a01b03168152602001836101c001516001600160a01b0319168152602001836101e00151815250612964565b6060810151600c80546001600160a01b0319166001600160a01b0390921691909117905560800151600d55565b60005b828110156110165760008030806389afcb448888878181106118cb576118cb614516565b90506020020160208101906118e091906137e4565b6040516001600160a01b03909116602482015260440160408051601f198184030181529181526020820180516001600160e01b031660e09490941b93909317909252905161192e925061459f565b600060405180830381855af49150503d8060008114611969576040519150601f19603f3d011682016040523d82523d6000602084013e61196e565b606091505b509150915083801561197e575081155b1561198c5761198c816122f6565b5050806119989061452c565b90506118a7565b6115798383836118a4565b606061096c612a9c565b6060610f58612a9c565b60006301ffc9a760e01b6001600160e01b03198316148061096c57506001600160e01b03198216630a85bd0160e11b1492915050565b60006119ff826119be565b8061096c5750506001600160e01b0319166380ac58cd60e01b1490565b600080600080611a3c600b546001600160601b03600160601b9091041690565b6001600160a01b038616600090815260086020908152604080832080548251818502810185019093528083529495509293909291849084015b82821015611abe57600084815260209081902060408051808201909152908401546001600160601b038082168352600160601b9091041681830152825260019092019101611a75565b5050825192935060009150505b81811015611bb7576000838281518110611ae757611ae7614516565b602002602001015190508481600001516001600160601b031610611b24576020810151611b1d906001600160601b031688614503565b9650611ba6565b602081015181518691611b36916145de565b6001600160601b031611611b63576020810151611b5c906001600160601b031689614503565b9750611ba6565b8051600090611b7b906001600160601b031687614605565b9050611b87818a614503565b98508082602001516001600160601b0316611ba29190614605565b9750505b50611bb08161452c565b9050611acb565b50506005546001600160a01b0381169150600160a01b900461ffff1681611bdc575060005b61271086611bea8383614605565b611bf49190614644565b611bfe919061465b565b9350866001600160a01b0316826001600160a01b031603611c4957612710611c268483614644565b611c329061270f614503565b611c3c919061465b565b611c469085614503565b93505b5050509193909250565b6001600160a01b038116611c7a5760405163e5ec2d2560e01b815260040160405180910390fd5b6001600160a01b0380831660009081526007602052604090205481169082168103611ca457505050565b336001600160a01b03841614801590611cc557506001600160a01b03811615155b15611ccf57505050565b506001600160a01b03918216600090815260076020526040902080546001600160a01b03191691909216179055565b60006001600160601b03821115611d2b57604051636f55e21560e11b815260048101839052602401610a35565b5090565b306001600160a01b03861603611d585760405163ac04f48f60e01b815260040160405180910390fd5b6001600160601b038316156121c2576004546001600160a01b03168015611e27576004805460405163267a0a0560e21b81526001600160a01b038416926399e8281492611db5923392600160a01b90920460a01b9188910161467d565b602060405180830381865afa158015611dd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df691906146b1565b611e275760048054604051633921815160e21b8152610a359233928592600160a01b90910460a01b918791016146ce565b506000611e32610f24565b90506001816005811115611e4857611e48613c67565b14611e685780604051638847aaed60e01b8152600401610a359190613c7d565b5082600360148282829054906101000a90046001600160601b0316611e8d91906145de565b82546001600160601b039182166101009390930a9283029190920219909116179055506001600160a01b038516600090815260086020526040812080549091805b82811015611f2057838181548110611ee857611ee8614516565b600091825260209091200154611f0e90600160601b90046001600160601b0316836145de565b9150611f198161452c565b9050611ece565b50600a546001600160601b0316611f3787836145de565b6001600160601b03161015611f7f57611f5086826145de565b600a54604051630352153b60e01b81526001600160601b03928316600482015291166024820152604401610a35565b600a54600160601b90046001600160601b0316611f9c87836145de565b6001600160601b03161115611fec57611fb586826145de565b600a5460405162f2e14960e61b81526001600160601b039283166004820152600160601b9091049091166024820152604401610a35565b604080513381526001600160a01b038a811660208301526001600160601b0389811683850152908a1660608301528716608082015290517f5982c40d76f99ce7f7cb2138b9af68857259da5d9da4f06ccbc884d861e8f58f9181900360a00190a16001821061215357600083612063600185614605565b8154811061207357612073614516565b60009182526020808320604080518082019091529201546001600160601b03808216808552600160601b909204169183018290529193506120b3916145de565b6001600160601b03169050866001600160601b031681036121505787826020018181516120e091906145de565b6001600160601b031690525081856120f9600187614605565b8154811061210957612109614516565b600091825260209182902083519101805493909201516001600160601b03908116600160601b026001600160c01b0319909416911617919091179055506121c29350505050565b50505b604080518082019091526001600160601b03808716825287811660208084019182528654600181018855600088815291822094519401805492518416600160601b026001600160c01b031990931694909316939093171790558290036121be576121bc88612b84565b505b5050505b5050505050565b806000036121d5575050565b600080836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612223576040519150601f19603f3d011682016040523d82523d6000602084013e612228565b606091505b50915091508161101657838160405163354db69760e01b8152600401610a35929190614714565b604051635c9fcd8560e11b8152600b60048201526000907f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade6001600160a01b03169063b93f9b0a90602401602060405180830381865afa1580156122b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122db9190614738565b6001600160a01b0316826001600160a01b0316149050919050565b805160208201fd5b8151516000908210801561233e5750825180518390811061232157612321614516565b60200260200101516001600160a01b0316846001600160a01b0316145b156123545761234c83612c56565b506001612358565b5060005b9392505050565b61236a8383836122fe565b61157957604051633641391960e21b815260040160405180910390fd5b60008381526020902061239b828483612c98565b6110165760405163d190acef60e01b815260040160405180910390fd5b600060606123c7858489612cae565b6123e85784836040516309a30f6560e01b8152600401610a35929190614714565b600b546001600160601b0390811690851681101561242c576040516308838d7360e21b81526001600160601b03808716600483015282166024820152604401610a35565b50600080866001600160a01b0316866001600160601b031686604051612452919061459f565b60006040518083038185875af1925050503d806000811461248f576040519150601f19603f3d011682016040523d82523d6000602084013e612494565b606091505b5091509150816124ab576000935091506124e09050565b306124bf6001600160a01b038b168a612d3f565b6001600160a01b031614604051806020016040528060008152509350935050505b9550959350505050565b6003546000906001600160601b03600160a01b909104811690851681101561253857604051635723208b60e11b81526001600160601b03808716600483015282166024820152604401610a35565b506001600160601b038416156125bc57600b8054600160601b600160c01b031916600160601b6001600160601b0387160217905561257883838888612e1f565b90507f139324ee0c9d8324da2602ed90fb064f661cd2e2498417dbd964903d19bf3c32818787876040516125af94939291906147c9565b60405180910390a1612619565b600b8054600160601b600160c01b0319169055600a805464ffffffffff60c81b1916600160c81b4264ffffffffff16021790556040517f39b8faea695f02450d8bc947229f0da7141777a2dc02ebbeda20c259acb5936390600090a15b95945050505050565b600582600581111561263657612636613c67565b03612667576001600160a01b03811661266257604051632865539760e21b815260040160405180910390fd5b61269b565b600482600581111561267b5761267b613c67565b1461269b5781604051638847aaed60e01b8152600401610a359190613c7d565b6005546001600160a01b039081169084168190036126f257600554600160b01b900460ff16156126de57604051639b0f501b60e01b815260040160405180910390fd5b6005805460ff60b01b1916600160b01b1790555b836001600160a01b0316816001600160a01b031614158061272c57506001600160a01b038085166000908152600260205260409020541615155b1561273a5761273a8461319e565b50600080600061274986611a1c565b919450925090508015612893576001600160a01b0380871660009081526007602052604090205416806127795750855b604051630d4d151360e01b81526001600160a01b038881166004830152602482018490528281166044830152861690630d4d1513906064016020604051808303816000875af19250505080156127ec575060408051601f3d908101601f191682019092526127e99181019061481a565b60015b61288f57604051630d4d151360e01b8152306004820152602481018390526001600160a01b03828116604483015260009190871690630d4d1513906064016020604051808303816000875af1158015612849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061286d919061481a565b6001600160a01b03891660009081526009602052604090206001015550612891565b505b505b6000866001600160a01b03168360405160006040518083038185875af1925050503d80600081146128e0576040519150601f19603f3d011682016040523d82523d6000602084013e6128e5565b606091505b505090508061290a576001600160a01b03871660009081526009602052604090208390555b604080516001600160a01b038916815260208101869052908101849052606081018390527f4c60206a5c1de41f3376d1d60f0949d96cb682033c90b1c2d9d9a62d4c4120c09060800160405180910390a150505050505050565b42816060015164ffffffffff1661297b9190614503565b600a60196101000a81548164ffffffffff021916908364ffffffffff1602179055508060800151600b60006101000a8154816001600160601b0302191690836001600160601b031602179055506117086040518061018001604052808360000151815260200183602001518152602001836040015181526020018360a001516001600160a01b031681526020018360c0015161ffff1681526020018360e001516001600160a01b031681526020018361010001516001600160a01b031681526020018361012001516001600160601b031681526020018361014001516001600160601b031681526020018361016001516001600160a01b031681526020018361018001516001600160a01b0319168152602001836101a00151815250613242565b604051635c9fcd8560e11b815260056004820152606090612b69906001600160a01b037f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade169063b93f9b0a90602401602060405180830381865afa158015612b08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2c9190614738565b6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061341c92505050565b612b71614833565b5060408051602081019091526000815290565b60006001600160a01b038216612bad57604051637ac7fa7d60e01b815260040160405180910390fd5b506001600160a01b038082166000818152600260205260409020549091168114612c2b5760008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a4919050565b60405163c343b42b60e01b81526001600160a01b038316600482015260248101829052604401610a35565b6000612c748280518051602090810290820120825260c08220915290565b90506006548114610c0557604051632867dd6360e01b815260040160405180910390fd5b600082612ca585846134d8565b14949350505050565b6000306001600160a01b03851603612cc857506000612358565b816001600160a01b0316846001600160a01b0316148015612ceb57506004835110155b15612d355760208301516001600160e01b03191663095ea7b360e01b811480612d2457506001600160e01b0319811663a22cb46560e01b145b15612d33576000915050612358565b505b5060019392505050565b6000806000846001600160a01b0316856001600160a01b0316636352211e86604051602401612d7091815260200190565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b939093179092529051612da8925061459f565b600060405180830381855afa9150503d8060008114612de3576040519150601f19603f3d011682016040523d82523d6000602084013e612de8565b606091505b5091509150811580612dfb575060208151105b15612e0b5760009250505061096c565b808060200190518101906126199190614738565b6003546000906001600160a01b031615612e5b57600354604051630101411760e31b81526001600160a01b039091166004820152602401610a35565b83612e6957612e6985612c56565b612e71613525565b6001600160a01b031663e579b0fb3060405180608001604052806040518060e001604052808b6000015181526020018b6020015164ffffffffff1681526020018b6040015164ffffffffff1681526020018b6060015161ffff168152602001612ef2612eed600b546001600160601b03600160601b9091041690565b611cfe565b6001600160601b031681526020018b6080015161ffff1681526020018b60a001516001600160a01b0316815250815260200160008054612f31906144b3565b80601f0160208091040260200160405190810160405280929190818152602001828054612f5d906144b3565b8015612faa5780601f10612f7f57610100808354040283529160200191612faa565b820191906000526020600020905b815481529060010190602001808311612f8d57829003601f168201915b5050505050815260200160018054612fc1906144b3565b80601f0160208091040260200160405190810160405280929190818152602001828054612fed906144b3565b801561303a5780601f1061300f5761010080835404028352916020019161303a565b820191906000526020600020905b81548152906001019060200180831161301d57829003601f168201915b50505050508152602001600081525086866040518563ffffffff1660e01b815260040161306a9493929190614849565b6020604051808303816000875af1158015613089573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130ad9190614738565b600380546001600160a01b0319166001600160a01b038316179055905060005b8351811015613195578381815181106130e8576130e8614516565b60200260200101516001600160a01b03166323b872dd308486858151811061311257613112614516565b60209081029190910101516040516001600160e01b031960e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561316c57600080fd5b505af1158015613180573d6000803e3d6000fd5b505050508061318e9061452c565b90506130cd565b50949350505050565b6001600160a01b038082166000818152600260205260409020549091168190036132175760008181526002602052604080822080546001600160a01b0319169055518291906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6040516316bf921960e01b81526001600160a01b038316600482015260248101829052604401610a35565b6132598160000151826020015183604001516135b1565b6127108161016001516080015161ffff16111561329a57610160810151608001516040516323dd0d6d60e11b815261ffff9091166004820152602401610a35565b6127108161016001516060015161ffff1611156132db57610160810151606001516040516323dd0d6d60e11b815261ffff9091166004820152602401610a35565b612710816080015161ffff1611156133125760808101516040516323dd0d6d60e11b815261ffff9091166004820152602401610a35565b61016081015180518051602090810290820120825260c082209152600655606081015160058054608084015161ffff16600160a01b026001600160b01b03199091166001600160a01b039093169290921791909117905560e0810151600a80546101008401516001600160601b03908116600160601b026001600160c01b031990921693169290921791909117905560006133ac34611cfe565b90506001600160601b038116156133f4576133cf8260a001518360c00151611c53565b6133f48260a001518360c0015183600060405180602001604052806000815250611d2f565b506101208101516101409091015160a01c600160a01b026001600160a01b0390911617600455565b604051633e4011b960e01b81523090633e4011b9906134419085908590600401614714565b60006040518083038186803b15801561345957600080fd5b505afa92505050801561346a575060015b6134d0573d808015613498576040519150601f19603f3d011682016040523d82523d6000602084013e61349d565b606091505b50600080828060200190518101906134b59190614996565b91509150816134c7576134c7816122f6565b6121c2816136be565b610c05614833565b600081815b845181101561351d57613509828683815181106134fc576134fc614516565b60200260200101516136c6565b9150806135158161452c565b9150506134dd565b509392505050565b604051635c9fcd8560e11b8152600360048201526000907f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade6001600160a01b03169063b93f9b0a90602401602060405180830381865afa15801561358d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f589190614738565b60006135bd8482614a68565b5060016135ca8382614a68565b50801561157957604051635c9fcd8560e11b8152601660048201527f0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade6001600160a01b03169063b93f9b0a90602401602060405180830381865afa158015613636573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061365a9190614738565b6001600160a01b031663fc9968df826040518263ffffffff1660e01b815260040161368791815260200190565b600060405180830381600087803b1580156136a157600080fd5b505af11580156136b5573d6000803e3d6000fd5b50505050505050565b805160208201f35b60008183106136e2576000828152602084905260409020612358565b5060009182526020526040902090565b60006020828403121561370457600080fd5b81356001600160e01b03198116811461235857600080fd5b60005b8381101561373757818101518382015260200161371f565b50506000910152565b6000815180845261375881602086016020860161371c565b601f01601f19169290920160200192915050565b6020815260006123586020830184613740565b60006020828403121561379157600080fd5b5035919050565b6001600160a01b038116811461170857600080fd5b80356114da81613798565b600080604083850312156137cb57600080fd5b82356137d681613798565b946020939093013593505050565b6000602082840312156137f657600080fd5b813561235881613798565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b038111828210171561383a5761383a613801565b60405290565b60405161020081016001600160401b038111828210171561383a5761383a613801565b604051601f8201601f191681016001600160401b038111828210171561388b5761388b613801565b604052919050565b60006001600160401b038211156138ac576138ac613801565b50601f01601f191660200190565b600082601f8301126138cb57600080fd5b81356138de6138d982613893565b613863565b8181528460208386010111156138f357600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561392657600080fd5b843561393181613798565b9350602085013561394181613798565b92506040850135915060608501356001600160401b0381111561396357600080fd5b61396f878288016138ba565b91505092959194509250565b6000806040838503121561398e57600080fd5b823561399981613798565b915060208301356001600160401b038111156139b457600080fd5b6139c0858286016138ba565b9150509250929050565b6000806000606084860312156139df57600080fd5b83356139ea81613798565b925060208401356139fa81613798565b929592945050506040919091013590565b60008083601f840112613a1d57600080fd5b5081356001600160401b03811115613a3457600080fd5b602083019150836020828501011115613a4c57600080fd5b9250929050565b60008060008060608587031215613a6957600080fd5b8435613a7481613798565b935060208501356001600160401b03811115613a8f57600080fd5b613a9b87828801613a0b565b9598909750949560400135949350505050565b60006001600160401b03821115613ac757613ac7613801565b5060051b60200190565b600082601f830112613ae257600080fd5b81356020613af26138d983613aae565b82815260059290921b84018101918181019086841115613b1157600080fd5b8286015b84811015613b35578035613b2881613798565b8352918301918301613b15565b509695505050505050565b803564ffffffffff811681146114da57600080fd5b803561ffff811681146114da57600080fd5b600060c08284031215613b7957600080fd5b60405160c081016001600160401b038282108183111715613b9c57613b9c613801565b816040528293508435915080821115613bb457600080fd5b50613bc185828601613ad1565b825250613bd060208401613b40565b6020820152613be160408401613b40565b6040820152613bf260608401613b55565b6060820152613c0360808401613b55565b608082015260a0830135613c1681613798565b60a0919091015292915050565b60008060408385031215613c3657600080fd5b82356001600160401b03811115613c4c57600080fd5b613c5885828601613b67565b95602094909401359450505050565b634e487b7160e01b600052602160045260246000fd5b6020810160068310613c9f57634e487b7160e01b600052602160045260246000fd5b91905290565b600082601f830112613cb657600080fd5b81356020613cc66138d983613aae565b82815260059290921b84018101918181019086841115613ce557600080fd5b8286015b84811015613b355780358352918301918301613ce9565b600082601f830112613d1157600080fd5b81356020613d216138d983613aae565b82815260059290921b84018101918181019086841115613d4057600080fd5b8286015b84811015613b35578035613d5781613798565b8352918301918301613d44565b80356001600160601b03811681146114da57600080fd5b600082601f830112613d8c57600080fd5b81356020613d9c6138d983613aae565b82815260059290921b84018101918181019086841115613dbb57600080fd5b8286015b84811015613b3557613dd081613d64565b8352918301918301613dbf565b600082601f830112613dee57600080fd5b81356020613dfe6138d983613aae565b82815260059290921b84018101918181019086841115613e1d57600080fd5b8286015b84811015613b355780356001600160401b03811115613e405760008081fd5b613e4e8986838b01016138ba565b845250918301918301613e21565b600082601f830112613e6d57600080fd5b81356020613e7d6138d983613aae565b828152600592831b8501820192828201919087851115613e9c57600080fd5b8387015b85811015613f2f5780356001600160401b03811115613ebf5760008081fd5b8801603f81018a13613ed15760008081fd5b858101356040613ee36138d983613aae565b82815291851b8301810191888101908d841115613f005760008081fd5b938201935b83851015613f1e57843582529389019390890190613f05565b885250505093850193508401613ea0565b5090979650505050505050565b600060208284031215613f4e57600080fd5b81356001600160401b0380821115613f6557600080fd5b908301906101208286031215613f7a57600080fd5b613f82613817565b823582811115613f9157600080fd5b613f9d87828601613ca5565b825250602083013582811115613fb257600080fd5b613fbe87828601613d00565b602083015250604083013582811115613fd657600080fd5b613fe287828601613d7b565b604083015250606083013582811115613ffa57600080fd5b61400687828601613ddd565b60608301525060808301358281111561401e57600080fd5b61402a87828601613e5c565b60808301525060a083013560a082015260c083013560c082015260e08301358281111561405657600080fd5b61406287828601613b67565b60e0830152506101009283013592810192909252509392505050565b60008060006060848603121561409357600080fd5b833561409e81613798565b925060208401356140ae81613798565b915060408401356001600160401b038111156140c957600080fd5b6140d5868287016138ba565b9150509250925092565b801515811461170857600080fd5b600080600080600060a0868803121561410557600080fd5b85356001600160401b038082111561411c57600080fd5b61412889838a01613ad1565b9650602088013591508082111561413e57600080fd5b61414a89838a01613ad1565b9550604088013591508082111561416057600080fd5b61416c89838a01613ca5565b9450606088013591508082111561418257600080fd5b5061418f88828901613ddd565b92505060808601356141a0816140df565b809150509295509295909350565b600080604083850312156141c157600080fd5b82356141cc81613798565b915060208301356141dc816140df565b809150509250929050565b80356001600160a01b0319811681146114da57600080fd5b60006020828403121561421157600080fd5b81356001600160401b038082111561422857600080fd5b90830190610200828603121561423d57600080fd5b614245613840565b82358281111561425457600080fd5b614260878286016138ba565b82525060208301358281111561427557600080fd5b614281878286016138ba565b6020830152506040830135604082015261429d606084016137ad565b6060820152608083013560808201526142b860a08401613b40565b60a08201526142c960c08401613d64565b60c08201526142da60e084016137ad565b60e08201526101006142ed818501613b55565b908201526101206142ff8482016137ad565b908201526101406143118482016137ad565b90820152610160614323848201613d64565b90820152610180614335848201613d64565b908201526101a06143478482016137ad565b908201526101c06143598482016141e7565b908201526101e0838101358381111561437157600080fd5b61437d88828701613b67565b918301919091525095945050505050565b6000806000806000608086880312156143a657600080fd5b85356143b181613798565b945060208601356143c181613798565b93506040860135925060608601356001600160401b038111156143e357600080fd5b6143ef88828901613a0b565b969995985093965092949392505050565b60008060006040848603121561441557600080fd5b83356001600160401b038082111561442c57600080fd5b818601915086601f83011261444057600080fd5b81358181111561444f57600080fd5b8760208260051b850101111561446457600080fd5b6020928301955093505084013561447a816140df565b809150509250925092565b6000806040838503121561449857600080fd5b82356144a381613798565b915060208301356141dc81613798565b600181811c908216806144c757607f821691505b6020821081036144e757634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082018082111561096c5761096c6144ed565b634e487b7160e01b600052603260045260246000fd5b60006001820161453e5761453e6144ed565b5060010190565b8183823760009101908152919050565b6001600160a01b038516815260606020820181905281018390528284608083013760006080848301015260006080601f19601f860116830101905082604083015295945050505050565b600082516145b181846020870161371c565b9190910192915050565b82151581526040602082015260006145d66040830184613740565b949350505050565b6001600160601b038181168382160190808211156145fe576145fe6144ed565b5092915050565b8181038181111561096c5761096c6144ed565b6001600160a01b0384811682528316602082015260606040820181905260009061261990830184613740565b808202811582820484141761096c5761096c6144ed565b60008261467857634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b03841681526001600160a01b03198316602082015260606040820181905260009061261990830184613740565b6000602082840312156146c357600080fd5b8151612358816140df565b6001600160a01b038581168252841660208201526001600160a01b03198316604082015260806060820181905260009061470a90830184613740565b9695505050505050565b6001600160a01b03831681526040602082018190526000906145d690830184613740565b60006020828403121561474a57600080fd5b815161235881613798565b600081518084526020808501945080840160005b8381101561478e5781516001600160a01b031687529582019590820190600101614769565b509495945050505050565b600081518084526020808501945080840160005b8381101561478e578151875295820195908201906001016147ad565b6001600160a01b03851681526080602082018190526000906147ed90830186614755565b82810360408401526147ff8186614799565b9150506001600160601b038316606083015295945050505050565b60006020828403121561482c57600080fd5b5051919050565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b03858116825260806020808401829052865182850192909252815160e061010086015280516101e08601819052600094939183019085906102008801905b808310156148b05783518716825292850192600192909201919085019061488e565b508484015164ffffffffff9081166101208a0152604085015116610140890152606084015161ffff9081166101608a015260808501516001600160601b03166101808a015260a0808601519091166101a08a015260c08501516001600160a01b03166101c08a0152948b0151888203607f19908101968a019690965295506149388187613740565b9550505050506040870151818584030160c08601526149578382613740565b92505050606086015160e084015282810360408401526149778186614755565b9050828103606084015261498b8185614799565b979650505050505050565b600080604083850312156149a957600080fd5b82516149b4816140df565b60208401519092506001600160401b038111156149d057600080fd5b8301601f810185136149e157600080fd5b80516149ef6138d982613893565b818152866020838501011115614a0457600080fd5b614a1582602083016020860161371c565b8093505050509250929050565b601f82111561157957600081815260208120601f850160051c81016020861015614a495750805b601f850160051c820191505b81811015610d0157828155600101614a55565b81516001600160401b03811115614a8157614a81613801565b614a9581614a8f84546144b3565b84614a22565b602080601f831160018114614aca5760008415614ab25750858301515b600019600386901b1c1916600185901b178555610d01565b600085815260208120601f198616915b82811015614af957888601518255948401946001909101908401614ada565b5085821015614b175787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220ca2323c5941329c194f1ea75f8fd04848dc5cee229578a877dce3da817993d6564736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade
-----Decoded View---------------
Arg [0] : globals (address): 0x1cA20040cE6aD406bC2A6c89976388829E7fbAde
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000001ca20040ce6ad406bc2a6c89976388829e7fbade
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.