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

Add deployer helper contracts #120

Open
wants to merge 1 commit into
base: v2-audit
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
97 changes: 97 additions & 0 deletions src/deployers/CollectionPlusDeployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import { IManager } from "../manager/IManager.sol";
import { IBaseToken } from "../token/interfaces/IBaseToken.sol";
import { IPropertyIPFSMetadataRenderer } from "../metadata/interfaces/IPropertyIPFSMetadataRenderer.sol";
import { ERC721RedeemMinter } from "../minters/ERC721RedeemMinter.sol";
import { TokenTypesV2 } from "../token/default/types/TokenTypesV2.sol";
import { Ownable } from "../lib/utils/Ownable.sol";

/// @title CollectionPlusDeployer
/// @notice A deployer that allows a user to deploy a Collection Plus style DAO in one transaction
/// @author @neokry
contract CollectionPlusDeployer {
/// ///
/// IMMUTABLES ///
/// ///

/// @notice The contract upgrade manager
IManager public immutable manager;

/// @notice The minter to deploy the DAO with
ERC721RedeemMinter public immutable redeemMinter;

/// ///
/// STRUCTS ///
/// ///

/// @notice Adds properties and/or items to be pseudo-randomly chosen from during token minting
/// @param names The names of the properties to add
/// @param items The items to add to each property
/// @param ipfsGroup The IPFS base URI and extension
struct MetadataParams {
string[] names;
IPropertyIPFSMetadataRenderer.ItemParam[] items;
IPropertyIPFSMetadataRenderer.IPFSGroup ipfsGroup;
}

/// ///
/// CONSTRUCTOR ///
/// ///

constructor(IManager _manager, ERC721RedeemMinter _redeemMinter) {
manager = _manager;
redeemMinter = _redeemMinter;
}

/// ///
/// DEPLOYMENT ///
/// ///

/// @notice Deploys a DAO with mirror and token redeeming enabled
/// @dev The address of this deployer must be set as founder 0
/// @param _founderParams The DAO founders
/// @param _tokenParams The ERC-721 token settings
/// @param _auctionParams The auction settings
/// @param _govParams The governance settings
/// @param _metadataParams The metadata settings
/// @param _minterParams The minter settings
function deploy(
IManager.FounderParams[] calldata _founderParams,
IManager.MirrorTokenParams calldata _tokenParams,
IManager.AuctionParams calldata _auctionParams,
IManager.GovParams calldata _govParams,
MetadataParams calldata _metadataParams,
ERC721RedeemMinter.RedeemSettings calldata _minterParams
) external returns (address) {
// Deploy the DAO with token mirroring enabled
(address token, address metadata, address auction, address treasury, ) = manager.deployWithMirror(
_founderParams,
_tokenParams,
_auctionParams,
_govParams
);

// Setup minter settings to use the redeem minter
TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1);
minters[0] = TokenTypesV2.MinterParams({ minter: address(redeemMinter), allowed: true });

// Add new minter
IBaseToken(token).updateMinters(minters);

// Initilize minter with given params
redeemMinter.setMintSettings(token, _minterParams);

// Initilize metadata renderer with given params
IPropertyIPFSMetadataRenderer(metadata).addProperties(_metadataParams.names, _metadataParams.items, _metadataParams.ipfsGroup);

// Transfer ownership of token contract
Ownable(token).transferOwnership(treasury);

// Transfer ownership of auction contract
Ownable(auction).transferOwnership(treasury);

return token;
}
}
206 changes: 206 additions & 0 deletions src/deployers/MigrationDeployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import { IManager } from "../manager/IManager.sol";
import { IBaseToken } from "../token/interfaces/IBaseToken.sol";
import { IPropertyIPFSMetadataRenderer } from "../metadata/interfaces/IPropertyIPFSMetadataRenderer.sol";
import { MerkleReserveMinter } from "../minters/MerkleReserveMinter.sol";
import { TokenTypesV2 } from "../token/default/types/TokenTypesV2.sol";
import { Ownable } from "../lib/utils/Ownable.sol";
import { ICrossDomainMessenger } from "./interfaces/ICrossDomainMessenger.sol";

/// @title MigrationDeployer
/// @notice A deployer that allows a DAO to migrate from L1 to L2
/// @author @neokry
contract MigrationDeployer {
/// ///
/// EVENTS ///
/// ///

/// @notice Deployer has been set
event DeployerSet(address deployer);

/// ///
/// ERRORS ///
/// ///

/// @dev Caller is not cross domain messenger
error NOT_CROSS_DOMAIN_MESSENGER();

/// @dev DAO is already deployed
error DAO_ALREADY_DEPLOYED();

/// @dev No DAO has been deployed
error NO_DAO_DEPLOYED();

/// @dev Transfer failed
error TRANSFER_FAILED();

/// ///
/// IMMUTABLES ///
/// ///

/// @notice The contract upgrade manager
IManager public immutable manager;

/// @notice The minter to deploy the DAO with
MerkleReserveMinter public immutable merkleMinter;

/// @notice The cross domain messenger for the chain
ICrossDomainMessenger public immutable crossDomainMessenger;

/// ///
/// STORAGE ///
/// ///

/// @notice Mapping of L1 deployer => L2 deployed token
mapping(address => address) public crossDomainDeployerToToken;

/// ///
/// MODIFIERS ///
/// ///

/// @notice Modifier to revert if sender is not cross domain messenger
modifier onlyCrossDomainMessenger() {
if (msg.sender != address(crossDomainMessenger)) revert NOT_CROSS_DOMAIN_MESSENGER();
_;
}

/// ///
/// CONSTRUCTOR ///
/// ///

constructor(
IManager _manager,
MerkleReserveMinter _merkleMinter,
ICrossDomainMessenger _crossDomainMessenger
) {
manager = _manager;
merkleMinter = _merkleMinter;
crossDomainMessenger = _crossDomainMessenger;
}

/// ///
/// DEPLOYMENT ///
/// ///

/// @notice Deploys a DAO via cross domain message
/// @dev The address of this deployer must be set as founder 0
/// @param _founderParams The DAO founders
/// @param _tokenParams The ERC-721 token settings
/// @param _auctionParams The auction settings
/// @param _govParams The governance settings
/// @param _minterParams The minter settings
function deploy(
IManager.FounderParams[] calldata _founderParams,
IManager.TokenParams calldata _tokenParams,
IManager.AuctionParams calldata _auctionParams,
IManager.GovParams calldata _govParams,
MerkleReserveMinter.MerkleMinterSettings calldata _minterParams
) external onlyCrossDomainMessenger returns (address token) {
if (_getTokenFromSender() != address(0)) {
revert DAO_ALREADY_DEPLOYED();
}

// Deploy the DAO
(address _token, , , , ) = manager.deploy(_founderParams, _tokenParams, _auctionParams, _govParams);

// Setup minter settings to use the redeem minter
TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1);
minters[0] = TokenTypesV2.MinterParams({ minter: address(merkleMinter), allowed: true });

// Add new minter
IBaseToken(_token).updateMinters(minters);

// Initilize minter with given params
merkleMinter.setMintSettings(_token, _minterParams);

// Set the deployer
_setTokenDeployer(_token);

return (_token);
}

///@notice Adds metadata properties to the migrated DAO
/// @param _names The names of the properties to add
/// @param _items The items to add to each property
/// @param _ipfsGroup The IPFS base URI and extension
function addMetadataProperties(
string[] calldata _names,
IPropertyIPFSMetadataRenderer.ItemParam[] calldata _items,
IPropertyIPFSMetadataRenderer.IPFSGroup calldata _ipfsGroup
) external onlyCrossDomainMessenger {
(, address metadata, , , ) = _getDAOAddressesFromSender();
IPropertyIPFSMetadataRenderer(metadata).addProperties(_names, _items, _ipfsGroup);
}

///@notice Called once all metadata properties are added to set ownership of migrated DAO contracts to treasury
function finalize() external onlyCrossDomainMessenger {
(address token, , address auction, address treasury, ) = _getDAOAddressesFromSender();

// Transfer ownership of token contract
Ownable(token).transferOwnership(treasury);

// Transfer ownership of auction contract
Ownable(auction).transferOwnership(treasury);
}

///@notice Resets the stored deployment if L1 DAO wants to redeploy
function resetDeployment() external onlyCrossDomainMessenger {
_resetTokenDeployer();
}

/// ///
/// DEPOSIT ///
/// ///

///@notice Helper method to deposit ether from L1 DAO treasury to L2 DAO treasury
function depositToTreasury() external payable onlyCrossDomainMessenger {
(, , , address treasury, ) = _getDAOAddressesFromSender();

(bool success, ) = treasury.call{ value: msg.value }("");

// Revert if transfer fails
if (!success) {
revert TRANSFER_FAILED();
}
}

/// ///
/// PRIVATE ///
/// ///

function _xMsgSender() private view returns (address) {
return crossDomainMessenger.xDomainMessageSender();
}

function _setTokenDeployer(address token) private {
crossDomainDeployerToToken[_xMsgSender()] = token;
}

function _resetTokenDeployer() private {
delete crossDomainDeployerToToken[_xMsgSender()];
}

function _getTokenFromSender() private view returns (address) {
return crossDomainDeployerToToken[_xMsgSender()];
}

function _getDAOAddressesFromSender()
private
returns (
address token,
address metadata,
address auction,
address treasury,
address governor
)
{
address _token = _getTokenFromSender();

if (_token == address(0)) revert NO_DAO_DEPLOYED();

(address _metadata, address _auction, address _treasury, address _governor) = manager.getAddresses(_token);
return (_token, _metadata, _auction, _treasury, _governor);
}
}
11 changes: 11 additions & 0 deletions src/deployers/interfaces/ICrossDomainMessenger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

/// @title ICrossDomainMessenger
interface ICrossDomainMessenger {
/// @notice Retrieves the address of the contract or wallet that initiated the currently
/// executing message on the other chain. Will throw an error if there is no message
/// currently being executed. Allows the recipient of a call to see who triggered it.
/// @return Address of the sender of the currently executing message on the other chain.
function xDomainMessageSender() external view returns (address);
}
Loading