编译器
0.8.20+commit.a1b79de6
文件 1 的 52:AuctionCrowdfund.sol
pragma solidity 0.8.20;
import "./AuctionCrowdfundBase.sol";
contract AuctionCrowdfund is AuctionCrowdfundBase {
using LibSafeERC721 for IERC721;
using LibSafeCast for uint256;
using LibRawResult for bytes;
constructor(IGlobals globals) AuctionCrowdfundBase(globals) {}
function initialize(AuctionCrowdfundOptions memory opts) external payable onlyConstructor {
AuctionCrowdfundBase._initialize(opts);
}
function finalize(
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts
) external onlyDelegateCall returns (Party party_) {
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active && lc != CrowdfundLifecycle.Expired) {
revert WrongLifecycleError(lc);
}
uint96 lastBid_ = lastBid;
_finalizeAuction(lc, market, auctionId, lastBid_);
IERC721 nftContract_ = nftContract;
uint256 nftTokenId_ = nftTokenId;
if (nftContract_.safeOwnerOf(nftTokenId_) == address(this) && lastBid_ != 0) {
party_ = _createParty(
governanceOpts,
proposalEngineOpts,
false,
nftContract_,
nftTokenId_
);
emit Won(lastBid_, party_);
} else {
lastBid = 0;
emit Lost();
}
_bidStatus = AuctionCrowdfundStatus.Finalized;
emit BatchMetadataUpdate(0, type(uint256).max);
}
}
文件 2 的 52:AuctionCrowdfundBase.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "../party/Party.sol";
import "../utils/LibSafeERC721.sol";
import "../utils/LibRawResult.sol";
import "../globals/IGlobals.sol";
import "../gatekeepers/IGateKeeper.sol";
import "../market-wrapper/IMarketWrapper.sol";
import "./Crowdfund.sol";
abstract contract AuctionCrowdfundBase is Crowdfund {
using LibSafeERC721 for IERC721;
using LibSafeCast for uint256;
using LibRawResult for bytes;
enum AuctionCrowdfundStatus {
Active,
Busy,
Finalized
}
struct AuctionCrowdfundOptions {
string name;
string symbol;
uint256 customizationPresetId;
uint256 auctionId;
IMarketWrapper market;
IERC721 nftContract;
uint256 nftTokenId;
uint40 duration;
uint96 maximumBid;
address payable splitRecipient;
uint16 splitBps;
address initialContributor;
address initialDelegate;
uint96 minContribution;
uint96 maxContribution;
IGateKeeper gateKeeper;
bytes12 gateKeeperId;
bool onlyHostCanBid;
FixedGovernanceOpts governanceOpts;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
}
event Bid(uint256 bidAmount);
event Won(uint256 bid, Party party);
event Lost();
error InvalidAuctionIdError();
error AuctionFinalizedError(uint256 auctionId);
error AlreadyHighestBidderError();
error ExceedsMaximumBidError(uint256 bidAmount, uint256 maximumBid);
error MinimumBidExceedsMaximumBidError(uint256 bidAmount, uint256 maximumBid);
error NoContributionsError();
error AuctionNotExpiredError();
IERC721 public nftContract;
uint256 public nftTokenId;
IMarketWrapper public market;
uint256 public auctionId;
uint96 public maximumBid;
uint96 public lastBid;
uint40 public expiry;
bool public onlyHostCanBid;
AuctionCrowdfundStatus internal _bidStatus;
constructor(IGlobals globals) Crowdfund(globals) {}
function _initialize(AuctionCrowdfundOptions memory opts) internal {
if (opts.onlyHostCanBid && opts.governanceOpts.hosts.length == 0) {
revert MissingHostsError();
}
nftContract = opts.nftContract;
nftTokenId = opts.nftTokenId;
market = opts.market;
expiry = uint40(opts.duration + block.timestamp);
auctionId = opts.auctionId;
maximumBid = opts.maximumBid;
onlyHostCanBid = opts.onlyHostCanBid;
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,
proposalEngineOpts: opts.proposalEngineOpts
})
);
_validateAuction(opts.market, opts.auctionId, opts.nftContract, opts.nftTokenId);
uint256 minimumBid = opts.market.getMinimumBid(opts.auctionId);
if (minimumBid > opts.maximumBid) {
revert MinimumBidExceedsMaximumBidError(minimumBid, opts.maximumBid);
}
}
receive() external payable {}
function bid() external {
if (onlyHostCanBid) revert OnlyPartyHostError();
_bid(type(uint96).max);
}
function bid(
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
uint256 hostIndex
) external {
if (onlyHostCanBid) {
_assertIsHost(msg.sender, governanceOpts, proposalEngineOpts, hostIndex);
} else if (address(gateKeeper) != address(0)) {
_assertIsContributor(msg.sender);
}
_bid(type(uint96).max);
}
function bid(
uint96 amount,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
uint256 hostIndex
) external {
_assertIsHost(msg.sender, governanceOpts, proposalEngineOpts, hostIndex);
_bid(amount);
}
function _bid(uint96 amount) private onlyDelegateCall {
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active) {
revert WrongLifecycleError(lc);
}
}
_bidStatus = AuctionCrowdfundStatus.Busy;
IMarketWrapper market_ = market;
uint256 auctionId_ = auctionId;
if (market_.isFinalized(auctionId_)) {
revert AuctionFinalizedError(auctionId_);
}
if (market_.getCurrentHighestBidder(auctionId_) == address(this)) {
revert AlreadyHighestBidderError();
}
if (amount == type(uint96).max) {
amount = market_.getMinimumBid(auctionId_).safeCastUint256ToUint96();
}
uint96 totalContributions_ = totalContributions;
if (amount > totalContributions_) {
revert ExceedsTotalContributionsError(amount, totalContributions_);
}
uint96 maximumBid_ = maximumBid;
if (amount > maximumBid_) {
revert ExceedsMaximumBidError(amount, maximumBid_);
}
lastBid = amount;
(bool s, bytes memory r) = address(market_).delegatecall(
abi.encodeCall(IMarketWrapper.bid, (auctionId_, amount))
);
if (!s) {
r.rawRevert();
}
emit Bid(amount);
_bidStatus = AuctionCrowdfundStatus.Active;
}
function getCrowdfundLifecycle() public view override returns (CrowdfundLifecycle) {
AuctionCrowdfundStatus status = _bidStatus;
if (status == AuctionCrowdfundStatus.Busy) {
return CrowdfundLifecycle.Busy;
}
if (status == AuctionCrowdfundStatus.Finalized) {
return
address(party) != address(0)
? CrowdfundLifecycle.Won
: CrowdfundLifecycle.Lost;
}
if (block.timestamp >= expiry) {
return CrowdfundLifecycle.Expired;
}
return CrowdfundLifecycle.Active;
}
function _getFinalPrice() internal view override returns (uint256 price) {
return lastBid;
}
function _validateAuction(
IMarketWrapper market_,
uint256 auctionId_,
IERC721 nftContract_,
uint256 nftTokenId_
) internal view {
if (!market_.auctionIdMatchesToken(auctionId_, address(nftContract_), nftTokenId_)) {
revert InvalidAuctionIdError();
}
}
function _finalizeAuction(
CrowdfundLifecycle lc,
IMarketWrapper market_,
uint256 auctionId_,
uint96 lastBid_
) internal {
_bidStatus = AuctionCrowdfundStatus.Busy;
if (lastBid_ != 0 || lc == CrowdfundLifecycle.Active) {
if (!market_.isFinalized(auctionId_)) {
if (
lc == CrowdfundLifecycle.Expired &&
market_.getCurrentHighestBidder(auctionId_) != address(this)
) return;
(bool s, bytes memory r) = address(market_).call(
abi.encodeCall(IMarketWrapper.finalize, auctionId_)
);
if (!s) {
r.rawRevert();
}
}
}
}
}
文件 3 的 52:BuyCrowdfund.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "../party/Party.sol";
import "../utils/LibSafeERC721.sol";
import "../globals/IGlobals.sol";
import "../gatekeepers/IGateKeeper.sol";
import "./BuyCrowdfundBase.sol";
contract BuyCrowdfund is BuyCrowdfundBase {
using LibSafeERC721 for IERC721;
using LibSafeCast for uint256;
using LibRawResult for bytes;
struct BuyCrowdfundOptions {
string name;
string symbol;
uint256 customizationPresetId;
IERC721 nftContract;
uint256 nftTokenId;
uint40 duration;
uint96 maximumPrice;
address payable splitRecipient;
uint16 splitBps;
address initialContributor;
address initialDelegate;
uint96 minContribution;
uint96 maxContribution;
IGateKeeper gateKeeper;
bytes12 gateKeeperId;
bool onlyHostCanBuy;
FixedGovernanceOpts governanceOpts;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
}
uint256 public nftTokenId;
IERC721 public nftContract;
bool public onlyHostCanBuy;
constructor(IGlobals globals) BuyCrowdfundBase(globals) {}
function initialize(BuyCrowdfundOptions memory opts) external payable onlyConstructor {
if (opts.onlyHostCanBuy && 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,
proposalEngineOpts: opts.proposalEngineOpts
})
);
onlyHostCanBuy = opts.onlyHostCanBuy;
nftTokenId = opts.nftTokenId;
nftContract = opts.nftContract;
}
function buy(
address payable callTarget,
uint96 callValue,
bytes memory callData,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
uint256 hostIndex
) external onlyDelegateCall returns (Party party_) {
bool isValidatedGovernanceOpts;
if (onlyHostCanBuy) {
_assertIsHost(msg.sender, governanceOpts, proposalEngineOpts, hostIndex);
isValidatedGovernanceOpts = true;
} else if (address(gateKeeper) != address(0)) {
_assertIsContributor(msg.sender);
}
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active) {
revert WrongLifecycleError(lc);
}
}
settledPrice = type(uint96).max;
(bool success, bytes memory revertData) = _buy(
nftContract,
nftTokenId,
callTarget,
callValue,
callData
);
if (!success) {
if (revertData.length > 0) {
revertData.rawRevert();
} else {
revert FailedToBuyNFTError(nftContract, nftTokenId);
}
}
return
_finalize(
nftContract,
nftTokenId,
callValue,
governanceOpts,
proposalEngineOpts,
isValidatedGovernanceOpts
);
}
}
文件 4 的 52:BuyCrowdfundBase.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "../party/Party.sol";
import "../utils/LibSafeERC721.sol";
import "../globals/IGlobals.sol";
import "../gatekeepers/IGateKeeper.sol";
import "./Crowdfund.sol";
abstract contract BuyCrowdfundBase is Crowdfund {
using LibSafeERC721 for IERC721;
using LibSafeCast for uint256;
struct BuyCrowdfundBaseOptions {
string name;
string symbol;
uint256 customizationPresetId;
uint40 duration;
uint96 maximumPrice;
address payable splitRecipient;
uint16 splitBps;
address initialContributor;
address initialDelegate;
uint96 minContribution;
uint96 maxContribution;
IGateKeeper gateKeeper;
bytes12 gateKeeperId;
FixedGovernanceOpts governanceOpts;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
}
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);
uint40 public expiry;
uint96 public maximumPrice;
uint96 public settledPrice;
constructor(IGlobals globals) Crowdfund(globals) {}
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,
proposalEngineOpts: opts.proposalEngineOpts
})
);
}
function _buy(
IERC721 token,
uint256 tokenId,
address payable callTarget,
uint96 callValue,
bytes memory callData
) internal returns (bool success, bytes memory revertData) {
if (!_isCallAllowed(callTarget, callData, token)) {
revert CallProhibitedError(callTarget, callData);
}
{
uint96 maximumPrice_ = maximumPrice;
if (callValue > maximumPrice_) {
revert MaximumPriceError(callValue, maximumPrice_);
}
}
(bool s, bytes memory r) = callTarget.call{ value: callValue }(callData);
if (!s) {
return (false, r);
}
return (token.safeOwnerOf(tokenId) == address(this), "");
}
function _finalize(
IERC721[] memory tokens,
uint256[] memory tokenIds,
uint96 totalEthUsed,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
bool isValidatedGovernanceOpts
) internal returns (Party party_) {
{
uint96 totalContributions_ = totalContributions;
if (totalEthUsed > totalContributions_) {
revert ExceedsTotalContributionsError(totalEthUsed, totalContributions_);
}
}
if (totalEthUsed != 0) {
settledPrice = totalEthUsed;
party_ = _createParty(
governanceOpts,
proposalEngineOpts,
isValidatedGovernanceOpts,
tokens,
tokenIds
);
emit Won(party_, tokens, tokenIds, totalEthUsed);
} else {
settledPrice = 0;
expiry = uint40(block.timestamp);
emit Lost();
}
emit BatchMetadataUpdate(0, type(uint256).max);
}
function _finalize(
IERC721 token,
uint256 tokenId,
uint96 totalEthUsed,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
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,
proposalEngineOpts,
isValidatedGovernanceOpts
);
}
function getCrowdfundLifecycle() public view override returns (CrowdfundLifecycle) {
if (settledPrice != 0) {
return
address(party) != address(0)
? CrowdfundLifecycle.Won
: CrowdfundLifecycle.Busy;
}
if (block.timestamp >= expiry) {
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) {
if (callTarget == address(this)) {
return false;
}
if (callTarget == address(token) && callData.length >= 4) {
bytes4 selector;
assembly {
selector := and(
mload(add(callData, 32)),
0xffffffff00000000000000000000000000000000000000000000000000000000
)
}
if (
selector == IERC721.approve.selector ||
selector == IERC721.setApprovalForAll.selector
) {
return false;
}
}
return true;
}
}
文件 5 的 52:CollectionBatchBuyCrowdfund.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "../party/Party.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 {
string name;
string symbol;
uint256 customizationPresetId;
IERC721 nftContract;
bytes32 nftTokenIdsMerkleRoot;
uint40 duration;
uint96 maximumPrice;
address payable splitRecipient;
uint16 splitBps;
address initialContributor;
address initialDelegate;
uint96 minContribution;
uint96 maxContribution;
IGateKeeper gateKeeper;
bytes12 gateKeeperId;
FixedGovernanceOpts governanceOpts;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
}
struct BatchBuyArgs {
uint256[] tokenIds;
address payable[] callTargets;
uint96[] callValues;
bytes[] callDatas;
bytes32[][] proofs;
uint256 minTokensBought;
uint256 minTotalEthUsed;
FixedGovernanceOpts governanceOpts;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
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();
IERC721 public nftContract;
bytes32 public nftTokenIdsMerkleRoot;
constructor(IGlobals globals) BuyCrowdfundBase(globals) {}
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,
proposalEngineOpts: opts.proposalEngineOpts
})
);
nftContract = opts.nftContract;
nftTokenIdsMerkleRoot = opts.nftTokenIdsMerkleRoot;
}
function batchBuy(BatchBuyArgs memory args) external onlyDelegateCall returns (Party party_) {
_assertIsHost(msg.sender, args.governanceOpts, args.proposalEngineOpts, args.hostIndex);
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active) {
revert WrongLifecycleError(lc);
}
}
if (args.minTokensBought == 0) {
revert InvalidMinTokensBoughtError(0);
}
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();
}
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)) {
_verifyTokenId(args.tokenIds[i], root, args.proofs[i]);
}
uint256 balanceBefore = address(this).balance;
(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 (revertData.length > 0) {
revertData.rawRevert();
} else {
revert FailedToBuyNFTError(token, args.tokenIds[i]);
}
} else {
if (address(this).balance != balanceBefore) {
revert ContributionsSpentForFailedBuyError();
}
continue;
}
}
totalEthUsed += args.callValues[i];
++tokensBought;
tokens[tokensBought - 1] = token;
args.tokenIds[tokensBought - 1] = args.tokenIds[i];
}
if (totalEthUsed == 0) revert NothingBoughtError();
if (tokensBought < args.minTokensBought) {
revert NotEnoughTokensBoughtError(tokensBought, args.minTokensBought);
}
if (totalEthUsed < args.minTotalEthUsed) {
revert NotEnoughEthUsedError(totalEthUsed, args.minTotalEthUsed);
}
assembly {
mstore(tokens, tokensBought)
mstore(mload(args), tokensBought)
}
return
_finalize(
tokens,
args.tokenIds,
totalEthUsed,
args.governanceOpts,
args.proposalEngineOpts,
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();
}
}
文件 6 的 52:CollectionBuyCrowdfund.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "../party/Party.sol";
import "../utils/LibSafeERC721.sol";
import "../utils/LibRawResult.sol";
import "../globals/IGlobals.sol";
import "../gatekeepers/IGateKeeper.sol";
import "./BuyCrowdfundBase.sol";
contract CollectionBuyCrowdfund is BuyCrowdfundBase {
using LibSafeERC721 for IERC721;
using LibSafeCast for uint256;
using LibRawResult for bytes;
struct CollectionBuyCrowdfundOptions {
string name;
string symbol;
uint256 customizationPresetId;
IERC721 nftContract;
uint40 duration;
uint96 maximumPrice;
address payable splitRecipient;
uint16 splitBps;
address initialContributor;
address initialDelegate;
uint96 minContribution;
uint96 maxContribution;
IGateKeeper gateKeeper;
bytes12 gateKeeperId;
FixedGovernanceOpts governanceOpts;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
}
IERC721 public nftContract;
constructor(IGlobals globals) BuyCrowdfundBase(globals) {}
function initialize(
CollectionBuyCrowdfundOptions 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,
proposalEngineOpts: opts.proposalEngineOpts
})
);
nftContract = opts.nftContract;
}
function buy(
uint256 tokenId,
address payable callTarget,
uint96 callValue,
bytes memory callData,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
uint256 hostIndex
) external onlyDelegateCall returns (Party party_) {
_assertIsHost(msg.sender, governanceOpts, proposalEngineOpts, hostIndex);
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active) {
revert WrongLifecycleError(lc);
}
}
settledPrice = type(uint96).max;
(bool success, bytes memory revertData) = _buy(
nftContract,
tokenId,
callTarget,
callValue,
callData
);
if (!success) {
if (revertData.length > 0) {
revertData.rawRevert();
} else {
revert FailedToBuyNFTError(nftContract, tokenId);
}
}
return
_finalize(
nftContract,
tokenId,
callValue,
governanceOpts,
proposalEngineOpts,
true
);
}
}
文件 7 的 52:Crowdfund.sol
pragma solidity 0.8.20;
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 "../party/IPartyFactory.sol";
import "../renderers/RendererStorage.sol";
import "./CrowdfundNFT.sol";
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,
Lost,
Won
}
struct FixedGovernanceOpts {
Party partyImpl;
IPartyFactory partyFactory;
address[] hosts;
uint40 voteDuration;
uint40 executionDelay;
uint16 passThresholdBps;
uint16 feeBps;
address payable feeRecipient;
}
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;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
}
struct Contribution {
uint96 previousTotalContributions;
uint96 amount;
}
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();
uint40 private constant DISABLE_RAGEQUIT_PERMANENTLY = 0xab2cb21860;
IGlobals private immutable _GLOBALS;
Party public party;
uint96 public totalContributions;
IGateKeeper public gateKeeper;
bytes12 public gateKeeperId;
address payable public splitRecipient;
uint16 public splitBps;
bool private _splitRecipientHasBurned;
bytes32 public partyOptsHash;
mapping(address => address) public delegationsByContributor;
mapping(address => Contribution[]) internal _contributionsByContributor;
mapping(address => Claim) public claims;
uint96 public minContribution;
uint96 public maxContribution;
bool public emergencyExecuteDisabled;
constructor(IGlobals globals) CrowdfundNFT(globals) {
_GLOBALS = globals;
}
function _initialize(CrowdfundOptions memory opts) internal {
CrowdfundNFT._initialize(opts.name, opts.symbol, opts.customizationPresetId);
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);
}
partyOptsHash = _hashOpts(opts.governanceOpts, opts.proposalEngineOpts);
splitRecipient = opts.splitRecipient;
splitBps = opts.splitBps;
minContribution = opts.minContribution;
maxContribution = opts.maxContribution;
uint96 initialContribution = msg.value.safeCastUint256ToUint96();
if (initialContribution > 0) {
_setDelegate(opts.initialContributor, opts.initialDelegate);
_contribute(opts.initialContributor, opts.initialDelegate, initialContribution, 0, "");
}
gateKeeper = opts.gateKeeper;
gateKeeperId = opts.gateKeeperId;
}
function emergencyExecute(
address targetAddress,
bytes calldata targetCallData,
uint256 amountEth
) external payable onlyDelegateCall {
if (!_isPartyDao(msg.sender)) {
revert OnlyPartyDaoError(msg.sender);
}
if (emergencyExecuteDisabled) {
revert OnlyWhenEmergencyActionsAllowedError();
}
(bool success, bytes memory res) = targetAddress.call{ value: amountEth }(targetCallData);
if (!success) {
res.rawRevert();
}
emit EmergencyExecute(targetAddress, targetCallData, amountEth);
}
function disableEmergencyExecute(
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
uint256 hostIndex
) external onlyDelegateCall {
if (
!_isHost(msg.sender, governanceOpts, proposalEngineOpts, hostIndex) &&
!_isPartyDao(msg.sender)
) {
revert OnlyPartyDaoOrHostError(msg.sender);
}
emergencyExecuteDisabled = true;
emit EmergencyExecuteDisabled();
}
function burn(address payable contributor) public {
return _burn(contributor, getCrowdfundLifecycle(), party);
}
function activateOrRefund(address payable contributor) external {
burn(contributor);
}
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();
}
}
}
function batchActivateOrRefund(
address payable[] calldata contributors,
bool revertOnFailure
) external {
batchBurn(contributors, revertOnFailure);
}
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);
}
}
function contribute(address delegate, bytes memory gateData) external payable onlyDelegateCall {
_setDelegate(msg.sender, delegate);
_contribute(
msg.sender,
delegate,
msg.value.safeCastUint256ToUint96(),
totalContributions,
gateData
);
}
function contributeFor(
address recipient,
address initialDelegate,
bytes memory gateData
) external payable onlyDelegateCall {
_setDelegate(recipient, initialDelegate);
_contribute(
recipient,
initialDelegate,
msg.value.safeCastUint256ToUint96(),
totalContributions,
gateData
);
}
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();
}
}
}
function supportsInterface(
bytes4 interfaceId
) public pure override(ERC721Receiver, CrowdfundNFT) returns (bool) {
return
ERC721Receiver.supportsInterface(interfaceId) ||
CrowdfundNFT.supportsInterface(interfaceId);
}
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;
}
}
}
function getCrowdfundLifecycle() public view virtual returns (CrowdfundLifecycle lifecycle);
function _getFinalPrice() internal view virtual returns (uint256);
function _assertIsHost(
address who,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
uint256 hostIndex
) internal view {
if (!_isHost(who, governanceOpts, proposalEngineOpts, hostIndex)) {
revert OnlyPartyHostError();
}
}
function _isHost(
address who,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
uint256 hostIndex
) private view returns (bool isHost) {
if (hostIndex < governanceOpts.hosts.length && who == governanceOpts.hosts[hostIndex]) {
_assertValidOpts(governanceOpts, proposalEngineOpts);
return true;
}
return false;
}
function _isPartyDao(address who) private view returns (bool isPartyDao) {
return who == _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);
}
function _assertIsContributor(address who) internal view {
if (_contributionsByContributor[who].length == 0) {
revert OnlyContributorError();
}
}
function _createParty(
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
bool governanceOptsAlreadyValidated,
IERC721[] memory preciousTokens,
uint256[] memory preciousTokenIds
) internal returns (Party party_) {
if (party != Party(payable(0))) {
revert PartyAlreadyExistsError(party);
}
if (!governanceOptsAlreadyValidated) {
_assertValidOpts(governanceOpts, proposalEngineOpts);
}
RendererStorage rendererStorage = RendererStorage(
_GLOBALS.getAddress(LibGlobals.GLOBAL_RENDERER_STORAGE)
);
address[] memory authorities = new address[](1);
authorities[0] = address(this);
party = party_ = governanceOpts.partyFactory.createParty(
governanceOpts.partyImpl,
authorities,
Party.PartyOptions({
name: name,
symbol: symbol,
customizationPresetId: rendererStorage.getPresetFor(address(this)),
governance: PartyGovernance.GovernanceOpts({
hosts: governanceOpts.hosts,
voteDuration: governanceOpts.voteDuration,
executionDelay: governanceOpts.executionDelay,
passThresholdBps: governanceOpts.passThresholdBps,
totalVotingPower: _getFinalPrice().safeCastUint256ToUint96(),
feeBps: governanceOpts.feeBps,
feeRecipient: governanceOpts.feeRecipient
}),
proposalEngine: proposalEngineOpts
}),
preciousTokens,
preciousTokenIds,
DISABLE_RAGEQUIT_PERMANENTLY
);
for (uint256 i; i < preciousTokens.length; ++i) {
preciousTokens[i].transferFrom(address(this), address(party_), preciousTokenIds[i]);
}
}
function _createParty(
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
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,
proposalEngineOpts,
governanceOptsAlreadyValidated,
tokens,
tokenIds
);
}
function _assertValidOpts(
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts
) private view {
bytes32 partyOptsHash_ = _hashOpts(governanceOpts, proposalEngineOpts);
if (partyOptsHash_ != partyOptsHash) {
revert InvalidGovernanceOptionsError();
}
}
function _hashOpts(
FixedGovernanceOpts memory govOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts
) internal pure returns (bytes32 h) {
return keccak256(abi.encode(govOpts, proposalEngineOpts));
}
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) {
ethOwed += c.amount;
} else if (c.previousTotalContributions + c.amount <= totalEthUsed) {
ethUsed += c.amount;
} else {
uint256 partialEthUsed = totalEthUsed - c.previousTotalContributions;
ethUsed += partialEthUsed;
ethOwed = c.amount - partialEthUsed;
}
}
}
address splitRecipient_ = splitRecipient;
uint256 splitBps_ = splitBps;
if (splitRecipient_ == address(0)) {
splitBps_ = 0;
}
votingPower = ((1e4 - splitBps_) * ethUsed) / 1e4;
if (splitRecipient_ == contributor) {
votingPower += (splitBps_ * totalEthUsed + (1e4 - 1)) / 1e4;
}
}
function _setDelegate(address contributor, address delegate) private {
if (delegate == address(0)) revert InvalidDelegateError();
address oldDelegate = delegationsByContributor[contributor];
if (oldDelegate == delegate) return;
if (msg.sender != contributor && oldDelegate != address(0)) return;
delegationsByContributor[contributor] = delegate;
}
function _contribute(
address contributor,
address delegate,
uint96 amount,
uint96 previousTotalContributions,
bytes memory gateData
) private {
if (contributor == address(this)) revert InvalidContributorError();
if (amount == 0) return;
{
IGateKeeper _gateKeeper = gateKeeper;
if (_gateKeeper != IGateKeeper(address(0))) {
if (!_gateKeeper.isAllowed(msg.sender, gateKeeperId, gateData)) {
revert NotAllowedByGateKeeperError(
msg.sender,
_gateKeeper,
gateKeeperId,
gateData
);
}
}
}
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active) {
revert WrongLifecycleError(lc);
}
}
totalContributions += amount;
Contribution[] storage contributions = _contributionsByContributor[contributor];
uint256 numContributions = contributions.length;
uint96 ethContributed;
for (uint256 i; i < numContributions; ++i) {
ethContributed += contributions[i].amount;
}
if (ethContributed + amount < minContribution) {
revert BelowMinimumContributionsError(ethContributed + amount, minContribution);
}
if (ethContributed + amount > maxContribution) {
revert AboveMaximumContributionsError(ethContributed + amount, maxContribution);
}
emit Contributed(msg.sender, contributor, amount, delegate, previousTotalContributions);
emit MetadataUpdate(uint256(uint160(contributor)));
if (numContributions >= 1) {
Contribution memory lastContribution = contributions[numContributions - 1];
uint256 totalContributionsAmountForReuse = lastContribution.previousTotalContributions +
lastContribution.amount;
if (totalContributionsAmountForReuse == previousTotalContributions) {
lastContribution.amount += amount;
contributions[numContributions - 1] = lastContribution;
return;
}
}
contributions.push(
Contribution({ previousTotalContributions: previousTotalContributions, amount: amount })
);
if (numContributions == 0) {
_mint(contributor);
}
}
function _burn(address payable contributor, CrowdfundLifecycle lc, Party party_) private {
if (lc == CrowdfundLifecycle.Won) {
if (party_ == Party(payable(0))) {
revert NoPartyError();
}
} else if (lc != CrowdfundLifecycle.Lost) {
revert WrongLifecycleError(lc);
}
{
address splitRecipient_ = splitRecipient;
if (contributor == splitRecipient_) {
if (_splitRecipientHasBurned) {
revert SplitRecipientAlreadyBurnedError();
}
_splitRecipientHasBurned = true;
}
if (splitRecipient_ != contributor || _doesTokenExistFor(contributor)) {
CrowdfundNFT._burn(contributor);
}
}
(uint256 ethUsed, uint256 ethOwed, uint256 votingPower) = _getFinalContribution(
contributor
);
if (votingPower > 0) {
address delegate = delegationsByContributor[contributor];
if (delegate == address(0)) {
delegate = contributor;
}
try party_.mint(contributor, votingPower, delegate) returns (uint256) {
} catch {
uint256 tokenId = party_.mint(address(this), votingPower, delegate);
claims[contributor].governanceTokenId = tokenId;
}
}
(bool s, ) = contributor.call{ value: ethOwed }("");
if (!s) {
claims[contributor].refund = ethOwed;
}
emit Burned(contributor, ethUsed, ethOwed, votingPower);
}
}
function _hashFixedGovernanceOpts(
Crowdfund.FixedGovernanceOpts memory opts
) pure returns (bytes32 h) {
assembly {
let oldHostsFieldValue := mload(opts)
mstore(opts, keccak256(add(oldHostsFieldValue, 0x20), mul(mload(oldHostsFieldValue), 32)))
h := keccak256(opts, 0xC0)
mstore(opts, oldHostsFieldValue)
}
}
文件 8 的 52:CrowdfundFactory.sol
pragma solidity 0.8.20;
import { LibRawResult } from "../utils/LibRawResult.sol";
import { Proxy } from "../utils/Proxy.sol";
import { Implementation } from "../utils/Implementation.sol";
import { IGateKeeper } from "../gatekeepers/IGateKeeper.sol";
import { AuctionCrowdfund, AuctionCrowdfundBase } from "./AuctionCrowdfund.sol";
import { BuyCrowdfund } from "./BuyCrowdfund.sol";
import { CollectionBuyCrowdfund } from "./CollectionBuyCrowdfund.sol";
import { RollingAuctionCrowdfund } from "./RollingAuctionCrowdfund.sol";
import { CollectionBatchBuyCrowdfund } from "./CollectionBatchBuyCrowdfund.sol";
import { InitialETHCrowdfund, ETHCrowdfundBase } from "./InitialETHCrowdfund.sol";
import { ReraiseETHCrowdfund } from "./ReraiseETHCrowdfund.sol";
import { MetadataProvider } from "../renderers/MetadataProvider.sol";
import { Party } from "../party/Party.sol";
contract CrowdfundFactory {
using LibRawResult for bytes;
event BuyCrowdfundCreated(
address indexed creator,
BuyCrowdfund indexed crowdfund,
BuyCrowdfund.BuyCrowdfundOptions opts
);
event AuctionCrowdfundCreated(
address indexed creator,
AuctionCrowdfund indexed crowdfund,
AuctionCrowdfundBase.AuctionCrowdfundOptions opts
);
event CollectionBuyCrowdfundCreated(
address indexed creator,
CollectionBuyCrowdfund indexed crowdfund,
CollectionBuyCrowdfund.CollectionBuyCrowdfundOptions opts
);
event RollingAuctionCrowdfundCreated(
address indexed creator,
RollingAuctionCrowdfund indexed crowdfund,
AuctionCrowdfundBase.AuctionCrowdfundOptions opts,
bytes32 allowedAuctionsMerkleRoot
);
event CollectionBatchBuyCrowdfundCreated(
address indexed creator,
CollectionBatchBuyCrowdfund indexed crowdfund,
CollectionBatchBuyCrowdfund.CollectionBatchBuyCrowdfundOptions opts
);
event InitialETHCrowdfundCreated(
address indexed creator,
InitialETHCrowdfund indexed crowdfund,
Party indexed party,
InitialETHCrowdfund.InitialETHCrowdfundOptions crowdfundOpts,
InitialETHCrowdfund.ETHPartyOptions partyOpts
);
event ReraiseETHCrowdfundCreated(
address indexed creator,
ReraiseETHCrowdfund indexed crowdfund,
ETHCrowdfundBase.ETHCrowdfundOptions opts
);
function createBuyCrowdfund(
BuyCrowdfund crowdfundImpl,
BuyCrowdfund.BuyCrowdfundOptions memory opts,
bytes memory createGateCallData
) external payable returns (BuyCrowdfund inst) {
opts.gateKeeperId = _prepareGate(opts.gateKeeper, opts.gateKeeperId, createGateCallData);
inst = BuyCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(BuyCrowdfund.initialize, (opts))
)
)
);
emit BuyCrowdfundCreated(msg.sender, inst, opts);
}
function createAuctionCrowdfund(
AuctionCrowdfund crowdfundImpl,
AuctionCrowdfundBase.AuctionCrowdfundOptions memory opts,
bytes memory createGateCallData
) external payable returns (AuctionCrowdfund inst) {
opts.gateKeeperId = _prepareGate(opts.gateKeeper, opts.gateKeeperId, createGateCallData);
inst = AuctionCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(AuctionCrowdfund.initialize, (opts))
)
)
);
emit AuctionCrowdfundCreated(msg.sender, inst, opts);
}
function createRollingAuctionCrowdfund(
RollingAuctionCrowdfund crowdfundImpl,
AuctionCrowdfundBase.AuctionCrowdfundOptions memory opts,
bytes32 allowedAuctionsMerkleRoot,
bytes memory createGateCallData
) external payable returns (RollingAuctionCrowdfund inst) {
opts.gateKeeperId = _prepareGate(opts.gateKeeper, opts.gateKeeperId, createGateCallData);
inst = RollingAuctionCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(
RollingAuctionCrowdfund.initialize,
(opts, allowedAuctionsMerkleRoot)
)
)
)
);
emit RollingAuctionCrowdfundCreated(msg.sender, inst, opts, allowedAuctionsMerkleRoot);
}
function createCollectionBuyCrowdfund(
CollectionBuyCrowdfund crowdfundImpl,
CollectionBuyCrowdfund.CollectionBuyCrowdfundOptions memory opts,
bytes memory createGateCallData
) external payable returns (CollectionBuyCrowdfund inst) {
opts.gateKeeperId = _prepareGate(opts.gateKeeper, opts.gateKeeperId, createGateCallData);
inst = CollectionBuyCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(CollectionBuyCrowdfund.initialize, (opts))
)
)
);
emit CollectionBuyCrowdfundCreated(msg.sender, inst, opts);
}
function createCollectionBatchBuyCrowdfund(
CollectionBatchBuyCrowdfund crowdfundImpl,
CollectionBatchBuyCrowdfund.CollectionBatchBuyCrowdfundOptions memory opts,
bytes memory createGateCallData
) external payable returns (CollectionBatchBuyCrowdfund inst) {
opts.gateKeeperId = _prepareGate(opts.gateKeeper, opts.gateKeeperId, createGateCallData);
inst = CollectionBatchBuyCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(CollectionBatchBuyCrowdfund.initialize, (opts))
)
)
);
emit CollectionBatchBuyCrowdfundCreated(msg.sender, inst, opts);
}
function createInitialETHCrowdfund(
InitialETHCrowdfund crowdfundImpl,
InitialETHCrowdfund.InitialETHCrowdfundOptions memory crowdfundOpts,
InitialETHCrowdfund.ETHPartyOptions memory partyOpts,
bytes memory createGateCallData
) external payable returns (InitialETHCrowdfund inst) {
return
createInitialETHCrowdfundWithMetadata(
crowdfundImpl,
crowdfundOpts,
partyOpts,
MetadataProvider(address(0)),
"",
createGateCallData
);
}
function createInitialETHCrowdfundWithMetadata(
InitialETHCrowdfund crowdfundImpl,
InitialETHCrowdfund.InitialETHCrowdfundOptions memory crowdfundOpts,
InitialETHCrowdfund.ETHPartyOptions memory partyOpts,
MetadataProvider customMetadataProvider,
bytes memory customMetadata,
bytes memory createGateCallData
) public payable returns (InitialETHCrowdfund inst) {
crowdfundOpts.gateKeeperId = _prepareGate(
crowdfundOpts.gateKeeper,
crowdfundOpts.gateKeeperId,
createGateCallData
);
inst = InitialETHCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(
InitialETHCrowdfund.initialize,
(crowdfundOpts, partyOpts, customMetadataProvider, customMetadata)
)
)
)
);
emit InitialETHCrowdfundCreated(msg.sender, inst, inst.party(), crowdfundOpts, partyOpts);
}
function createReraiseETHCrowdfund(
ReraiseETHCrowdfund crowdfundImpl,
ETHCrowdfundBase.ETHCrowdfundOptions memory opts,
bytes memory createGateCallData
) external payable returns (ReraiseETHCrowdfund inst) {
opts.gateKeeperId = _prepareGate(opts.gateKeeper, opts.gateKeeperId, createGateCallData);
inst = ReraiseETHCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(ReraiseETHCrowdfund.initialize, (opts))
)
)
);
emit ReraiseETHCrowdfundCreated(msg.sender, inst, opts);
}
function _prepareGate(
IGateKeeper gateKeeper,
bytes12 gateKeeperId,
bytes memory createGateCallData
) private returns (bytes12 newGateKeeperId) {
if (address(gateKeeper) == address(0) || gateKeeperId != bytes12(0)) {
return gateKeeperId;
}
(bool s, bytes memory r) = address(gateKeeper).call(createGateCallData);
if (!s) {
r.rawRevert();
}
return abi.decode(r, (bytes12));
}
}
文件 9 的 52:CrowdfundNFT.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "../utils/ReadOnlyDelegateCall.sol";
import "../utils/EIP165.sol";
import "../utils/IERC4906.sol";
import "../globals/IGlobals.sol";
import "../globals/LibGlobals.sol";
import "../renderers/RendererStorage.sol";
contract CrowdfundNFT is IERC721, IERC4906, EIP165, ReadOnlyDelegateCall {
error AlreadyMintedError(address owner, uint256 tokenId);
error AlreadyBurnedError(address owner, uint256 tokenId);
error InvalidTokenError(uint256 tokenId);
error InvalidAddressError();
IGlobals private immutable _GLOBALS;
string public name;
string public symbol;
mapping(uint256 => address) private _owners;
modifier alwaysRevert() {
revert("ALWAYS FAILING");
_;
}
constructor(IGlobals globals) {
_GLOBALS = globals;
}
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);
}
}
function transferFrom(address, address, uint256) external pure alwaysRevert {}
function safeTransferFrom(address, address, uint256) external pure alwaysRevert {}
function safeTransferFrom(
address,
address,
uint256,
bytes calldata
) external pure alwaysRevert {}
function approve(address, uint256) external pure alwaysRevert {}
function setApprovalForAll(address, bool) external pure alwaysRevert {}
function getApproved(uint256) external pure returns (address) {
return address(0);
}
function isApprovedForAll(address, address) external pure returns (bool) {
return false;
}
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return
super.supportsInterface(interfaceId) ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x49064906;
}
function tokenURI(uint256) external view returns (string memory) {
return _delegateToRenderer();
}
function contractURI() external view returns (string memory) {
return _delegateToRenderer();
}
function ownerOf(uint256 tokenId) external view returns (address owner) {
owner = _owners[tokenId];
if (owner == address(0)) {
revert InvalidTokenError(tokenId);
}
}
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(
_GLOBALS.getAddress(LibGlobals.GLOBAL_CF_NFT_RENDER_IMPL),
msg.data
);
assert(false);
return "";
}
}
文件 10 的 52:EIP165.sol
pragma solidity 0.8.20;
abstract contract EIP165 {
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return interfaceId == this.supportsInterface.selector;
}
}
文件 11 的 52:ERC1155.sol
pragma solidity ^0.8;
import "../../tokens/IERC1155.sol";
abstract contract ERC1155 is IERC1155 {
event URI(string value, uint256 indexed id);
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => bool)) public isApprovedForAll;
function uri(uint256 id) public view virtual returns (string memory);
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");
uint256 id;
uint256 amount;
for (uint256 i; i < ids.length; ) {
id = ids[i];
amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
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 {
for (uint256 i; i < owners.length; ++i) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0xd9b67a26 ||
interfaceId == 0x0e89341c;
}
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;
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
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;
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
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);
}
}
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;
}
}
文件 12 的 52:ERC1155Receiver.sol
pragma solidity ^0.8;
import "../vendor/solmate/ERC1155.sol";
import "../utils/EIP165.sol";
abstract contract ERC1155Receiver is EIP165, ERC1155TokenReceiverBase {
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return
super.supportsInterface(interfaceId) ||
interfaceId == type(ERC1155TokenReceiverBase).interfaceId;
}
}
文件 13 的 52:ERC721.sol
pragma solidity >=0.8.0;
import "../../tokens/IERC721.sol";
import "../../utils/EIP165.sol";
abstract contract ERC721 is IERC721, EIP165 {
string public name;
string public symbol;
function tokenURI(uint256 id ) public virtual returns (string memory);
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];
}
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
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"
);
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"
);
}
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return
super.supportsInterface(interfaceId) ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
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");
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
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"
);
}
}
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
文件 14 的 52:ERC721Receiver.sol
pragma solidity ^0.8;
import "./IERC721Receiver.sol";
import "../utils/EIP165.sol";
import "../vendor/solmate/ERC721.sol";
abstract contract ERC721Receiver is IERC721Receiver, EIP165, ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes memory
) public virtual override(IERC721Receiver, ERC721TokenReceiver) returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return
EIP165.supportsInterface(interfaceId) ||
interfaceId == type(IERC721Receiver).interfaceId;
}
}
文件 15 的 52:ETHCrowdfundBase.sol
pragma solidity 0.8.20;
import "../utils/LibAddress.sol";
import "../utils/LibSafeCast.sol";
import "../party/Party.sol";
import "../gatekeepers/IGateKeeper.sol";
contract ETHCrowdfundBase is Implementation {
using LibRawResult for bytes;
using LibSafeCast for uint256;
using LibAddress for address payable;
enum CrowdfundLifecycle {
Invalid,
Active,
Lost,
Won,
Finalized
}
struct ETHCrowdfundOptions {
Party party;
address payable initialContributor;
address initialDelegate;
uint96 minContribution;
uint96 maxContribution;
bool disableContributingForExistingCard;
uint96 minTotalContributions;
uint96 maxTotalContributions;
uint16 exchangeRateBps;
uint16 fundingSplitBps;
address payable fundingSplitRecipient;
uint40 duration;
IGateKeeper gateKeeper;
bytes12 gateKeeperId;
}
error WrongLifecycleError(CrowdfundLifecycle lc);
error NotAllowedByGateKeeperError(
address contributor,
IGateKeeper gateKeeper,
bytes12 gateKeeperId,
bytes gateData
);
error OnlyPartyHostError();
error OnlyPartyDaoError(address notDao);
error OnlyPartyDaoOrHostError(address notDao);
error NotOwnerError(uint256 tokenId);
error OnlyWhenEmergencyActionsAllowedError();
error InvalidDelegateError();
error NotEnoughContributionsError(uint96 totalContribution, uint96 minTotalContributions);
error MinGreaterThanMaxError(uint96 min, uint96 max);
error MaxTotalContributionsCannotBeZeroError(uint96 maxTotalContributions);
error BelowMinimumContributionsError(uint96 contributions, uint96 minContributions);
error AboveMaximumContributionsError(uint96 contributions, uint96 maxContributions);
error InvalidExchangeRateError(uint16 exchangeRateBps);
error ContributingForExistingCardDisabledError();
error ZeroVotingPowerError();
error FundingSplitAlreadyPaidError();
error FundingSplitNotConfiguredError();
event Contributed(
address indexed sender,
address indexed contributor,
uint256 amount,
address delegate
);
event Finalized();
event FundingSplitSent(address indexed fundingSplitRecipient, uint256 amount);
event EmergencyExecuteDisabled();
event EmergencyExecute(address target, bytes data, uint256 amountEth);
IGlobals private immutable _GLOBALS;
Party public party;
uint96 public minContribution;
uint96 public maxContribution;
bool public disableContributingForExistingCard;
bool public fundingSplitPaid;
bool public emergencyExecuteDisabled;
uint96 public minTotalContributions;
uint96 public maxTotalContributions;
uint96 public totalContributions;
uint40 public expiry;
uint16 public exchangeRateBps;
uint16 public fundingSplitBps;
address payable public fundingSplitRecipient;
IGateKeeper public gateKeeper;
bytes12 public gateKeeperId;
mapping(address => address) public delegationsByContributor;
constructor(IGlobals globals) {
_GLOBALS = globals;
}
function _initialize(ETHCrowdfundOptions memory opts) internal {
if (opts.minContribution > opts.maxContribution) {
revert MinGreaterThanMaxError(opts.minContribution, opts.maxContribution);
}
minContribution = opts.minContribution;
maxContribution = opts.maxContribution;
if (opts.minTotalContributions > opts.maxTotalContributions) {
revert MinGreaterThanMaxError(opts.minTotalContributions, opts.maxTotalContributions);
}
minTotalContributions = opts.minTotalContributions;
if (opts.maxTotalContributions == 0) {
revert MaxTotalContributionsCannotBeZeroError(opts.maxTotalContributions);
}
maxTotalContributions = opts.maxTotalContributions;
party = opts.party;
expiry = uint40(block.timestamp + opts.duration);
if (opts.exchangeRateBps == 0) revert InvalidExchangeRateError(opts.exchangeRateBps);
exchangeRateBps = opts.exchangeRateBps;
fundingSplitBps = opts.fundingSplitBps;
fundingSplitRecipient = opts.fundingSplitRecipient;
disableContributingForExistingCard = opts.disableContributingForExistingCard;
}
function getCrowdfundLifecycle() public view returns (CrowdfundLifecycle lifecycle) {
if (maxTotalContributions == 0) {
return CrowdfundLifecycle.Invalid;
}
uint256 expiry_ = expiry;
if (expiry_ == 0) {
return CrowdfundLifecycle.Finalized;
}
if (block.timestamp >= expiry_) {
if (totalContributions >= minTotalContributions) {
return CrowdfundLifecycle.Won;
} else {
return CrowdfundLifecycle.Lost;
}
}
return CrowdfundLifecycle.Active;
}
function _processContribution(
address payable contributor,
address delegate,
uint96 amount
) internal returns (uint96 votingPower) {
address oldDelegate = delegationsByContributor[contributor];
if (msg.sender == contributor || oldDelegate == address(0)) {
delegationsByContributor[contributor] = delegate;
} else {
delegate = oldDelegate;
}
emit Contributed(msg.sender, contributor, amount, delegate);
if (amount == 0) return 0;
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active) {
revert WrongLifecycleError(lc);
}
uint96 maxContribution_ = maxContribution;
if (amount > maxContribution_) {
revert AboveMaximumContributionsError(amount, maxContribution_);
}
uint96 newTotalContributions = totalContributions + amount;
uint96 maxTotalContributions_ = maxTotalContributions;
if (newTotalContributions >= maxTotalContributions_) {
totalContributions = maxTotalContributions_;
_finalize(maxTotalContributions_);
uint96 refundAmount = newTotalContributions - maxTotalContributions;
if (refundAmount > 0) {
amount -= refundAmount;
payable(msg.sender).transferEth(refundAmount);
}
} else {
totalContributions = newTotalContributions;
}
uint96 minContribution_ = minContribution;
if (amount < minContribution_) {
revert BelowMinimumContributionsError(amount, minContribution_);
}
address payable fundingSplitRecipient_ = fundingSplitRecipient;
uint16 fundingSplitBps_ = fundingSplitBps;
if (fundingSplitRecipient_ != address(0) && fundingSplitBps_ > 0) {
amount = (amount * (1e4 - fundingSplitBps_)) / 1e4;
}
votingPower = (amount * exchangeRateBps) / 1e4;
if (votingPower == 0) revert ZeroVotingPowerError();
}
function convertVotingPowerToContribution(
uint96 votingPower
) public view returns (uint96 amount) {
amount = (votingPower * 1e4) / exchangeRateBps;
address payable fundingSplitRecipient_ = fundingSplitRecipient;
uint16 fundingSplitBps_ = fundingSplitBps;
if (fundingSplitRecipient_ != address(0) && fundingSplitBps_ > 0) {
amount = (amount * 1e4) / (1e4 - fundingSplitBps_);
}
}
function finalize() external {
uint96 totalContributions_ = totalContributions;
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc == CrowdfundLifecycle.Active) {
if (!party.isHost(msg.sender)) revert OnlyPartyHostError();
uint96 minTotalContributions_ = minTotalContributions;
if (totalContributions_ < minTotalContributions_) {
revert NotEnoughContributionsError(totalContributions_, minTotalContributions_);
}
} else {
if (lc != CrowdfundLifecycle.Won) {
revert WrongLifecycleError(lc);
}
}
_finalize(totalContributions_);
}
function _finalize(uint96 totalContributions_) internal {
delete expiry;
address payable fundingSplitRecipient_ = fundingSplitRecipient;
uint16 fundingSplitBps_ = fundingSplitBps;
if (fundingSplitRecipient_ != address(0) && fundingSplitBps_ > 0) {
totalContributions_ -= (totalContributions_ * fundingSplitBps_) / 1e4;
}
uint96 newVotingPower = (totalContributions_ * exchangeRateBps) / 1e4;
party.increaseTotalVotingPower(newVotingPower);
payable(address(party)).transferEth(totalContributions_);
emit Finalized();
}
function sendFundingSplit() external returns (uint96 splitAmount) {
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Finalized) revert WrongLifecycleError(lc);
if (fundingSplitPaid) revert FundingSplitAlreadyPaidError();
address payable fundingSplitRecipient_ = fundingSplitRecipient;
uint16 fundingSplitBps_ = fundingSplitBps;
if (fundingSplitRecipient_ == address(0) || fundingSplitBps_ == 0) {
revert FundingSplitNotConfiguredError();
}
fundingSplitPaid = true;
splitAmount = (totalContributions * fundingSplitBps_) / 1e4;
payable(fundingSplitRecipient_).transferEth(splitAmount);
emit FundingSplitSent(fundingSplitRecipient_, splitAmount);
}
function emergencyExecute(
address targetAddress,
bytes calldata targetCallData,
uint256 amountEth
) external payable {
if (_GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET) != msg.sender) {
revert OnlyPartyDaoError(msg.sender);
}
if (emergencyExecuteDisabled) {
revert OnlyWhenEmergencyActionsAllowedError();
}
(bool success, bytes memory res) = targetAddress.call{ value: amountEth }(targetCallData);
if (!success) {
res.rawRevert();
}
emit EmergencyExecute(targetAddress, targetCallData, amountEth);
}
function disableEmergencyExecute() external {
if (
!party.isHost(msg.sender) &&
_GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET) != msg.sender
) {
revert OnlyPartyDaoOrHostError(msg.sender);
}
emergencyExecuteDisabled = true;
emit EmergencyExecuteDisabled();
}
}
文件 16 的 52:IERC1155.sol
pragma solidity ^0.8;
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);
}
文件 17 的 52:IERC165.sol
pragma solidity ^0.8.19;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 18 的 52:IERC20.sol
pragma solidity ^0.8;
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);
}
文件 19 的 52:IERC2981.sol
pragma solidity ^0.8.19;
import "../utils/introspection/IERC165.sol";
interface IERC2981 is IERC165 {
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}
文件 20 的 52:IERC4906.sol
pragma solidity ^0.8;
interface IERC4906 {
event MetadataUpdate(uint256 _tokenId);
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}
文件 21 的 52:IERC721.sol
pragma solidity ^0.8;
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);
}
文件 22 的 52:IERC721Receiver.sol
pragma solidity ^0.8;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes memory data
) external returns (bytes4);
}
文件 23 的 52:IGateKeeper.sol
pragma solidity 0.8.20;
interface IGateKeeper {
function isAllowed(
address participant,
bytes12 id,
bytes memory userData
) external view returns (bool);
}
文件 24 的 52:IGlobals.sol
pragma solidity 0.8.20;
import "../utils/Implementation.sol";
interface IGlobals {
function multiSig() external view returns (address);
function getBytes32(uint256 key) external view returns (bytes32);
function getUint256(uint256 key) external view returns (uint256);
function getBool(uint256 key) external view returns (bool);
function getAddress(uint256 key) external view returns (address);
function getImplementation(uint256 key) external view returns (Implementation);
function getIncludesBytes32(uint256 key, bytes32 value) external view returns (bool);
function getIncludesUint256(uint256 key, uint256 value) external view returns (bool);
function getIncludesAddress(uint256 key, address value) external view returns (bool);
function setBytes32(uint256 key, bytes32 value) external;
function setUint256(uint256 key, uint256 value) external;
function setBool(uint256 key, bool value) external;
function setAddress(uint256 key, address value) external;
function setIncludesBytes32(uint256 key, bytes32 value, bool isIncluded) external;
function setIncludesUint256(uint256 key, uint256 value, bool isIncluded) external;
function setIncludesAddress(uint256 key, address value, bool isIncluded) external;
}
文件 25 的 52:IMarketWrapper.sol
pragma solidity 0.8.20;
interface IMarketWrapper {
function auctionIdMatchesToken(
uint256 auctionId,
address nftContract,
uint256 tokenId
) external view returns (bool);
function getMinimumBid(uint256 auctionId) external view returns (uint256);
function getCurrentHighestBidder(uint256 auctionId) external view returns (address);
function bid(uint256 auctionId, uint256 bidAmount) external;
function isFinalized(uint256 auctionId) external view returns (bool);
function finalize(uint256 auctionId) external;
}
文件 26 的 52:IMetadataProvider.sol
pragma solidity ^0.8;
interface IMetadataProvider {
function supportsRegistrars() external view returns (bool);
function getMetadata(
address instance,
uint256 tokenId
) external view returns (bytes memory metadata);
}
文件 27 的 52:IPartyFactory.sol
pragma solidity 0.8.20;
import { Party } from "../party/Party.sol";
import { IERC721 } from "../tokens/IERC721.sol";
import { MetadataProvider } from "../renderers/MetadataProvider.sol";
interface IPartyFactory {
event PartyCreated(
Party indexed party,
Party.PartyOptions opts,
IERC721[] preciousTokens,
uint256[] preciousTokenIds,
address creator
);
function createParty(
Party partyImpl,
address[] memory authorities,
Party.PartyOptions calldata opts,
IERC721[] memory preciousTokens,
uint256[] memory preciousTokenIds,
uint40 rageQuitTimestamp
) external returns (Party party);
function createPartyWithMetadata(
Party partyImpl,
address[] memory authorities,
Party.PartyOptions memory opts,
IERC721[] memory preciousTokens,
uint256[] memory preciousTokenIds,
uint40 rageQuitTimestamp,
MetadataProvider provider,
bytes memory metadata
) external returns (Party party);
}
文件 28 的 52:IProposalExecutionEngine.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
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;
function executeProposal(
ExecuteProposalParams memory params
) external returns (bytes memory nextProgressData);
function cancelProposal(uint256 proposalId) external;
}
文件 29 的 52:ITokenDistributor.sol
pragma solidity 0.8.20;
import "../tokens/IERC20.sol";
import "../party/Party.sol";
interface ITokenDistributor {
enum TokenType {
Native,
Erc20
}
struct DistributionInfo {
TokenType tokenType;
uint256 distributionId;
Party party;
address payable feeRecipient;
address token;
uint128 memberSupply;
uint128 fee;
uint96 totalShares;
}
event DistributionCreated(Party indexed party, DistributionInfo info);
event DistributionFeeClaimed(
Party indexed party,
address indexed feeRecipient,
TokenType tokenType,
address token,
uint256 amount
);
event DistributionClaimedByPartyToken(
Party indexed party,
uint256 indexed partyTokenId,
address indexed owner,
TokenType tokenType,
address token,
uint256 amountClaimed
);
function createNativeDistribution(
Party party,
address payable feeRecipient,
uint16 feeBps
) external payable returns (DistributionInfo memory info);
function createErc20Distribution(
IERC20 token,
Party party,
address payable feeRecipient,
uint16 feeBps
) external returns (DistributionInfo memory info);
function claim(
DistributionInfo calldata info,
uint256 partyTokenId
) external returns (uint128 amountClaimed);
function claimFee(DistributionInfo calldata info, address payable recipient) external;
function batchClaim(
DistributionInfo[] calldata infos,
uint256[] calldata partyTokenIds
) external returns (uint128[] memory amountsClaimed);
function batchClaimFee(
DistributionInfo[] calldata infos,
address payable[] calldata recipients
) external;
function getClaimAmount(
DistributionInfo calldata info,
uint256 partyTokenId
) external view returns (uint128);
function wasFeeClaimed(Party party, uint256 distributionId) external view returns (bool);
function hasPartyTokenIdClaimed(
Party party,
uint256 partyTokenId,
uint256 distributionId
) external view returns (bool);
function getRemainingMemberSupply(
Party party,
uint256 distributionId
) external view returns (uint128);
}
文件 30 的 52:Implementation.sol
pragma solidity 0.8.20;
abstract contract Implementation {
error OnlyDelegateCallError();
error OnlyConstructorError();
address public immutable IMPL;
constructor() {
IMPL = address(this);
}
modifier onlyDelegateCall() virtual {
if (address(this) == IMPL) {
revert OnlyDelegateCallError();
}
_;
}
modifier onlyConstructor() {
if (address(this).code.length != 0) {
revert OnlyConstructorError();
}
_;
}
}
文件 31 的 52:InitialETHCrowdfund.sol
pragma solidity 0.8.20;
import { ETHCrowdfundBase } from "./ETHCrowdfundBase.sol";
import { ProposalStorage } from "../proposals/ProposalStorage.sol";
import { LibAddress } from "../utils/LibAddress.sol";
import { LibRawResult } from "../utils/LibRawResult.sol";
import { LibSafeCast } from "../utils/LibSafeCast.sol";
import { Party, PartyGovernance } from "../party/Party.sol";
import { Crowdfund } from "../crowdfund/Crowdfund.sol";
import { MetadataProvider } from "../renderers/MetadataProvider.sol";
import { IGateKeeper } from "../gatekeepers/IGateKeeper.sol";
import { IGlobals } from "../globals/IGlobals.sol";
import { IERC721 } from "../tokens/IERC721.sol";
contract InitialETHCrowdfund is ETHCrowdfundBase {
using LibRawResult for bytes;
using LibSafeCast for uint256;
using LibAddress for address payable;
struct InitialETHCrowdfundOptions {
address payable initialContributor;
address initialDelegate;
uint96 minContribution;
uint96 maxContribution;
bool disableContributingForExistingCard;
uint96 minTotalContributions;
uint96 maxTotalContributions;
uint16 exchangeRateBps;
uint16 fundingSplitBps;
address payable fundingSplitRecipient;
uint40 duration;
IGateKeeper gateKeeper;
bytes12 gateKeeperId;
}
struct ETHPartyOptions {
string name;
string symbol;
uint256 customizationPresetId;
Crowdfund.FixedGovernanceOpts governanceOpts;
ProposalStorage.ProposalEngineOpts proposalEngineOpts;
IERC721[] preciousTokens;
uint256[] preciousTokenIds;
uint40 rageQuitTimestamp;
}
struct BatchContributeArgs {
uint256[] tokenIds;
address delegate;
uint96[] values;
bytes[] gateDatas;
}
struct BatchContributeForArgs {
uint256[] tokenIds;
address payable[] recipients;
address[] initialDelegates;
uint96[] values;
bytes[] gateDatas;
bool revertOnFailure;
}
event Refunded(address indexed contributor, uint256 indexed tokenId, uint256 amount);
constructor(IGlobals globals) ETHCrowdfundBase(globals) {}
function initialize(
InitialETHCrowdfundOptions memory crowdfundOpts,
ETHPartyOptions memory partyOpts,
MetadataProvider customMetadataProvider,
bytes memory customMetadata
) external payable onlyConstructor {
Party party_ = _createParty(partyOpts, customMetadataProvider, customMetadata);
_initialize(
ETHCrowdfundOptions({
party: party_,
initialContributor: crowdfundOpts.initialContributor,
initialDelegate: crowdfundOpts.initialDelegate,
minContribution: crowdfundOpts.minContribution,
maxContribution: crowdfundOpts.maxContribution,
disableContributingForExistingCard: crowdfundOpts
.disableContributingForExistingCard,
minTotalContributions: crowdfundOpts.minTotalContributions,
maxTotalContributions: crowdfundOpts.maxTotalContributions,
exchangeRateBps: crowdfundOpts.exchangeRateBps,
fundingSplitBps: crowdfundOpts.fundingSplitBps,
fundingSplitRecipient: crowdfundOpts.fundingSplitRecipient,
duration: crowdfundOpts.duration,
gateKeeper: crowdfundOpts.gateKeeper,
gateKeeperId: crowdfundOpts.gateKeeperId
})
);
uint96 initialContribution = msg.value.safeCastUint256ToUint96();
if (initialContribution > 0) {
_contribute(
crowdfundOpts.initialContributor,
crowdfundOpts.initialDelegate,
initialContribution,
0,
""
);
}
gateKeeper = crowdfundOpts.gateKeeper;
gateKeeperId = crowdfundOpts.gateKeeperId;
}
function contribute(
address delegate,
bytes memory gateData
) public payable onlyDelegateCall returns (uint96 votingPower) {
return
_contribute(
payable(msg.sender),
delegate,
msg.value.safeCastUint256ToUint96(),
0,
gateData
);
}
function contribute(
uint256 tokenId,
address delegate,
bytes memory gateData
) public payable onlyDelegateCall returns (uint96 votingPower) {
return
_contribute(
payable(msg.sender),
delegate,
msg.value.safeCastUint256ToUint96(),
tokenId,
gateData
);
}
function batchContribute(
BatchContributeArgs calldata args
) external payable onlyDelegateCall returns (uint96[] memory votingPowers) {
uint256 numContributions = args.tokenIds.length;
votingPowers = new uint96[](numContributions);
uint256 ethAvailable = msg.value;
for (uint256 i; i < numContributions; ++i) {
ethAvailable -= args.values[i];
votingPowers[i] = _contribute(
payable(msg.sender),
args.delegate,
args.values[i],
args.tokenIds[i],
args.gateDatas[i]
);
}
if (ethAvailable > 0) payable(msg.sender).transfer(ethAvailable);
}
function contributeFor(
uint256 tokenId,
address payable recipient,
address initialDelegate,
bytes memory gateData
) external payable onlyDelegateCall returns (uint96 votingPower) {
return
_contribute(
recipient,
initialDelegate,
msg.value.safeCastUint256ToUint96(),
tokenId,
gateData
);
}
function batchContributeFor(
BatchContributeForArgs calldata args
) external payable onlyDelegateCall returns (uint96[] memory votingPowers) {
uint256 numContributions = args.recipients.length;
votingPowers = new uint96[](numContributions);
uint256 ethAvailable = msg.value;
for (uint256 i; i < numContributions; ++i) {
(bool s, bytes memory r) = address(this).call{ value: args.values[i] }(
abi.encodeCall(
this.contributeFor,
(
args.tokenIds[i],
args.recipients[i],
args.initialDelegates[i],
args.gateDatas[i]
)
)
);
if (!s) {
if (args.revertOnFailure) {
r.rawRevert();
}
} else {
votingPowers[i] = abi.decode(r, (uint96));
ethAvailable -= args.values[i];
}
}
if (ethAvailable > 0) payable(msg.sender).transfer(ethAvailable);
}
function _contribute(
address payable contributor,
address delegate,
uint96 amount,
uint256 tokenId,
bytes memory gateData
) private returns (uint96 votingPower) {
if (delegate == address(0)) {
revert InvalidDelegateError();
}
IGateKeeper _gateKeeper = gateKeeper;
if (_gateKeeper != IGateKeeper(address(0))) {
if (!_gateKeeper.isAllowed(msg.sender, gateKeeperId, gateData)) {
revert NotAllowedByGateKeeperError(
contributor,
_gateKeeper,
gateKeeperId,
gateData
);
}
}
votingPower = _processContribution(contributor, delegate, amount);
if (amount == 0) return 0;
if (tokenId == 0) {
party.mint(contributor, votingPower, delegate);
} else if (disableContributingForExistingCard) {
revert ContributingForExistingCardDisabledError();
} else if (party.ownerOf(tokenId) == contributor) {
party.addVotingPower(tokenId, votingPower);
} else {
revert NotOwnerError(tokenId);
}
}
function refund(uint256 tokenId) external returns (uint96 amount) {
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Lost) {
revert WrongLifecycleError(lc);
}
}
uint96 votingPower = party.votingPowerByTokenId(tokenId).safeCastUint256ToUint96();
amount = convertVotingPowerToContribution(votingPower);
if (amount > 0) {
address payable contributor = payable(party.ownerOf(tokenId));
party.burn(tokenId);
contributor.transferEth(amount);
emit Refunded(contributor, tokenId, amount);
}
}
function batchRefund(
uint256[] calldata tokenIds,
bool revertOnFailure
) external returns (uint96[] memory amounts) {
uint256 numRefunds = tokenIds.length;
amounts = new uint96[](numRefunds);
for (uint256 i; i < numRefunds; ++i) {
(bool s, bytes memory r) = address(this).call(
abi.encodeCall(this.refund, (tokenIds[i]))
);
if (!s) {
if (revertOnFailure) {
r.rawRevert();
}
} else {
amounts[i] = abi.decode(r, (uint96));
}
}
}
function _createParty(
ETHPartyOptions memory opts,
MetadataProvider customMetadataProvider,
bytes memory customMetadata
) private returns (Party) {
address[] memory authorities = new address[](1);
authorities[0] = address(this);
if (address(customMetadataProvider) == address(0)) {
return
opts.governanceOpts.partyFactory.createParty(
opts.governanceOpts.partyImpl,
authorities,
Party.PartyOptions({
name: opts.name,
symbol: opts.symbol,
customizationPresetId: opts.customizationPresetId,
governance: PartyGovernance.GovernanceOpts({
hosts: opts.governanceOpts.hosts,
voteDuration: opts.governanceOpts.voteDuration,
executionDelay: opts.governanceOpts.executionDelay,
passThresholdBps: opts.governanceOpts.passThresholdBps,
totalVotingPower: 0,
feeBps: opts.governanceOpts.feeBps,
feeRecipient: opts.governanceOpts.feeRecipient
}),
proposalEngine: opts.proposalEngineOpts
}),
opts.preciousTokens,
opts.preciousTokenIds,
opts.rageQuitTimestamp
);
} else {
return
opts.governanceOpts.partyFactory.createPartyWithMetadata(
opts.governanceOpts.partyImpl,
authorities,
Party.PartyOptions({
name: opts.name,
symbol: opts.symbol,
customizationPresetId: opts.customizationPresetId,
governance: PartyGovernance.GovernanceOpts({
hosts: opts.governanceOpts.hosts,
voteDuration: opts.governanceOpts.voteDuration,
executionDelay: opts.governanceOpts.executionDelay,
passThresholdBps: opts.governanceOpts.passThresholdBps,
totalVotingPower: 0,
feeBps: opts.governanceOpts.feeBps,
feeRecipient: opts.governanceOpts.feeRecipient
}),
proposalEngine: opts.proposalEngineOpts
}),
opts.preciousTokens,
opts.preciousTokenIds,
opts.rageQuitTimestamp,
customMetadataProvider,
customMetadata
);
}
}
}
文件 32 的 52:LibAddress.sol
pragma solidity 0.8.20;
library LibAddress {
error EthTransferFailed(address receiver, bytes errData);
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);
}
}
}
文件 33 的 52:LibERC20Compat.sol
pragma solidity 0.8.20;
import "../tokens/IERC20.sol";
library LibERC20Compat {
error NotATokenError(IERC20 token);
error TokenTransferFailedError(IERC20 token, address to, uint256 amount);
error TokenApprovalFailed(IERC20 token, address spender, uint256 amount);
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);
}
function compatTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
(bool s, bytes memory r) = address(token).call(
abi.encodeCall(IERC20.transferFrom, (from, 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);
}
function compatApprove(IERC20 token, address spender, uint256 amount) internal {
(bool s, bytes memory r) = address(token).call(
abi.encodeCall(IERC20.approve, (spender, 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 TokenApprovalFailed(token, spender, amount);
}
}
文件 34 的 52:LibGlobals.sol
pragma solidity 0.8.20;
library LibGlobals {
uint256 internal constant GLOBAL_PARTY_IMPL = 1;
uint256 internal constant GLOBAL_PROPOSAL_ENGINE_IMPL = 2;
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_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_METADATA_REGISTRY = 26;
uint256 internal constant GLOBAL_SEAPORT = 30;
uint256 internal constant GLOBAL_CONDUIT_CONTROLLER = 31;
uint256 internal constant GLOBAL_OFF_CHAIN_SIGNATURE_VALIDATOR = 32;
}
文件 35 的 52:LibProposal.sol
pragma solidity 0.8.20;
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;
}
}
文件 36 的 52:LibRawResult.sol
pragma solidity 0.8.20;
library LibRawResult {
function rawRevert(bytes memory b) internal pure {
assembly {
revert(add(b, 32), mload(b))
}
}
function rawReturn(bytes memory b) internal pure {
assembly {
return(add(b, 32), mload(b))
}
}
}
文件 37 的 52:LibSafeCast.sol
pragma solidity 0.8.20;
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);
}
}
文件 38 的 52:LibSafeERC721.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "./LibRawResult.sol";
library LibSafeERC721 {
using LibRawResult for bytes;
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));
}
}
文件 39 的 52:MerkleProof.sol
pragma solidity ^0.8.19;
library MerkleProof {
error MerkleProofInvalidMultiproof();
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
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;
}
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;
}
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
if (leavesLen + proofLen - 1 != totalHashes) {
revert MerkleProofInvalidMultiproof();
}
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
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) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
if (leavesLen + proofLen - 1 != totalHashes) {
revert MerkleProofInvalidMultiproof();
}
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
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) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
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) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
文件 40 的 52:MetadataProvider.sol
pragma solidity 0.8.20;
import { Multicall } from "../utils/Multicall.sol";
import { MetadataRegistry } from "./MetadataRegistry.sol";
import { IMetadataProvider } from "./IMetadataProvider.sol";
import { IGlobals } from "../globals/IGlobals.sol";
import { LibGlobals } from "../globals/LibGlobals.sol";
contract MetadataProvider is IMetadataProvider, Multicall {
event MetadataSet(address indexed instance, bytes metadata);
error NotAuthorized(address caller, address instance);
IGlobals private immutable _GLOBALS;
bool public constant supportsRegistrars = true;
mapping(address instance => bytes metadata) private _metadata;
constructor(IGlobals globals) {
_GLOBALS = globals;
}
function getMetadata(address instance, uint256) external view override returns (bytes memory) {
return _metadata[instance];
}
function setMetadata(address instance, bytes memory metadata) external {
if (instance != msg.sender) {
MetadataRegistry registry = MetadataRegistry(
_GLOBALS.getAddress(LibGlobals.GLOBAL_METADATA_REGISTRY)
);
if (!registry.isRegistrar(msg.sender, instance)) {
revert NotAuthorized(msg.sender, instance);
}
}
_metadata[instance] = metadata;
emit MetadataSet(instance, metadata);
}
}
文件 41 的 52:MetadataRegistry.sol
pragma solidity 0.8.20;
import { IGlobals } from "../globals/IGlobals.sol";
import { LibGlobals } from "../globals/LibGlobals.sol";
import { IMetadataProvider } from "./IMetadataProvider.sol";
import { Multicall } from "../utils/Multicall.sol";
contract MetadataRegistry is Multicall {
event ProviderSet(address indexed instance, IMetadataProvider indexed provider);
event RegistrarSet(address indexed registrar, address indexed instance, bool canSetData);
error NotAuthorized(address caller, address instance);
IGlobals private immutable _GLOBALS;
mapping(address instance => IMetadataProvider provider) public getProvider;
mapping(address registrar => mapping(address instance => bool canSetData)) private _isRegistrar;
constructor(IGlobals globals, address[] memory registrars) {
_GLOBALS = globals;
for (uint256 i = 0; i < registrars.length; i++) {
_isRegistrar[registrars[i]][address(1)] = true;
}
}
function setProvider(address instance, IMetadataProvider provider) external {
if (!isRegistrar(msg.sender, instance)) revert NotAuthorized(msg.sender, instance);
getProvider[instance] = provider;
emit ProviderSet(instance, provider);
}
function setRegistrar(address registrar, address instance, bool canSetData) external {
if (
msg.sender != instance &&
msg.sender != _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET)
) {
revert NotAuthorized(msg.sender, instance);
}
_isRegistrar[registrar][instance] = canSetData;
emit RegistrarSet(registrar, instance, canSetData);
}
function isRegistrar(address registrar, address instance) public view returns (bool) {
return
registrar == instance ||
_isRegistrar[registrar][address(1)] ||
_isRegistrar[registrar][instance];
}
function getMetadata(address instance, uint256 tokenId) external view returns (bytes memory) {
IMetadataProvider provider = getProvider[instance];
return
address(provider) != address(0) ? provider.getMetadata(instance, tokenId) : bytes("");
}
}
文件 42 的 52:Multicall.sol
pragma solidity 0.8.20;
import "../utils/LibRawResult.sol";
abstract contract Multicall {
using LibRawResult for bytes;
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();
}
}
}
}
文件 43 的 52:Party.sol
pragma solidity 0.8.20;
import "../tokens/IERC721.sol";
import "./PartyGovernanceNFT.sol";
import "./PartyGovernance.sol";
contract Party is PartyGovernanceNFT {
struct PartyOptions {
PartyGovernance.GovernanceOpts governance;
ProposalStorage.ProposalEngineOpts proposalEngine;
string name;
string symbol;
uint256 customizationPresetId;
}
struct PartyInitData {
PartyOptions options;
IERC721[] preciousTokens;
uint256[] preciousTokenIds;
address[] authorities;
uint40 rageQuitTimestamp;
}
uint16 public constant VERSION_ID = 1;
constructor(IGlobals globals) PartyGovernanceNFT(globals) {}
function initialize(PartyInitData memory initData) external onlyConstructor {
PartyGovernanceNFT._initialize(
initData.options.name,
initData.options.symbol,
initData.options.customizationPresetId,
initData.options.governance,
initData.options.proposalEngine,
initData.preciousTokens,
initData.preciousTokenIds,
initData.authorities,
initData.rageQuitTimestamp
);
}
receive() external payable {}
}
文件 44 的 52:PartyGovernance.sol
pragma solidity 0.8.20;
import "../distribution/ITokenDistributor.sol";
import "../utils/ReadOnlyDelegateCall.sol";
import "../tokens/IERC721.sol";
import "../tokens/IERC20.sol";
import "../tokens/ERC721Receiver.sol";
import "../tokens/ERC1155Receiver.sol";
import "../utils/LibERC20Compat.sol";
import "../utils/LibRawResult.sol";
import "../utils/LibSafeCast.sol";
import "../utils/IERC4906.sol";
import "../globals/IGlobals.sol";
import "../globals/LibGlobals.sol";
import "../proposals/IProposalExecutionEngine.sol";
import "../proposals/LibProposal.sol";
import "../proposals/ProposalStorage.sol";
import "./Party.sol";
abstract contract PartyGovernance is
ERC721Receiver,
ERC1155Receiver,
ProposalStorage,
Implementation,
IERC4906,
ReadOnlyDelegateCall
{
using LibERC20Compat for IERC20;
using LibRawResult for bytes;
using LibSafeCast for uint256;
using LibSafeCast for int192;
using LibSafeCast for uint96;
enum ProposalStatus {
Invalid,
Voting,
Defeated,
Passed,
Ready,
InProgress,
Complete,
Cancelled
}
struct GovernanceOpts {
address[] hosts;
uint40 voteDuration;
uint40 executionDelay;
uint16 passThresholdBps;
uint96 totalVotingPower;
uint16 feeBps;
address payable feeRecipient;
}
struct GovernanceValues {
uint40 voteDuration;
uint40 executionDelay;
uint16 passThresholdBps;
uint96 totalVotingPower;
}
struct VotingPowerSnapshot {
uint40 timestamp;
uint96 delegatedVotingPower;
uint96 intrinsicVotingPower;
bool isDelegated;
}
struct Proposal {
uint40 maxExecutableTime;
uint40 cancelDelay;
bytes proposalData;
}
struct ProposalStateValues {
uint40 proposedTime;
uint40 passedTime;
uint40 executedTime;
uint40 completedTime;
uint96 votes;
uint96 totalVotingPower;
}
struct ProposalState {
ProposalStateValues values;
bytes32 hash;
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 OnlyTokenDistributorOrSelfError();
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);
error DistributionsRequireVoteError();
error PartyNotStartedError();
error CannotRageQuitAndAcceptError();
uint256 private constant UINT40_HIGH_BIT = 1 << 39;
uint96 private constant VETO_VALUE = type(uint96).max;
IGlobals private immutable _GLOBALS;
bool public emergencyExecuteDisabled;
uint16 public feeBps;
address payable public feeRecipient;
uint40 public lastRageQuitTimestamp;
bytes32 public preciousListHash;
uint256 public lastProposalId;
mapping(address => bool) public isHost;
mapping(address => address) public delegationsByVoter;
GovernanceValues internal _governanceValues;
mapping(uint256 => ProposalState) private _proposalStateByProposalId;
mapping(address => VotingPowerSnapshot[]) private _votingPowerSnapshotsByVoter;
modifier onlyHost() {
if (!isHost[msg.sender]) {
revert OnlyPartyHostError();
}
_;
}
modifier onlyActiveMember() {
{
VotingPowerSnapshot memory snap = _getLastVotingPowerSnapshotForVoter(msg.sender);
if (snap.intrinsicVotingPower == 0 && snap.delegatedVotingPower == 0) {
revert OnlyActiveMemberError();
}
}
_;
}
modifier onlyPartyDao() {
{
address partyDao = _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);
if (msg.sender != partyDao) {
revert OnlyPartyDaoError(msg.sender, partyDao);
}
}
_;
}
modifier onlyPartyDaoOrHost() {
address partyDao = _GLOBALS.getAddress(LibGlobals.GLOBAL_DAO_WALLET);
if (msg.sender != partyDao && !isHost[msg.sender]) {
revert OnlyPartyDaoOrHostError(msg.sender, partyDao);
}
_;
}
modifier onlyWhenEmergencyExecuteAllowed() {
if (emergencyExecuteDisabled) {
revert OnlyWhenEmergencyActionsAllowedError();
}
_;
}
modifier onlyWhenNotGloballyDisabled() {
if (_GLOBALS.getBool(LibGlobals.GLOBAL_DISABLE_PARTY_ACTIONS)) {
revert OnlyWhenEnabledError();
}
_;
}
constructor(IGlobals globals) {
_GLOBALS = globals;
}
function _initialize(
GovernanceOpts memory govOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
IERC721[] memory preciousTokens,
uint256[] memory preciousTokenIds
) internal virtual {
if (govOpts.feeBps > 1e4) {
revert InvalidBpsError(govOpts.feeBps);
}
if (govOpts.passThresholdBps > 1e4) {
revert InvalidBpsError(govOpts.passThresholdBps);
}
_initProposalImpl(
IProposalExecutionEngine(_GLOBALS.getAddress(LibGlobals.GLOBAL_PROPOSAL_ENGINE_IMPL)),
abi.encode(proposalEngineOpts)
);
_governanceValues = GovernanceValues({
voteDuration: govOpts.voteDuration,
executionDelay: govOpts.executionDelay,
passThresholdBps: govOpts.passThresholdBps,
totalVotingPower: govOpts.totalVotingPower
});
feeBps = govOpts.feeBps;
feeRecipient = govOpts.feeRecipient;
_setPreciousList(preciousTokens, preciousTokenIds);
for (uint256 i = 0; i < govOpts.hosts.length; ++i) {
isHost[govOpts.hosts[i]] = true;
}
}
fallback() external {
_readOnlyDelegateCall(address(_getSharedProposalStorage().engineImpl), msg.data);
}
function supportsInterface(
bytes4 interfaceId
) public pure virtual override(ERC721Receiver, ERC1155Receiver) returns (bool) {
return
ERC721Receiver.supportsInterface(interfaceId) ||
ERC1155Receiver.supportsInterface(interfaceId) ||
interfaceId == 0x49064906;
}
function getProposalExecutionEngine() external view returns (IProposalExecutionEngine) {
return _getSharedProposalStorage().engineImpl;
}
function getProposalEngineOpts() external view returns (ProposalEngineOpts memory) {
return _getSharedProposalStorage().opts;
}
function getVotingPowerAt(
address voter,
uint40 timestamp
) external view returns (uint96 votingPower) {
return getVotingPowerAt(voter, timestamp, type(uint256).max);
}
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;
}
function getProposalStateInfo(
uint256 proposalId
) external view returns (ProposalStatus status, ProposalStateValues memory values) {
values = _proposalStateByProposalId[proposalId].values;
status = _getProposalStatus(values);
}
function getGovernanceValues() external view returns (GovernanceValues memory gv) {
return _governanceValues;
}
function getProposalHash(Proposal memory proposal) public pure returns (bytes32 proposalHash) {
bytes32 dataHash = keccak256(proposal.proposalData);
assembly {
let dataPos := add(proposal, 0x40)
let t := mload(dataPos)
mstore(dataPos, dataHash)
proposalHash := keccak256(proposal, 0x60)
mstore(dataPos, t)
}
}
function findVotingPowerSnapshotIndex(
address voter,
uint40 timestamp
) public view returns (uint256 index) {
VotingPowerSnapshot[] storage snaps = _votingPowerSnapshotsByVoter[voter];
uint256 high = snaps.length;
uint256 low = 0;
while (low < high) {
uint256 mid = (low + high) / 2;
if (snaps[mid].timestamp > timestamp) {
high = mid;
} else {
low = mid + 1;
}
}
return high == 0 ? type(uint256).max : high - 1;
}
function delegateVotingPower(address delegate) external {
_adjustVotingPower(msg.sender, 0, delegate);
emit VotingPowerDelegated(msg.sender, delegate);
}
function abdicateHost(address newPartyHost) external onlyHost {
if (newPartyHost != address(0)) {
if (isHost[newPartyHost]) {
revert InvalidNewHostError();
}
isHost[newPartyHost] = true;
}
isHost[msg.sender] = false;
emit HostStatusTransferred(msg.sender, newPartyHost);
}
function distribute(
uint256 amount,
ITokenDistributor.TokenType tokenType,
address token,
uint256 tokenId
)
external
onlyWhenNotGloballyDisabled
returns (ITokenDistributor.DistributionInfo memory distInfo)
{
if (msg.sender != address(this)) {
if (_getSharedProposalStorage().opts.distributionsRequireVote) {
revert DistributionsRequireVoteError();
}
VotingPowerSnapshot memory snap = _getLastVotingPowerSnapshotForVoter(msg.sender);
if (snap.intrinsicVotingPower == 0 && snap.delegatedVotingPower == 0) {
revert OnlyActiveMemberError();
}
}
if (_governanceValues.totalVotingPower == 0) {
revert PartyNotStartedError();
}
ITokenDistributor distributor = ITokenDistributor(
_GLOBALS.getAddress(LibGlobals.GLOBAL_TOKEN_DISTRIBUTOR)
);
emit DistributionCreated(tokenType, token, tokenId);
emit BatchMetadataUpdate(0, type(uint256).max);
address payable feeRecipient_ = feeRecipient;
uint16 feeBps_ = feeBps;
if (tokenType == ITokenDistributor.TokenType.Native) {
return
distributor.createNativeDistribution{ value: amount }(
Party(payable(address(this))),
feeRecipient_,
feeBps_
);
}
assert(tokenType == ITokenDistributor.TokenType.Erc20);
IERC20(token).compatTransfer(address(distributor), amount);
return
distributor.createErc20Distribution(
IERC20(token),
Party(payable(address(this))),
feeRecipient_,
feeBps_
);
}
function propose(
Proposal memory proposal,
uint256 latestSnapIndex
) external onlyActiveMember returns (uint256 proposalId) {
proposalId = ++lastProposalId;
(
_proposalStateByProposalId[proposalId].values,
_proposalStateByProposalId[proposalId].hash
) = (
ProposalStateValues({
proposedTime: uint40(block.timestamp),
passedTime: 0,
executedTime: 0,
completedTime: 0,
votes: 0,
totalVotingPower: _governanceValues.totalVotingPower
}),
getProposalHash(proposal)
);
emit Proposed(proposalId, msg.sender, proposal);
accept(proposalId, latestSnapIndex);
emit BatchMetadataUpdate(0, type(uint256).max);
}
function accept(uint256 proposalId, uint256 snapIndex) public returns (uint256 totalVotes) {
ProposalState storage info = _proposalStateByProposalId[proposalId];
ProposalStateValues memory values = info.values;
{
ProposalStatus status = _getProposalStatus(values);
if (
status != ProposalStatus.Voting &&
status != ProposalStatus.Passed &&
status != ProposalStatus.Ready
) {
revert BadProposalStatusError(status);
}
}
if (lastRageQuitTimestamp == block.timestamp) {
revert CannotRageQuitAndAcceptError();
}
if (info.hasVoted[msg.sender]) {
revert AlreadyVotedError(msg.sender);
}
info.hasVoted[msg.sender] = true;
uint96 votingPower = getVotingPowerAt(msg.sender, values.proposedTime - 1, snapIndex);
values.votes += votingPower;
info.values = values;
emit ProposalAccepted(proposalId, msg.sender, votingPower);
if (
values.passedTime == 0 &&
_areVotesPassing(
values.votes,
values.totalVotingPower,
_governanceValues.passThresholdBps
)
) {
info.values.passedTime = uint40(block.timestamp);
emit ProposalPassed(proposalId);
emit BatchMetadataUpdate(0, type(uint256).max);
}
return values.votes;
}
function veto(uint256 proposalId) external onlyHost {
ProposalState storage info = _proposalStateByProposalId[proposalId];
ProposalStateValues memory values = info.values;
{
ProposalStatus status = _getProposalStatus(values);
if (
status != ProposalStatus.Voting &&
status != ProposalStatus.Passed &&
status != ProposalStatus.Ready
) {
revert BadProposalStatusError(status);
}
}
info.values.votes = VETO_VALUE;
emit ProposalVetoed(proposalId, msg.sender);
emit BatchMetadataUpdate(0, type(uint256).max);
}
function execute(
uint256 proposalId,
Proposal memory proposal,
IERC721[] memory preciousTokens,
uint256[] memory preciousTokenIds,
bytes calldata progressData,
bytes calldata extraData
) external payable onlyActiveMember onlyWhenNotGloballyDisabled onlyDelegateCall {
ProposalState storage proposalState = _proposalStateByProposalId[proposalId];
_validateProposalHash(proposal, proposalState.hash);
ProposalStateValues memory values = proposalState.values;
ProposalStatus status = _getProposalStatus(values);
if (status != ProposalStatus.Ready && status != ProposalStatus.InProgress) {
revert BadProposalStatusError(status);
}
if (status == ProposalStatus.Ready) {
if (proposal.maxExecutableTime < block.timestamp) {
revert ExecutionTimeExceededError(
proposal.maxExecutableTime,
uint40(block.timestamp)
);
}
proposalState.values.executedTime = uint40(block.timestamp);
}
if (!_isPreciousListCorrect(preciousTokens, preciousTokenIds)) {
revert BadPreciousListError();
}
proposalState.values.completedTime = uint40(block.timestamp);
bool completed = _executeProposal(
proposalId,
proposal,
preciousTokens,
preciousTokenIds,
_getProposalFlags(values),
progressData,
extraData
);
if (!completed) {
proposalState.values.completedTime = 0;
}
}
function cancel(uint256 proposalId, Proposal calldata proposal) external onlyActiveMember {
ProposalState storage proposalState = _proposalStateByProposalId[proposalId];
_validateProposalHash(proposal, proposalState.hash);
ProposalStateValues memory values = proposalState.values;
{
ProposalStatus status = _getProposalStatus(values);
if (status != ProposalStatus.InProgress) {
revert BadProposalStatusError(status);
}
}
{
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) {
if (cancelDelay > globalMaxCancelDelay) {
cancelDelay = globalMaxCancelDelay;
}
}
if (globalMinCancelDelay != 0) {
if (cancelDelay < globalMinCancelDelay) {
cancelDelay = globalMinCancelDelay;
}
}
uint256 cancelTime = values.executedTime + cancelDelay;
if (block.timestamp < cancelTime) {
revert ProposalCannotBeCancelledYetError(
uint40(block.timestamp),
uint40(cancelTime)
);
}
}
proposalState.values.completedTime = uint40(block.timestamp | UINT40_HIGH_BIT);
{
(bool success, bytes memory resultData) = (
address(_getSharedProposalStorage().engineImpl)
).delegatecall(abi.encodeCall(IProposalExecutionEngine.cancelProposal, (proposalId)));
if (!success) {
resultData.rawRevert();
}
}
emit ProposalCancelled(proposalId);
emit BatchMetadataUpdate(0, type(uint256).max);
}
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);
}
function disableEmergencyExecute() external onlyPartyDaoOrHost {
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) {
IProposalExecutionEngine.ExecuteProposalParams
memory executeParams = IProposalExecutionEngine.ExecuteProposalParams({
proposalId: proposalId,
proposalData: proposal.proposalData,
progressData: progressData,
extraData: extraData,
preciousTokens: preciousTokens,
preciousTokenIds: preciousTokenIds,
flags: flags
});
bytes memory nextProgressData;
{
(bool success, bytes memory resultData) = address(
_getSharedProposalStorage().engineImpl
).delegatecall(
abi.encodeCall(IProposalExecutionEngine.executeProposal, (executeParams))
);
if (!success) {
resultData.rawRevert();
}
nextProgressData = abi.decode(resultData, (bytes));
}
emit ProposalExecuted(proposalId, msg.sender, nextProgressData);
emit BatchMetadataUpdate(0, type(uint256).max);
return nextProgressData.length == 0;
}
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 (
hintIndex < snapsLength &&
snaps[hintIndex].timestamp <= timestamp &&
(hintIndex == snapsLength - 1 || snaps[hintIndex + 1].timestamp > timestamp)
) {
return snaps[hintIndex];
}
hintIndex = findVotingPowerSnapshotIndex(voter, timestamp);
if (hintIndex != type(uint256).max) {
return snaps[hintIndex];
}
}
return snap;
}
function _transferVotingPower(address from, address to, uint256 power) internal {
int192 powerI192 = power.safeCastUint256ToInt192();
_adjustVotingPower(from, -powerI192, address(0));
_adjustVotingPower(to, powerI192, address(0));
}
function _adjustVotingPower(address voter, int192 votingPower, address delegate) internal {
VotingPowerSnapshot memory oldSnap = _getLastVotingPowerSnapshotForVoter(voter);
address oldDelegate = delegationsByVoter[voter];
oldDelegate = oldDelegate == address(0) ? voter : oldDelegate;
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;
_rebalanceDelegates(voter, oldDelegate, delegate, oldSnap, newSnap);
}
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) {
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) {
VotingPowerSnapshot memory newDelegateSnap = _getLastVotingPowerSnapshotForVoter(
newDelegate
);
uint96 newDelegateDelegatedVotingPower = newDelegateSnap.delegatedVotingPower +
newSnap.intrinsicVotingPower;
if (newDelegate == oldDelegate) {
newDelegateDelegatedVotingPower -= oldSnap.intrinsicVotingPower;
}
VotingPowerSnapshot memory updatedNewDelegateSnap = VotingPowerSnapshot({
timestamp: uint40(block.timestamp),
delegatedVotingPower: newDelegateDelegatedVotingPower,
intrinsicVotingPower: newDelegateSnap.intrinsicVotingPower,
isDelegated: newDelegateSnap.isDelegated
});
_insertVotingPowerSnapshot(newDelegate, updatedNewDelegateSnap);
}
}
function _insertVotingPowerSnapshot(address voter, VotingPowerSnapshot memory snap) private {
VotingPowerSnapshot[] storage voterSnaps = _votingPowerSnapshotsByVoter[voter];
uint256 n = voterSnaps.length;
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 pure returns (uint256) {
if (_isUnanimousVotes(pv.votes, pv.totalVotingPower)) {
return LibProposal.PROPOSAL_FLAG_UNANIMOUS;
}
return 0;
}
function _getProposalStatus(
ProposalStateValues memory pv
) private view returns (ProposalStatus status) {
if (pv.proposedTime == 0) {
return ProposalStatus.Invalid;
}
if (pv.executedTime != 0) {
if (pv.completedTime == 0) {
return ProposalStatus.InProgress;
}
if (pv.completedTime & UINT40_HIGH_BIT == UINT40_HIGH_BIT) {
return ProposalStatus.Cancelled;
}
return ProposalStatus.Complete;
}
if (pv.votes == type(uint96).max) {
return ProposalStatus.Defeated;
}
uint40 t = uint40(block.timestamp);
GovernanceValues memory gv = _governanceValues;
if (pv.passedTime != 0) {
if (pv.passedTime + gv.executionDelay <= t) {
return ProposalStatus.Ready;
}
if (_isUnanimousVotes(pv.votes, pv.totalVotingPower)) {
return ProposalStatus.Ready;
}
return ProposalStatus.Passed;
}
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;
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)
}
}
function _validateProposalHash(Proposal memory proposal, bytes32 expectedHash) private pure {
bytes32 actualHash = getProposalHash(proposal);
if (expectedHash != actualHash) {
revert BadProposalHashError(actualHash, expectedHash);
}
}
}
文件 45 的 52:PartyGovernanceNFT.sol
pragma solidity 0.8.20;
import "../utils/LibSafeCast.sol";
import "../utils/LibAddress.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";
contract PartyGovernanceNFT is PartyGovernance, ERC721, IERC2981 {
using LibSafeCast for uint256;
using LibSafeCast for uint96;
using LibERC20Compat for IERC20;
using LibAddress for address payable;
error OnlyAuthorityError();
error OnlySelfError();
error UnauthorizedToBurnError();
error FixedRageQuitTimestampError(uint40 rageQuitTimestamp);
error CannotRageQuitError(uint40 rageQuitTimestamp);
error CannotDisableRageQuitAfterInitializationError();
error InvalidTokenOrderError();
error BelowMinWithdrawAmountError(uint256 amount, uint256 minAmount);
error NothingToBurnError();
event AuthorityAdded(address indexed authority);
event AuthorityRemoved(address indexed authority);
event RageQuitSet(uint40 oldRageQuitTimestamp, uint40 newRageQuitTimestamp);
event Burn(address caller, uint256 tokenId, uint256 votingPower);
event RageQuit(address caller, uint256[] tokenIds, IERC20[] withdrawTokens, address receiver);
uint40 private constant ENABLE_RAGEQUIT_PERMANENTLY = 0x6b5b567bfe;
uint40 private constant DISABLE_RAGEQUIT_PERMANENTLY = 0xab2cb21860;
address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
IGlobals private immutable _GLOBALS;
uint96 public tokenCount;
uint96 public mintedVotingPower;
uint40 public rageQuitTimestamp;
mapping(uint256 => uint256) public votingPowerByTokenId;
mapping(address => bool) public isAuthority;
modifier onlyAuthority() {
if (!isAuthority[msg.sender]) {
revert OnlyAuthorityError();
}
_;
}
modifier onlySelf() {
if (msg.sender != address(this)) {
revert OnlySelfError();
}
_;
}
constructor(IGlobals globals) payable PartyGovernance(globals) ERC721("", "") {
_GLOBALS = globals;
}
function _initialize(
string memory name_,
string memory symbol_,
uint256 customizationPresetId,
PartyGovernance.GovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts,
IERC721[] memory preciousTokens,
uint256[] memory preciousTokenIds,
address[] memory authorities,
uint40 rageQuitTimestamp_
) internal {
PartyGovernance._initialize(
governanceOpts,
proposalEngineOpts,
preciousTokens,
preciousTokenIds
);
name = name_;
symbol = symbol_;
rageQuitTimestamp = rageQuitTimestamp_;
unchecked {
for (uint256 i; i < authorities.length; ++i) {
isAuthority[authorities[i]] = true;
}
}
if (customizationPresetId != 0) {
RendererStorage(_GLOBALS.getAddress(LibGlobals.GLOBAL_RENDERER_STORAGE))
.useCustomizationPreset(customizationPresetId);
}
}
function ownerOf(uint256 tokenId) public view override returns (address owner) {
return ERC721.ownerOf(tokenId);
}
function supportsInterface(
bytes4 interfaceId
) public pure override(PartyGovernance, ERC721, IERC165) returns (bool) {
return
PartyGovernance.supportsInterface(interfaceId) ||
ERC721.supportsInterface(interfaceId) ||
interfaceId == type(IERC2981).interfaceId;
}
function tokenURI(uint256) public view override returns (string memory) {
_delegateToRenderer();
return "";
}
function contractURI() external view returns (string memory) {
_delegateToRenderer();
return "";
}
function royaltyInfo(uint256, uint256) external view returns (address, uint256) {
_delegateToRenderer();
return (address(0), 0);
}
function getDistributionShareOf(uint256 tokenId) public view returns (uint256) {
return votingPowerByTokenId[tokenId];
}
function getVotingPowerShareOf(uint256 tokenId) public view returns (uint256) {
uint256 totalVotingPower = _governanceValues.totalVotingPower;
return
totalVotingPower == 0 ? 0 : (votingPowerByTokenId[tokenId] * 1e18) / totalVotingPower;
}
function mint(
address owner,
uint256 votingPower,
address delegate
) external onlyAuthority returns (uint256 tokenId) {
uint96 mintedVotingPower_ = mintedVotingPower;
uint96 totalVotingPower = _governanceValues.totalVotingPower;
uint96 votingPower_ = votingPower.safeCastUint256ToUint96();
if (totalVotingPower != 0 && totalVotingPower - mintedVotingPower_ < votingPower_) {
unchecked {
votingPower_ = totalVotingPower - mintedVotingPower_;
}
}
unchecked {
tokenId = ++tokenCount;
}
mintedVotingPower += votingPower_;
votingPowerByTokenId[tokenId] = votingPower_;
address delegate_ = delegationsByVoter[owner];
if (delegate_ != address(0)) {
delegate = delegate_;
}
_adjustVotingPower(owner, votingPower_.safeCastUint96ToInt192(), delegate);
_safeMint(owner, tokenId);
}
function addVotingPower(uint256 tokenId, uint256 votingPower) external onlyAuthority {
uint96 mintedVotingPower_ = mintedVotingPower;
uint96 totalVotingPower = _governanceValues.totalVotingPower;
uint96 votingPower_ = votingPower.safeCastUint256ToUint96();
if (totalVotingPower != 0 && totalVotingPower - mintedVotingPower_ < votingPower_) {
unchecked {
votingPower_ = totalVotingPower - mintedVotingPower_;
}
}
mintedVotingPower += votingPower_;
votingPowerByTokenId[tokenId] += votingPower_;
_adjustVotingPower(ownerOf(tokenId), votingPower_.safeCastUint96ToInt192(), address(0));
}
function increaseTotalVotingPower(uint96 newVotingPower) external onlyAuthority {
_governanceValues.totalVotingPower += newVotingPower;
}
function burn(uint256[] memory tokenIds) public onlyAuthority {
if (_governanceValues.totalVotingPower != 0) revert UnauthorizedToBurnError();
_burnAndUpdateVotingPower(tokenIds, false);
}
function _burnAndUpdateVotingPower(
uint256[] memory tokenIds,
bool checkIfAuthorizedToBurn
) private returns (uint96 totalVotingPowerBurned) {
for (uint256 i; i < tokenIds.length; ++i) {
uint256 tokenId = tokenIds[i];
address owner = ownerOf(tokenId);
if (checkIfAuthorizedToBurn) {
if (
msg.sender != owner &&
getApproved[tokenId] != msg.sender &&
!isApprovedForAll[owner][msg.sender]
) {
revert UnauthorizedToBurnError();
}
}
uint96 votingPower = votingPowerByTokenId[tokenId].safeCastUint256ToUint96();
totalVotingPowerBurned += votingPower;
delete votingPowerByTokenId[tokenId];
_adjustVotingPower(owner, -votingPower.safeCastUint96ToInt192(), address(0));
_burn(tokenId);
emit Burn(msg.sender, tokenId, votingPower);
}
mintedVotingPower -= totalVotingPowerBurned;
}
function burn(uint256 tokenId) external {
uint256[] memory tokenIds = new uint256[](1);
tokenIds[0] = tokenId;
burn(tokenIds);
}
function setRageQuit(uint40 newRageQuitTimestamp) external onlyHost {
if (newRageQuitTimestamp == DISABLE_RAGEQUIT_PERMANENTLY) {
revert CannotDisableRageQuitAfterInitializationError();
}
uint40 oldRageQuitTimestamp = rageQuitTimestamp;
if (
oldRageQuitTimestamp == ENABLE_RAGEQUIT_PERMANENTLY ||
oldRageQuitTimestamp == DISABLE_RAGEQUIT_PERMANENTLY
) {
revert FixedRageQuitTimestampError(oldRageQuitTimestamp);
}
emit RageQuitSet(oldRageQuitTimestamp, rageQuitTimestamp = newRageQuitTimestamp);
}
function rageQuit(
uint256[] calldata tokenIds,
IERC20[] calldata withdrawTokens,
uint256[] calldata minWithdrawAmounts,
address receiver
) external {
if (tokenIds.length == 0) revert NothingToBurnError();
uint40 currentRageQuitTimestamp = rageQuitTimestamp;
if (currentRageQuitTimestamp != ENABLE_RAGEQUIT_PERMANENTLY) {
if (
currentRageQuitTimestamp == DISABLE_RAGEQUIT_PERMANENTLY ||
currentRageQuitTimestamp < block.timestamp
) {
revert CannotRageQuitError(currentRageQuitTimestamp);
}
}
rageQuitTimestamp = DISABLE_RAGEQUIT_PERMANENTLY;
lastRageQuitTimestamp = uint40(block.timestamp);
uint256[] memory withdrawAmounts = new uint256[](withdrawTokens.length);
{
IERC20 prevToken;
for (uint256 i; i < withdrawTokens.length; ++i) {
IERC20 token = withdrawTokens[i];
if (prevToken >= token) revert InvalidTokenOrderError();
prevToken = token;
uint256 balance = address(token) == ETH_ADDRESS
? address(this).balance
: token.balanceOf(address(this));
for (uint256 j; j < tokenIds.length; ++j) {
uint256 shareOfVotingPower = getVotingPowerShareOf(tokenIds[j]);
withdrawAmounts[i] += (balance * shareOfVotingPower) / 1e18;
}
}
}
{
uint96 totalVotingPowerBurned = _burnAndUpdateVotingPower(tokenIds, true);
_governanceValues.totalVotingPower -= totalVotingPowerBurned;
}
{
uint16 feeBps_ = feeBps;
for (uint256 i; i < withdrawTokens.length; ++i) {
IERC20 token = withdrawTokens[i];
uint256 amount = withdrawAmounts[i];
uint256 fee = (amount * feeBps_) / 1e4;
if (fee > 0) {
amount -= fee;
if (address(token) == ETH_ADDRESS) {
payable(feeRecipient).transferEth(fee);
} else {
token.compatTransfer(feeRecipient, fee);
}
}
if (amount > 0) {
uint256 minAmount = minWithdrawAmounts[i];
if (amount < minAmount) {
revert BelowMinWithdrawAmountError(amount, minAmount);
}
if (address(token) == ETH_ADDRESS) {
payable(receiver).transferEth(amount);
} else {
token.compatTransfer(receiver, amount);
}
}
}
}
rageQuitTimestamp = currentRageQuitTimestamp;
emit RageQuit(msg.sender, tokenIds, withdrawTokens, receiver);
}
function transferFrom(address owner, address to, uint256 tokenId) public override {
_transferVotingPower(owner, to, votingPowerByTokenId[tokenId]);
super.transferFrom(owner, to, tokenId);
}
function safeTransferFrom(address owner, address to, uint256 tokenId) public override {
super.safeTransferFrom(owner, to, tokenId);
}
function safeTransferFrom(
address owner,
address to,
uint256 tokenId,
bytes calldata data
) public override {
super.safeTransferFrom(owner, to, tokenId, data);
}
function addAuthority(address authority) external onlySelf {
isAuthority[authority] = true;
emit AuthorityAdded(authority);
}
function abdicateAuthority() external onlyAuthority {
delete isAuthority[msg.sender];
emit AuthorityRemoved(msg.sender);
}
function _delegateToRenderer() private view {
_readOnlyDelegateCall(
_GLOBALS.getAddress(LibGlobals.GLOBAL_GOVERNANCE_NFT_RENDER_IMPL),
msg.data
);
assert(false);
}
}
文件 46 的 52:ProposalStorage.sol
pragma solidity 0.8.20;
import "./IProposalExecutionEngine.sol";
import "../utils/LibRawResult.sol";
abstract contract ProposalStorage {
using LibRawResult for bytes;
struct SharedProposalStorage {
IProposalExecutionEngine engineImpl;
ProposalEngineOpts opts;
}
struct ProposalEngineOpts {
bool enableAddAuthorityProposal;
bool allowArbCallsToSpendPartyEth;
bool allowOperators;
bool distributionsRequireVote;
}
uint256 internal constant PROPOSAL_FLAG_UNANIMOUS = 0x1;
uint256 private constant SHARED_STORAGE_SLOT =
uint256(keccak256("ProposalStorage.SharedProposalStorage"));
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()
internal
pure
returns (SharedProposalStorage storage stor)
{
uint256 s = SHARED_STORAGE_SLOT;
assembly {
stor.slot := s
}
}
}
文件 47 的 52:Proxy.sol
pragma solidity 0.8.20;
import "./LibRawResult.sol";
import "./Implementation.sol";
contract Proxy {
using LibRawResult for bytes;
Implementation public immutable IMPL;
constructor(Implementation impl, bytes memory initCallData) payable {
IMPL = impl;
(bool s, bytes memory r) = address(impl).delegatecall(initCallData);
if (!s) {
r.rawRevert();
}
}
fallback() external payable {
Implementation impl = IMPL;
assembly {
calldatacopy(0x00, 0x00, calldatasize())
let s := delegatecall(gas(), impl, 0x00, calldatasize(), 0x00, 0)
returndatacopy(0x00, 0x00, returndatasize())
if iszero(s) {
revert(0x00, returndatasize())
}
return(0x00, returndatasize())
}
}
}
文件 48 的 52:ReadOnlyDelegateCall.sol
pragma solidity 0.8.20;
import "./LibRawResult.sol";
interface IReadOnlyDelegateCall {
function delegateCallAndRevert(address impl, bytes memory callData) external view;
}
abstract contract ReadOnlyDelegateCall {
using LibRawResult for bytes;
function delegateCallAndRevert(address impl, bytes memory callData) external {
require(msg.sender == address(this));
(bool s, bytes memory r) = impl.delegatecall(callData);
abi.encode(s, r).rawRevert();
}
function _readOnlyDelegateCall(address impl, bytes memory callData) internal view {
try IReadOnlyDelegateCall(address(this)).delegateCallAndRevert(impl, callData) {
assert(false);
} catch (bytes memory r) {
(bool success, bytes memory resultData) = abi.decode(r, (bool, bytes));
if (!success) {
resultData.rawRevert();
}
resultData.rawReturn();
}
}
}
文件 49 的 52:RendererStorage.sol
pragma solidity 0.8.20;
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;
address public owner;
mapping(uint256 => bytes) public customizationPresets;
mapping(address => uint256) public getPresetFor;
mapping(uint256 => address) public files;
modifier onlyOwner() {
address owner_ = owner;
if (msg.sender != owner_) {
revert NotOwnerError(msg.sender, owner_);
}
_;
}
constructor(address _owner) {
owner = _owner;
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)"/>'
)
);
}
function transferOwnership(address newOwner) external onlyOwner {
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
function writeFile(uint256 key, string memory data) external onlyOwner {
files[key] = SSTORE2.write(bytes(data));
}
function readFile(uint256 key) external view returns (string memory data) {
return string(SSTORE2.read(files[key]));
}
function createCustomizationPreset(
uint256 id,
bytes memory customizationData
) external onlyOwner {
customizationPresets[id] = customizationData;
}
function useCustomizationPreset(uint256 id) external {
getPresetFor[msg.sender] = id;
}
}
文件 50 的 52:ReraiseETHCrowdfund.sol
pragma solidity 0.8.20;
import "./ETHCrowdfundBase.sol";
import "../crowdfund/CrowdfundNFT.sol";
import "../utils/LibAddress.sol";
import "../utils/LibRawResult.sol";
import "../utils/LibSafeCast.sol";
import "../party/Party.sol";
import "../globals/IGlobals.sol";
import "../gatekeepers/IGateKeeper.sol";
contract ReraiseETHCrowdfund is ETHCrowdfundBase, CrowdfundNFT {
using LibRawResult for bytes;
using LibSafeCast for uint256;
using LibAddress for address payable;
struct BatchContributeArgs {
address delegate;
uint96[] values;
bytes[] gateDatas;
}
struct BatchContributeForArgs {
address payable[] recipients;
address[] initialDelegates;
uint96[] values;
bytes[] gateDatas;
bool revertOnFailure;
}
event Claimed(address indexed contributor, uint256 indexed tokenId, uint256 votingPower);
event Refunded(address indexed contributor, uint256 amount);
error RemainingVotingPowerAfterClaimError(uint256 remainingVotingPower);
IGlobals private immutable _GLOBALS;
mapping(address => uint96) public pendingVotingPower;
constructor(IGlobals globals) CrowdfundNFT(globals) ETHCrowdfundBase(globals) {
_GLOBALS = globals;
}
function initialize(ETHCrowdfundOptions memory opts) external payable onlyConstructor {
ETHCrowdfundBase._initialize(opts);
_initialize(
opts.party.name(),
opts.party.symbol(),
0
);
uint96 initialContribution = msg.value.safeCastUint256ToUint96();
if (initialContribution > 0) {
_contribute(opts.initialContributor, opts.initialDelegate, initialContribution, "");
}
gateKeeper = opts.gateKeeper;
gateKeeperId = opts.gateKeeperId;
}
function _initialize(string memory name_, string memory symbol_, uint256) internal override {
name = name_;
symbol = symbol_;
RendererStorage rendererStorage = RendererStorage(
_GLOBALS.getAddress(LibGlobals.GLOBAL_RENDERER_STORAGE)
);
uint256 customizationPresetId = rendererStorage.getPresetFor(address(party));
if (customizationPresetId != 0) {
rendererStorage.useCustomizationPreset(customizationPresetId);
}
}
function contribute(
address delegate,
bytes memory gateData
) public payable onlyDelegateCall returns (uint96 votingPower) {
return
_contribute(
payable(msg.sender),
delegate,
msg.value.safeCastUint256ToUint96(),
gateData
);
}
function batchContribute(
BatchContributeArgs calldata args
) external payable onlyDelegateCall returns (uint96[] memory votingPowers) {
uint256 numContributions = args.values.length;
votingPowers = new uint96[](numContributions);
uint256 ethAvailable = msg.value;
for (uint256 i; i < numContributions; ++i) {
ethAvailable -= args.values[i];
votingPowers[i] = _contribute(
payable(msg.sender),
args.delegate,
args.values[i],
args.gateDatas[i]
);
}
if (ethAvailable > 0) payable(msg.sender).transfer(ethAvailable);
}
function contributeFor(
address payable recipient,
address initialDelegate,
bytes memory gateData
) external payable onlyDelegateCall returns (uint96 votingPower) {
return
_contribute(recipient, initialDelegate, msg.value.safeCastUint256ToUint96(), gateData);
}
function batchContributeFor(
BatchContributeForArgs memory args
) external payable onlyDelegateCall returns (uint96[] memory votingPowers) {
uint256 numContributions = args.recipients.length;
votingPowers = new uint96[](numContributions);
uint256 ethAvailable = msg.value;
for (uint256 i; i < numContributions; ++i) {
(bool s, bytes memory r) = address(this).call{ value: args.values[i] }(
abi.encodeCall(
this.contributeFor,
(args.recipients[i], args.initialDelegates[i], args.gateDatas[i])
)
);
if (!s) {
if (args.revertOnFailure) {
r.rawRevert();
}
} else {
votingPowers[i] = abi.decode(r, (uint96));
ethAvailable -= args.values[i];
}
}
if (ethAvailable > 0) payable(msg.sender).transfer(ethAvailable);
}
function _contribute(
address payable contributor,
address delegate,
uint96 amount,
bytes memory gateData
) private returns (uint96 votingPower) {
if (delegate == address(0)) {
revert InvalidDelegateError();
}
IGateKeeper _gateKeeper = gateKeeper;
if (_gateKeeper != IGateKeeper(address(0))) {
if (!_gateKeeper.isAllowed(msg.sender, gateKeeperId, gateData)) {
revert NotAllowedByGateKeeperError(
contributor,
_gateKeeper,
gateKeeperId,
gateData
);
}
}
votingPower = _processContribution(contributor, delegate, amount);
if (amount == 0) return 0;
uint256 previousVotingPower = pendingVotingPower[contributor];
pendingVotingPower[contributor] += votingPower;
if (previousVotingPower == 0) _mint(contributor);
}
function claim(address contributor) external {
claim(
0,
contributor
);
}
function claim(uint256 tokenId, address contributor) public {
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Finalized) {
revert WrongLifecycleError(lc);
}
}
uint96 votingPower = pendingVotingPower[contributor];
if (votingPower == 0) return;
{
uint96 contribution = convertVotingPowerToContribution(votingPower);
uint96 maxContribution_ = maxContribution;
if (contribution > maxContribution_) {
revert AboveMaximumContributionsError(contribution, maxContribution_);
}
}
_burn(contributor);
delete pendingVotingPower[contributor];
if (tokenId == 0) {
tokenId = party.mint(contributor, votingPower, delegationsByContributor[contributor]);
} else if (disableContributingForExistingCard) {
revert ContributingForExistingCardDisabledError();
} else if (party.ownerOf(tokenId) == contributor) {
party.addVotingPower(tokenId, votingPower);
} else {
revert NotOwnerError(tokenId);
}
emit Claimed(contributor, tokenId, votingPower);
}
function batchClaim(
uint256[] calldata tokenIds,
address[] calldata contributors,
bool revertOnFailure
) external {
for (uint256 i; i < contributors.length; ++i) {
(bool s, bytes memory r) = address(this).call(
abi.encodeWithSignature("claim(uint256,address)", tokenIds[i], contributors[i])
);
if (revertOnFailure && !s) {
r.rawRevert();
}
}
}
function claimMultiple(
uint96[] memory votingPowerByCard,
uint256[] memory tokenIds,
address contributor
) external {
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Finalized) {
revert WrongLifecycleError(lc);
}
}
uint256 votingPower = pendingVotingPower[contributor];
if (votingPower == 0) return;
_burn(contributor);
delete pendingVotingPower[contributor];
address delegate = delegationsByContributor[contributor];
uint96 minContribution_ = minContribution;
uint96 maxContribution_ = maxContribution;
for (uint256 i; i < votingPowerByCard.length; ++i) {
uint96 votingPowerForCard = votingPowerByCard[i];
if (votingPowerForCard == 0) continue;
uint96 contribution = convertVotingPowerToContribution(votingPowerByCard[i]);
if (contribution < minContribution_) {
revert BelowMinimumContributionsError(contribution, minContribution_);
}
if (contribution > maxContribution_) {
revert AboveMaximumContributionsError(contribution, maxContribution_);
}
votingPower -= votingPowerForCard;
uint256 tokenId = tokenIds[i];
if (tokenId == 0) {
tokenId = party.mint(contributor, votingPowerForCard, delegate);
} else if (disableContributingForExistingCard) {
revert ContributingForExistingCardDisabledError();
} else if (party.ownerOf(tokenId) == contributor) {
party.addVotingPower(tokenId, votingPowerForCard);
} else {
revert NotOwnerError(tokenId);
}
emit Claimed(contributor, tokenId, votingPowerForCard);
}
if (votingPower != 0) revert RemainingVotingPowerAfterClaimError(votingPower);
}
function batchClaimMultiple(
uint96[][] calldata votingPowerByCards,
uint256[][] calldata tokenIds,
address[] calldata contributors,
bool revertOnFailure
) external {
for (uint256 i; i < contributors.length; ++i) {
(bool s, bytes memory r) = address(this).call(
abi.encodeCall(
this.claimMultiple,
(votingPowerByCards[i], tokenIds[i], contributors[i])
)
);
if (revertOnFailure && !s) {
r.rawRevert();
}
}
}
function refund(address payable contributor) external returns (uint96 amount) {
{
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Lost) {
revert WrongLifecycleError(lc);
}
}
uint96 votingPower = pendingVotingPower[contributor];
amount = convertVotingPowerToContribution(votingPower);
if (amount == 0) return 0;
_burn(contributor);
delete pendingVotingPower[contributor];
contributor.transferEth(amount);
emit Refunded(contributor, amount);
}
function batchRefund(
address payable[] calldata contributors,
bool revertOnFailure
) external returns (uint96[] memory amounts) {
uint256 numRefunds = contributors.length;
amounts = new uint96[](numRefunds);
for (uint256 i; i < numRefunds; ++i) {
(bool s, bytes memory r) = address(this).call(
abi.encodeCall(this.refund, (contributors[i]))
);
if (!s) {
if (revertOnFailure) {
r.rawRevert();
}
} else {
amounts[i] = abi.decode(r, (uint96));
}
}
}
}
文件 51 的 52:RollingAuctionCrowdfund.sol
pragma solidity 0.8.20;
import "openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "./AuctionCrowdfundBase.sol";
contract RollingAuctionCrowdfund is AuctionCrowdfundBase {
using LibSafeERC721 for IERC721;
using LibSafeCast for uint256;
using LibRawResult for bytes;
struct RollOverArgs {
uint256 nextNftTokenId;
uint256 nextAuctionId;
uint96 nextMaximumBid;
bytes32[] proof;
uint256 hostIndex;
}
event AuctionUpdated(uint256 nextNftTokenId, uint256 nextAuctionId, uint256 nextMaximumBid);
error BadNextAuctionError();
bytes32 public allowedAuctionsMerkleRoot;
constructor(IGlobals globals) AuctionCrowdfundBase(globals) {}
function initialize(
AuctionCrowdfundBase.AuctionCrowdfundOptions memory opts,
bytes32 allowedAuctionsMerkleRoot_
) external payable onlyConstructor {
AuctionCrowdfundBase._initialize(opts);
allowedAuctionsMerkleRoot = allowedAuctionsMerkleRoot_;
}
function finalize(
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts
) external onlyDelegateCall returns (Party party_) {
RollOverArgs memory args;
return finalizeOrRollOver(args, governanceOpts, proposalEngineOpts);
}
function finalizeOrRollOver(
RollOverArgs memory args,
FixedGovernanceOpts memory governanceOpts,
ProposalStorage.ProposalEngineOpts memory proposalEngineOpts
) public onlyDelegateCall returns (Party party_) {
CrowdfundLifecycle lc = getCrowdfundLifecycle();
if (lc != CrowdfundLifecycle.Active && lc != CrowdfundLifecycle.Expired) {
revert WrongLifecycleError(lc);
}
uint96 lastBid_ = lastBid;
_finalizeAuction(lc, market, auctionId, lastBid_);
IERC721 nftContract_ = nftContract;
uint256 nftTokenId_ = nftTokenId;
if (nftContract_.safeOwnerOf(nftTokenId_) == address(this) && lastBid_ != 0) {
party_ = _createParty(
governanceOpts,
proposalEngineOpts,
false,
nftContract,
nftTokenId
);
emit Won(lastBid, party_);
emit BatchMetadataUpdate(0, type(uint256).max);
_bidStatus = AuctionCrowdfundStatus.Finalized;
} else if (lc == CrowdfundLifecycle.Expired) {
lastBid = 0;
emit Lost();
emit BatchMetadataUpdate(0, type(uint256).max);
_bidStatus = AuctionCrowdfundStatus.Finalized;
} else {
if (allowedAuctionsMerkleRoot != bytes32(0)) {
if (
!MerkleProof.verify(
args.proof,
allowedAuctionsMerkleRoot,
keccak256(
abi.encodePacked(bytes32(0), args.nextAuctionId, args.nextNftTokenId)
)
)
) {
revert BadNextAuctionError();
}
} else {
_assertIsHost(msg.sender, governanceOpts, proposalEngineOpts, args.hostIndex);
}
_validateAuction(market, args.nextAuctionId, nftContract, args.nextNftTokenId);
uint256 minimumBid = market.getMinimumBid(args.nextAuctionId);
if (args.nextMaximumBid < minimumBid) {
revert MinimumBidExceedsMaximumBidError(minimumBid, args.nextMaximumBid);
}
nftTokenId = args.nextNftTokenId;
auctionId = args.nextAuctionId;
maximumBid = args.nextMaximumBid;
lastBid = 0;
emit AuctionUpdated(args.nextNftTokenId, args.nextAuctionId, args.nextMaximumBid);
_bidStatus = AuctionCrowdfundStatus.Active;
}
}
}
文件 52 的 52:SSTORE2.sol
pragma solidity >=0.8.0;
library SSTORE2 {
uint256 internal constant DATA_OFFSET = 1;
function write(bytes memory data) internal returns (address pointer) {
bytes memory runtimeCode = abi.encodePacked(hex"00", data);
bytes memory creationCode = abi.encodePacked(
hex"60_0B_59_81_38_03_80_92_59_39_F3",
runtimeCode
);
assembly {
pointer := create(0, add(creationCode, 32), mload(creationCode))
}
require(pointer != address(0), "DEPLOYMENT_FAILED");
}
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);
}
function readBytecode(address pointer, uint256 start, uint256 size) private view returns (bytes memory data) {
assembly {
data := mload(0x40)
mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
mstore(data, size)
extcodecopy(pointer, add(data, 32), start, size)
}
}
}
{
"compilationTarget": {
"contracts/crowdfund/CrowdfundFactory.sol": "CrowdfundFactory"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts/",
":solmate/=lib/solmate/src/"
],
"viaIR": true
}
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract AuctionCrowdfund","name":"crowdfund","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"contract IMarketWrapper","name":"market","type":"address"},{"internalType":"contract IERC721","name":"nftContract","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"uint96","name":"maximumBid","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"},{"internalType":"bool","name":"onlyHostCanBid","type":"bool"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"indexed":false,"internalType":"struct AuctionCrowdfundBase.AuctionCrowdfundOptions","name":"opts","type":"tuple"}],"name":"AuctionCrowdfundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract BuyCrowdfund","name":"crowdfund","type":"address"},{"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":"uint256","name":"nftTokenId","type":"uint256"},{"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"},{"internalType":"bool","name":"onlyHostCanBuy","type":"bool"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"indexed":false,"internalType":"struct BuyCrowdfund.BuyCrowdfundOptions","name":"opts","type":"tuple"}],"name":"BuyCrowdfundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract CollectionBatchBuyCrowdfund","name":"crowdfund","type":"address"},{"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":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"indexed":false,"internalType":"struct CollectionBatchBuyCrowdfund.CollectionBatchBuyCrowdfundOptions","name":"opts","type":"tuple"}],"name":"CollectionBatchBuyCrowdfundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract CollectionBuyCrowdfund","name":"crowdfund","type":"address"},{"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":"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":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"indexed":false,"internalType":"struct CollectionBuyCrowdfund.CollectionBuyCrowdfundOptions","name":"opts","type":"tuple"}],"name":"CollectionBuyCrowdfundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract InitialETHCrowdfund","name":"crowdfund","type":"address"},{"indexed":true,"internalType":"contract Party","name":"party","type":"address"},{"components":[{"internalType":"address payable","name":"initialContributor","type":"address"},{"internalType":"address","name":"initialDelegate","type":"address"},{"internalType":"uint96","name":"minContribution","type":"uint96"},{"internalType":"uint96","name":"maxContribution","type":"uint96"},{"internalType":"bool","name":"disableContributingForExistingCard","type":"bool"},{"internalType":"uint96","name":"minTotalContributions","type":"uint96"},{"internalType":"uint96","name":"maxTotalContributions","type":"uint96"},{"internalType":"uint16","name":"exchangeRateBps","type":"uint16"},{"internalType":"uint16","name":"fundingSplitBps","type":"uint16"},{"internalType":"address payable","name":"fundingSplitRecipient","type":"address"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"contract IGateKeeper","name":"gateKeeper","type":"address"},{"internalType":"bytes12","name":"gateKeeperId","type":"bytes12"}],"indexed":false,"internalType":"struct InitialETHCrowdfund.InitialETHCrowdfundOptions","name":"crowdfundOpts","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"},{"internalType":"contract IERC721[]","name":"preciousTokens","type":"address[]"},{"internalType":"uint256[]","name":"preciousTokenIds","type":"uint256[]"},{"internalType":"uint40","name":"rageQuitTimestamp","type":"uint40"}],"indexed":false,"internalType":"struct InitialETHCrowdfund.ETHPartyOptions","name":"partyOpts","type":"tuple"}],"name":"InitialETHCrowdfundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract ReraiseETHCrowdfund","name":"crowdfund","type":"address"},{"components":[{"internalType":"contract Party","name":"party","type":"address"},{"internalType":"address payable","name":"initialContributor","type":"address"},{"internalType":"address","name":"initialDelegate","type":"address"},{"internalType":"uint96","name":"minContribution","type":"uint96"},{"internalType":"uint96","name":"maxContribution","type":"uint96"},{"internalType":"bool","name":"disableContributingForExistingCard","type":"bool"},{"internalType":"uint96","name":"minTotalContributions","type":"uint96"},{"internalType":"uint96","name":"maxTotalContributions","type":"uint96"},{"internalType":"uint16","name":"exchangeRateBps","type":"uint16"},{"internalType":"uint16","name":"fundingSplitBps","type":"uint16"},{"internalType":"address payable","name":"fundingSplitRecipient","type":"address"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"contract IGateKeeper","name":"gateKeeper","type":"address"},{"internalType":"bytes12","name":"gateKeeperId","type":"bytes12"}],"indexed":false,"internalType":"struct ETHCrowdfundBase.ETHCrowdfundOptions","name":"opts","type":"tuple"}],"name":"ReraiseETHCrowdfundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract RollingAuctionCrowdfund","name":"crowdfund","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"contract IMarketWrapper","name":"market","type":"address"},{"internalType":"contract IERC721","name":"nftContract","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"uint96","name":"maximumBid","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"},{"internalType":"bool","name":"onlyHostCanBid","type":"bool"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"indexed":false,"internalType":"struct AuctionCrowdfundBase.AuctionCrowdfundOptions","name":"opts","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"allowedAuctionsMerkleRoot","type":"bytes32"}],"name":"RollingAuctionCrowdfundCreated","type":"event"},{"inputs":[{"internalType":"contract AuctionCrowdfund","name":"crowdfundImpl","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"contract IMarketWrapper","name":"market","type":"address"},{"internalType":"contract IERC721","name":"nftContract","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"uint96","name":"maximumBid","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"},{"internalType":"bool","name":"onlyHostCanBid","type":"bool"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"internalType":"struct AuctionCrowdfundBase.AuctionCrowdfundOptions","name":"opts","type":"tuple"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createAuctionCrowdfund","outputs":[{"internalType":"contract AuctionCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract BuyCrowdfund","name":"crowdfundImpl","type":"address"},{"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":"uint256","name":"nftTokenId","type":"uint256"},{"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"},{"internalType":"bool","name":"onlyHostCanBuy","type":"bool"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"internalType":"struct BuyCrowdfund.BuyCrowdfundOptions","name":"opts","type":"tuple"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createBuyCrowdfund","outputs":[{"internalType":"contract BuyCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract CollectionBatchBuyCrowdfund","name":"crowdfundImpl","type":"address"},{"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":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"internalType":"struct CollectionBatchBuyCrowdfund.CollectionBatchBuyCrowdfundOptions","name":"opts","type":"tuple"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createCollectionBatchBuyCrowdfund","outputs":[{"internalType":"contract CollectionBatchBuyCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract CollectionBuyCrowdfund","name":"crowdfundImpl","type":"address"},{"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":"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":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"internalType":"struct CollectionBuyCrowdfund.CollectionBuyCrowdfundOptions","name":"opts","type":"tuple"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createCollectionBuyCrowdfund","outputs":[{"internalType":"contract CollectionBuyCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract InitialETHCrowdfund","name":"crowdfundImpl","type":"address"},{"components":[{"internalType":"address payable","name":"initialContributor","type":"address"},{"internalType":"address","name":"initialDelegate","type":"address"},{"internalType":"uint96","name":"minContribution","type":"uint96"},{"internalType":"uint96","name":"maxContribution","type":"uint96"},{"internalType":"bool","name":"disableContributingForExistingCard","type":"bool"},{"internalType":"uint96","name":"minTotalContributions","type":"uint96"},{"internalType":"uint96","name":"maxTotalContributions","type":"uint96"},{"internalType":"uint16","name":"exchangeRateBps","type":"uint16"},{"internalType":"uint16","name":"fundingSplitBps","type":"uint16"},{"internalType":"address payable","name":"fundingSplitRecipient","type":"address"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"contract IGateKeeper","name":"gateKeeper","type":"address"},{"internalType":"bytes12","name":"gateKeeperId","type":"bytes12"}],"internalType":"struct InitialETHCrowdfund.InitialETHCrowdfundOptions","name":"crowdfundOpts","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"},{"internalType":"contract IERC721[]","name":"preciousTokens","type":"address[]"},{"internalType":"uint256[]","name":"preciousTokenIds","type":"uint256[]"},{"internalType":"uint40","name":"rageQuitTimestamp","type":"uint40"}],"internalType":"struct InitialETHCrowdfund.ETHPartyOptions","name":"partyOpts","type":"tuple"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createInitialETHCrowdfund","outputs":[{"internalType":"contract InitialETHCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract InitialETHCrowdfund","name":"crowdfundImpl","type":"address"},{"components":[{"internalType":"address payable","name":"initialContributor","type":"address"},{"internalType":"address","name":"initialDelegate","type":"address"},{"internalType":"uint96","name":"minContribution","type":"uint96"},{"internalType":"uint96","name":"maxContribution","type":"uint96"},{"internalType":"bool","name":"disableContributingForExistingCard","type":"bool"},{"internalType":"uint96","name":"minTotalContributions","type":"uint96"},{"internalType":"uint96","name":"maxTotalContributions","type":"uint96"},{"internalType":"uint16","name":"exchangeRateBps","type":"uint16"},{"internalType":"uint16","name":"fundingSplitBps","type":"uint16"},{"internalType":"address payable","name":"fundingSplitRecipient","type":"address"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"contract IGateKeeper","name":"gateKeeper","type":"address"},{"internalType":"bytes12","name":"gateKeeperId","type":"bytes12"}],"internalType":"struct InitialETHCrowdfund.InitialETHCrowdfundOptions","name":"crowdfundOpts","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"},{"internalType":"contract IERC721[]","name":"preciousTokens","type":"address[]"},{"internalType":"uint256[]","name":"preciousTokenIds","type":"uint256[]"},{"internalType":"uint40","name":"rageQuitTimestamp","type":"uint40"}],"internalType":"struct InitialETHCrowdfund.ETHPartyOptions","name":"partyOpts","type":"tuple"},{"internalType":"contract MetadataProvider","name":"customMetadataProvider","type":"address"},{"internalType":"bytes","name":"customMetadata","type":"bytes"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createInitialETHCrowdfundWithMetadata","outputs":[{"internalType":"contract InitialETHCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract ReraiseETHCrowdfund","name":"crowdfundImpl","type":"address"},{"components":[{"internalType":"contract Party","name":"party","type":"address"},{"internalType":"address payable","name":"initialContributor","type":"address"},{"internalType":"address","name":"initialDelegate","type":"address"},{"internalType":"uint96","name":"minContribution","type":"uint96"},{"internalType":"uint96","name":"maxContribution","type":"uint96"},{"internalType":"bool","name":"disableContributingForExistingCard","type":"bool"},{"internalType":"uint96","name":"minTotalContributions","type":"uint96"},{"internalType":"uint96","name":"maxTotalContributions","type":"uint96"},{"internalType":"uint16","name":"exchangeRateBps","type":"uint16"},{"internalType":"uint16","name":"fundingSplitBps","type":"uint16"},{"internalType":"address payable","name":"fundingSplitRecipient","type":"address"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"contract IGateKeeper","name":"gateKeeper","type":"address"},{"internalType":"bytes12","name":"gateKeeperId","type":"bytes12"}],"internalType":"struct ETHCrowdfundBase.ETHCrowdfundOptions","name":"opts","type":"tuple"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createReraiseETHCrowdfund","outputs":[{"internalType":"contract ReraiseETHCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract RollingAuctionCrowdfund","name":"crowdfundImpl","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"customizationPresetId","type":"uint256"},{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"contract IMarketWrapper","name":"market","type":"address"},{"internalType":"contract IERC721","name":"nftContract","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"},{"internalType":"uint40","name":"duration","type":"uint40"},{"internalType":"uint96","name":"maximumBid","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"},{"internalType":"bool","name":"onlyHostCanBid","type":"bool"},{"components":[{"internalType":"contract Party","name":"partyImpl","type":"address"},{"internalType":"contract IPartyFactory","name":"partyFactory","type":"address"},{"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"},{"components":[{"internalType":"bool","name":"enableAddAuthorityProposal","type":"bool"},{"internalType":"bool","name":"allowArbCallsToSpendPartyEth","type":"bool"},{"internalType":"bool","name":"allowOperators","type":"bool"},{"internalType":"bool","name":"distributionsRequireVote","type":"bool"}],"internalType":"struct ProposalStorage.ProposalEngineOpts","name":"proposalEngineOpts","type":"tuple"}],"internalType":"struct AuctionCrowdfundBase.AuctionCrowdfundOptions","name":"opts","type":"tuple"},{"internalType":"bytes32","name":"allowedAuctionsMerkleRoot","type":"bytes32"},{"internalType":"bytes","name":"createGateCallData","type":"bytes"}],"name":"createRollingAuctionCrowdfund","outputs":[{"internalType":"contract RollingAuctionCrowdfund","name":"inst","type":"address"}],"stateMutability":"payable","type":"function"}]