Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement ERC-1167 minimal proxies #289

Merged
merged 2 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/crowdfund/AuctionCrowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract AuctionCrowdfund is AuctionCrowdfundBase {
/// revert if called outside the constructor.
/// @param opts Options used to initialize the crowdfund. These are fixed
/// and cannot be changed later.
function initialize(AuctionCrowdfundOptions memory opts) external payable onlyConstructor {
function initialize(AuctionCrowdfundOptions memory opts) external payable onlyInitialize {
AuctionCrowdfundBase._initialize(opts);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/crowdfund/BuyCrowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ contract BuyCrowdfund is BuyCrowdfundBase {
/// revert if called outside the constructor.
/// @param opts Options used to initialize the crowdfund. These are fixed
/// and cannot be changed later.
function initialize(BuyCrowdfundOptions memory opts) external payable onlyConstructor {
function initialize(BuyCrowdfundOptions memory opts) external payable onlyInitialize {
if (opts.onlyHostCanBuy && opts.governanceOpts.hosts.length == 0) {
revert MissingHostsError();
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/crowdfund/CollectionBatchBuyCrowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ contract CollectionBatchBuyCrowdfund is BuyCrowdfundBase {
/// and cannot be changed later.
function initialize(
CollectionBatchBuyCrowdfundOptions memory opts
) external payable onlyConstructor {
) external payable onlyInitialize {
if (opts.governanceOpts.hosts.length == 0) {
revert MissingHostsError();
}
Expand Down
4 changes: 1 addition & 3 deletions contracts/crowdfund/CollectionBuyCrowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ contract CollectionBuyCrowdfund is BuyCrowdfundBase {
/// revert if called outside the constructor.
/// @param opts Options used to initialize the crowdfund. These are fixed
/// and cannot be changed later.
function initialize(
CollectionBuyCrowdfundOptions memory opts
) external payable onlyConstructor {
function initialize(CollectionBuyCrowdfundOptions memory opts) external payable onlyInitialize {
if (opts.governanceOpts.hosts.length == 0) {
revert MissingHostsError();
}
Expand Down
84 changes: 21 additions & 63 deletions contracts/crowdfund/CrowdfundFactory.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;

import { Clones } from "openzeppelin/contracts/proxy/Clones.sol";

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";
Expand All @@ -18,6 +18,7 @@ import { Party } from "../party/Party.sol";

/// @notice Factory used to deploys new proxified `Crowdfund` instances.
contract CrowdfundFactory {
using Clones for address;
using LibRawResult for bytes;

event BuyCrowdfundCreated(
Expand Down Expand Up @@ -72,14 +73,8 @@ contract CrowdfundFactory {
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))
)
)
);
inst = BuyCrowdfund(address(crowdfundImpl).clone());
inst.initialize{ value: msg.value }(opts);
emit BuyCrowdfundCreated(msg.sender, inst, opts);
}

Expand All @@ -96,14 +91,8 @@ contract CrowdfundFactory {
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))
)
)
);
inst = AuctionCrowdfund(payable(address(crowdfundImpl).clone()));
inst.initialize{ value: msg.value }(opts);
emit AuctionCrowdfundCreated(msg.sender, inst, opts);
}

Expand All @@ -121,17 +110,8 @@ contract CrowdfundFactory {
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)
)
)
)
);
inst = RollingAuctionCrowdfund(payable(address(crowdfundImpl).clone()));
inst.initialize{ value: msg.value }(opts, allowedAuctionsMerkleRoot);
emit RollingAuctionCrowdfundCreated(msg.sender, inst, opts, allowedAuctionsMerkleRoot);
}

Expand All @@ -147,14 +127,8 @@ contract CrowdfundFactory {
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))
)
)
);
inst = CollectionBuyCrowdfund(address(crowdfundImpl).clone());
inst.initialize{ value: msg.value }(opts);
emit CollectionBuyCrowdfundCreated(msg.sender, inst, opts);
}

Expand All @@ -170,14 +144,8 @@ contract CrowdfundFactory {
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))
)
)
);
inst = CollectionBatchBuyCrowdfund(address(crowdfundImpl).clone());
inst.initialize{ value: msg.value }(opts);
emit CollectionBatchBuyCrowdfundCreated(msg.sender, inst, opts);
}

Expand Down Expand Up @@ -227,16 +195,12 @@ contract CrowdfundFactory {
crowdfundOpts.gateKeeperId,
createGateCallData
);
inst = InitialETHCrowdfund(
payable(
new Proxy{ value: msg.value }(
Implementation(address(crowdfundImpl)),
abi.encodeCall(
InitialETHCrowdfund.initialize,
(crowdfundOpts, partyOpts, customMetadataProvider, customMetadata)
)
)
)
inst = InitialETHCrowdfund(address(crowdfundImpl).clone());
inst.initialize{ value: msg.value }(
crowdfundOpts,
partyOpts,
customMetadataProvider,
customMetadata
);
emit InitialETHCrowdfundCreated(msg.sender, inst, inst.party(), crowdfundOpts, partyOpts);
}
Expand All @@ -253,14 +217,8 @@ contract CrowdfundFactory {
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))
)
)
);
inst = ReraiseETHCrowdfund(address(crowdfundImpl).clone());
inst.initialize{ value: msg.value }(opts);
emit ReraiseETHCrowdfundCreated(msg.sender, inst, opts);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/crowdfund/InitialETHCrowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ contract InitialETHCrowdfund is ETHCrowdfundBase {
ETHPartyOptions memory partyOpts,
MetadataProvider customMetadataProvider,
bytes memory customMetadata
) external payable onlyConstructor {
) external payable onlyInitialize {
// Create party the initial crowdfund will be for.
Party party_ = _createParty(partyOpts, customMetadataProvider, customMetadata);

Expand Down
2 changes: 1 addition & 1 deletion contracts/crowdfund/ReraiseETHCrowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ contract ReraiseETHCrowdfund is ETHCrowdfundBase, CrowdfundNFT {
/// @notice Initializer to be delegatecalled by `Proxy` constructor. Will
/// revert if called outside the constructor.
/// @param opts The options to initialize the crowdfund with.
function initialize(ETHCrowdfundOptions memory opts) external payable onlyConstructor {
function initialize(ETHCrowdfundOptions memory opts) external payable onlyInitialize {
// Initialize the crowdfund.
ETHCrowdfundBase._initialize(opts);

Expand Down
2 changes: 1 addition & 1 deletion contracts/crowdfund/RollingAuctionCrowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract RollingAuctionCrowdfund is AuctionCrowdfundBase {
function initialize(
AuctionCrowdfundBase.AuctionCrowdfundOptions memory opts,
bytes32 allowedAuctionsMerkleRoot_
) external payable onlyConstructor {
) external payable onlyInitialize {
// Initialize the base contract.
AuctionCrowdfundBase._initialize(opts);

Expand Down
2 changes: 1 addition & 1 deletion contracts/party/Party.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract Party is PartyGovernanceNFT {
/// @notice Initializer to be delegatecalled by `Proxy` constructor. Will
/// revert if called outside the constructor.
/// @param initData Options used to initialize the party governance.
function initialize(PartyInitData memory initData) external onlyConstructor {
function initialize(PartyInitData memory initData) external onlyInitialize {
PartyGovernanceNFT._initialize(
initData.options.name,
initData.options.symbol,
Expand Down
16 changes: 6 additions & 10 deletions contracts/party/PartyFactory.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;

import { Clones } from "openzeppelin/contracts/proxy/Clones.sol";

import { IERC721 } from "../tokens/IERC721.sol";
import { Proxy } from "../utils/Proxy.sol";

import { Party } from "./Party.sol";
import { IPartyFactory } from "./IPartyFactory.sol";
import { IGlobals } from "../globals/IGlobals.sol";
import { LibGlobals } from "../globals/LibGlobals.sol";
import { Implementation } from "../utils/Implementation.sol";
import { MetadataRegistry } from "../renderers/MetadataRegistry.sol";
import { MetadataProvider } from "../renderers/MetadataProvider.sol";

/// @notice Factory used to deploy new proxified `Party` instances.
contract PartyFactory is IPartyFactory {
using Clones for address;

error NoAuthorityError();

// The `Globals` contract storing global configuration values. This contract
Expand Down Expand Up @@ -46,14 +48,8 @@ contract PartyFactory is IPartyFactory {
authorities: authorities,
rageQuitTimestamp: rageQuitTimestamp
});
party = Party(
payable(
new Proxy(
Implementation(address(partyImpl)),
abi.encodeCall(Party.initialize, (initData))
)
)
);
party = Party(payable(address(partyImpl).clone()));
party.initialize(initData);
emit PartyCreated(party, opts, preciousTokens, preciousTokenIds, msg.sender);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/proposals/ProposalExecutionEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ contract ProposalExecutionEngine is
);
}
_initProposalImpl(newImpl, initData);
emit ProposalEngineImplementationUpgraded(address(IMPL), expectedImpl);
emit ProposalEngineImplementationUpgraded(address(implementation), expectedImpl);
}

// Retrieve the explicit storage bucket for the ProposalExecutionEngine logic.
Expand Down
31 changes: 22 additions & 9 deletions contracts/utils/Implementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,41 @@ pragma solidity 0.8.20;

// Base contract for all contracts intended to be delegatecalled into.
abstract contract Implementation {
event Initialized();

error AlreadyInitialized();
error OnlyDelegateCallError();
error OnlyConstructorError();

address public immutable IMPL;
/// @notice The address of the implementation contract.
address public immutable implementation;

/// @notice Whether or not the implementation has been initialized.
bool public initialized;

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

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

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

initialized = true;
emit Initialized();

_;
}

/// @notice The address of the implementation contract.
/// @dev This is an alias for `implementation` for backwards compatibility.
function IMPL() external view returns (address) {
return implementation;
}
}
37 changes: 0 additions & 37 deletions contracts/utils/Proxy.sol

This file was deleted.

Loading