VETokenVotingDaoFactory

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/VETokenVotingDaoFactory.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import { DAO } from "@aragon/osx/core/dao/DAO.sol";
import { DAOFactory } from "@aragon/osx/framework/dao/DAOFactory.sol";
import { PluginSetupProcessor } from "@aragon/osx/framework/plugin/setup/PluginSetupProcessor.sol";
import { PluginRepoFactory } from "@aragon/osx/framework/plugin/repo/PluginRepoFactory.sol";
import { PluginRepo } from "@aragon/osx/framework/plugin/repo/PluginRepo.sol";
import { PermissionManager } from "@aragon/osx/core/permission/PermissionManager.sol";
import { PermissionLib } from "@aragon/osx-commons-contracts/src/permission/PermissionLib.sol";
import { Action } from "@aragon/osx-commons-contracts/src/executors/IExecutor.sol";
import { IPluginSetup } from "@aragon/osx-commons-contracts/src/plugin/setup/IPluginSetup.sol";
import { hashHelpers, PluginSetupRef } from "@aragon/osx/framework/plugin/setup/PluginSetupProcessorHelpers.sol";

import { VotingEscrowV1_2_0 as VotingEscrow } from "@escrow/VotingEscrowIncreasing_v1_2_0.sol";
import { ClockV1_2_0 as Clock } from "@clock/Clock_v1_2_0.sol";
import { LockV1_2_0 as Lock } from "@lock/Lock_v1_2_0.sol";
import { LinearIncreasingCurve as Curve } from "@curve/LinearIncreasingCurve.sol";
import { DynamicExitQueue as ExitQueue } from "@queue/DynamicExitQueue.sol";
import { EscrowIVotesAdapter } from "@delegation/EscrowIVotesAdapter.sol";
import { AddressGaugeVoter } from "@voting/AddressGaugeVoter.sol";

import { VESystemSetup, VESystemSetupParams } from "./VESystemSetup.sol";
import { TokenVotingSetupHats } from "@token-voting-hats/TokenVotingSetupHats.sol";
import { TokenVotingHats } from "@token-voting-hats/TokenVotingHats.sol";
import { AdminSetup } from "@admin-plugin/AdminSetup.sol";
import { Admin } from "@admin-plugin/Admin.sol";
import { MajorityVotingBase } from "@token-voting-hats/base/MajorityVotingBase.sol";
import { IPlugin } from "@aragon/osx-commons-contracts/src/plugin/IPlugin.sol";
import { GovernanceERC20 } from "@token-voting-hats/erc20/GovernanceERC20.sol";

/// @notice DAO configuration parameters
struct DaoConfig {
  string metadataUri;
  string subdomain;
}

/// @notice Comprehensive VE system configuration
struct VeSystemConfig {
  // Underlying token
  address underlyingToken;
  uint256 minDeposit;
  // VE token naming
  string veTokenName;
  string veTokenSymbol;
  // Voting escrow settings
  uint48 minLockDuration;
  uint16 feePercent;
  uint48 cooldownPeriod;
  // Voting power curve parameters
  int256 curveConstant;
  int256 curveLinear;
  int256 curveQuadratic;
  uint48 curveMaxEpochs;
}

/// @notice Token voting hats plugin configuration (flattened for ease of use)
struct TokenVotingHatsPluginConfig {
  // Governance settings
  MajorityVotingBase.VotingMode votingMode;
  uint32 supportThreshold;
  uint32 minParticipation;
  uint64 minDuration;
  uint256 minProposerVotingPower;
  // Hats Protocol settings
  uint256 proposerHatId;
  uint256 voterHatId;
  uint256 executorHatId;
}

/// @notice The struct containing all the parameters to deploy the DAO
struct DeploymentParameters {
  // Configuration structs
  DaoConfig dao;
  VeSystemConfig veSystem;
  TokenVotingHatsPluginConfig tokenVotingHats;

  // Plugin setup contracts (must be deployed first)
  VESystemSetup veSystemSetup;
  TokenVotingSetupHats tokenVotingSetup;
  PluginRepo tokenVotingPluginRepo;
  AdminSetup adminSetup;
  PluginRepo adminPluginRepo;
  address adminAddress;

  // Plugin metadata
  string tokenVotingHatsMetadata;

  // Plugin repo version info
  uint8 pluginRepoRelease;
  uint16 pluginRepoBuild;

  // OSx framework addresses (chain-specific)
  address osxDaoFactory;
  PluginSetupProcessor pluginSetupProcessor;
  PluginRepoFactory pluginRepoFactory;
}

/// @notice Struct containing all VE system components
struct VEPluginSet {
  VotingEscrow votingEscrow;
  Clock clock;
  Curve curve;
  ExitQueue exitQueue;
  Lock nftLock;
  EscrowIVotesAdapter ivotesAdapter;
  AddressGaugeVoter voter;
}

/// @notice Contains the artifacts that resulted from running a deployment
struct Deployment {
  DAO dao;
  VEPluginSet veSystem;
  TokenVotingHats tokenVotingPlugin;
  PluginRepo tokenVotingPluginRepo;
  Admin adminPlugin;
  PluginRepo adminPluginRepo;
}

/// @notice A singleton contract designed to run the deployment once and become a read-only store of the contracts
/// deployed
contract VETokenVotingDaoFactory {
  address public immutable deployer;

  function version() external pure returns (string memory) {
    return "1.0.0";
  }

  error AlreadyDeployed();
  error Unauthorized();

  DeploymentParameters parameters;
  Deployment deployment;

  constructor(DeploymentParameters memory _parameters) {
    // Record the deployer to prevent unauthorized deployments
    deployer = msg.sender;

    // Configuration structs
    parameters.dao = _parameters.dao;
    parameters.veSystem = _parameters.veSystem;
    parameters.tokenVotingHats = _parameters.tokenVotingHats;

    // Plugin setup contracts
    parameters.veSystemSetup = _parameters.veSystemSetup;
    parameters.tokenVotingSetup = _parameters.tokenVotingSetup;
    parameters.tokenVotingPluginRepo = _parameters.tokenVotingPluginRepo;
    parameters.adminSetup = _parameters.adminSetup;
    parameters.adminPluginRepo = _parameters.adminPluginRepo;
    parameters.adminAddress = _parameters.adminAddress;

    // Plugin metadata
    parameters.tokenVotingHatsMetadata = _parameters.tokenVotingHatsMetadata;

    // Plugin repo version info
    parameters.pluginRepoRelease = _parameters.pluginRepoRelease;
    parameters.pluginRepoBuild = _parameters.pluginRepoBuild;

    // OSx framework addresses
    parameters.osxDaoFactory = _parameters.osxDaoFactory;
    parameters.pluginSetupProcessor = _parameters.pluginSetupProcessor;
    parameters.pluginRepoFactory = _parameters.pluginRepoFactory;
  }

  function deployOnce() public {
    if (msg.sender != deployer) revert Unauthorized();
    if (address(deployment.dao) != address(0)) revert AlreadyDeployed();

    DAO dao = _prepareDao();
    deployment.dao = dao;

    _grantApplyInstallationPermissions(dao);

    VEPluginSet memory veSystem = _deployVESystem(dao);
    deployment.veSystem = veSystem;

    (TokenVotingHats tvPlugin, PluginRepo tvRepo) = _installTokenVotingHats(dao, veSystem);
    deployment.tokenVotingPlugin = tvPlugin;
    deployment.tokenVotingPluginRepo = tvRepo;

    (Admin adminPlugin, PluginRepo adminRepo) = _installAdminPlugin(dao);
    deployment.adminPlugin = adminPlugin;
    deployment.adminPluginRepo = adminRepo;

    _revokeApplyInstallationPermissions(dao);
    _revokeOwnerPermission(dao);
  }

  function _prepareDao() internal returns (DAO dao) {
    (dao,) = DAOFactory(parameters.osxDaoFactory)
      .createDao(
        DAOFactory.DAOSettings({
          trustedForwarder: address(0),
          daoURI: "",
          subdomain: parameters.dao.subdomain,
          metadata: bytes(parameters.dao.metadataUri)
        }),
        new DAOFactory.PluginSettings[](0)
      );

    // Grant ROOT_PERMISSION to this factory so it can install plugins
    Action[] memory actions = new Action[](1);
    actions[0].to = address(dao);
    actions[0].data = abi.encodeCall(PermissionManager.grant, (address(dao), address(this), dao.ROOT_PERMISSION_ID()));

    dao.execute(bytes32(0), actions, 0);
  }

  function _deployVESystem(DAO dao) internal returns (VEPluginSet memory veSystem) {
    VESystemSetupParams memory setupParams = VESystemSetupParams({
      underlyingToken: parameters.veSystem.underlyingToken,
      veTokenName: parameters.veSystem.veTokenName,
      veTokenSymbol: parameters.veSystem.veTokenSymbol,
      minDeposit: parameters.veSystem.minDeposit,
      minLockDuration: parameters.veSystem.minLockDuration,
      feePercent: parameters.veSystem.feePercent,
      cooldownPeriod: parameters.veSystem.cooldownPeriod,
      curveConstant: parameters.veSystem.curveConstant,
      curveLinear: parameters.veSystem.curveLinear,
      curveQuadratic: parameters.veSystem.curveQuadratic,
      curveMaxEpochs: parameters.veSystem.curveMaxEpochs
    });

    (address escrowProxy, IPluginSetup.PreparedSetupData memory preparedSetupData) =
      parameters.veSystemSetup.prepareInstallation(address(dao), abi.encode(setupParams));

    veSystem.votingEscrow = VotingEscrow(escrowProxy);
    veSystem.clock = Clock(preparedSetupData.helpers[0]);
    veSystem.curve = Curve(preparedSetupData.helpers[1]);
    veSystem.exitQueue = ExitQueue(preparedSetupData.helpers[2]);
    veSystem.nftLock = Lock(preparedSetupData.helpers[3]);
    veSystem.ivotesAdapter = EscrowIVotesAdapter(preparedSetupData.helpers[4]);
    veSystem.voter = AddressGaugeVoter(preparedSetupData.helpers[5]);

    for (uint256 i = 0; i < preparedSetupData.permissions.length; i++) {
      PermissionLib.MultiTargetPermission memory perm = preparedSetupData.permissions[i];
      if (perm.operation == PermissionLib.Operation.Grant) {
        dao.grant(perm.where, perm.who, perm.permissionId);
      } else if (perm.operation == PermissionLib.Operation.Revoke) {
        dao.revoke(perm.where, perm.who, perm.permissionId);
      }
    }

    // Temporarily grant factory ESCROW_ADMIN_ROLE to wire components
    dao.grant(address(veSystem.votingEscrow), address(this), veSystem.votingEscrow.ESCROW_ADMIN_ROLE());

    // Wire up VE components
    veSystem.votingEscrow.setCurve(address(veSystem.curve));
    veSystem.votingEscrow.setQueue(address(veSystem.exitQueue));
    veSystem.votingEscrow.setLockNFT(address(veSystem.nftLock));
    veSystem.votingEscrow.setIVotesAdapter(address(veSystem.ivotesAdapter));
    veSystem.votingEscrow.setVoter(address(veSystem.voter));

    // Revoke temporary permission
    dao.revoke(address(veSystem.votingEscrow), address(this), veSystem.votingEscrow.ESCROW_ADMIN_ROLE());
  }

  function _installTokenVotingHats(DAO dao, VEPluginSet memory veSystem)
    internal
    returns (TokenVotingHats plugin, PluginRepo pluginRepo)
  {
    if (address(parameters.tokenVotingPluginRepo) == address(0)) {
      pluginRepo = PluginRepoFactory(parameters.pluginRepoFactory)
        .createPluginRepoWithFirstVersion(
          "token-voting-hats", address(parameters.tokenVotingSetup), address(dao), " ", " "
        );
    } else {
      pluginRepo = parameters.tokenVotingPluginRepo;
    }

    PluginRepo.Tag memory repoTag = PluginRepo.Tag(parameters.pluginRepoRelease, parameters.pluginRepoBuild);

    bytes memory installData = parameters.tokenVotingSetup
      .encodeInstallationParametersHats(
        MajorityVotingBase.VotingSettings({
          votingMode: parameters.tokenVotingHats.votingMode,
          supportThreshold: parameters.tokenVotingHats.supportThreshold,
          minParticipation: parameters.tokenVotingHats.minParticipation,
          minDuration: parameters.tokenVotingHats.minDuration,
          minProposerVotingPower: parameters.tokenVotingHats.minProposerVotingPower
        }),
        TokenVotingSetupHats.TokenSettings({ addr: address(veSystem.ivotesAdapter), name: "", symbol: "" }),
        GovernanceERC20.MintSettings({
          receivers: new address[](0), amounts: new uint256[](0), ensureDelegationOnMint: false
        }),
        IPlugin.TargetConfig({ target: address(dao), operation: IPlugin.Operation.Call }),
        0,
        bytes(parameters.tokenVotingHatsMetadata),
        new address[](0),
        TokenVotingSetupHats.HatsConfig({
          proposerHatId: parameters.tokenVotingHats.proposerHatId,
          voterHatId: parameters.tokenVotingHats.voterHatId,
          executorHatId: parameters.tokenVotingHats.executorHatId
        })
      );

    (address pluginAddress, IPluginSetup.PreparedSetupData memory preparedSetupData) = parameters.pluginSetupProcessor
      .prepareInstallation(
        address(dao),
        PluginSetupProcessor.PrepareInstallationParams({
          pluginSetupRef: PluginSetupRef(repoTag, pluginRepo), data: installData
        })
      );

    parameters.pluginSetupProcessor
      .applyInstallation(
        address(dao),
        PluginSetupProcessor.ApplyInstallationParams({
          pluginSetupRef: PluginSetupRef(repoTag, pluginRepo),
          plugin: pluginAddress,
          permissions: preparedSetupData.permissions,
          helpersHash: hashHelpers(preparedSetupData.helpers)
        })
      );

    plugin = TokenVotingHats(pluginAddress);
  }

  function _installAdminPlugin(DAO dao) internal returns (Admin plugin, PluginRepo pluginRepo) {
    pluginRepo = parameters.adminPluginRepo;

    // Use release 1, build 2 (latest admin plugin version)
    PluginRepo.Tag memory repoTag = PluginRepo.Tag(1, 2);

    // Encode installation parameters: admin address and target config
    bytes memory installData = abi.encode(
      parameters.adminAddress, IPlugin.TargetConfig({ target: address(dao), operation: IPlugin.Operation.Call })
    );

    (address pluginAddress, IPluginSetup.PreparedSetupData memory preparedSetupData) = parameters.pluginSetupProcessor
      .prepareInstallation(
        address(dao),
        PluginSetupProcessor.PrepareInstallationParams({
          pluginSetupRef: PluginSetupRef(repoTag, pluginRepo), data: installData
        })
      );

    parameters.pluginSetupProcessor
      .applyInstallation(
        address(dao),
        PluginSetupProcessor.ApplyInstallationParams({
          pluginSetupRef: PluginSetupRef(repoTag, pluginRepo),
          plugin: pluginAddress,
          permissions: preparedSetupData.permissions,
          helpersHash: hashHelpers(preparedSetupData.helpers)
        })
      );

    plugin = Admin(pluginAddress);
  }

  function _grantApplyInstallationPermissions(DAO dao) internal {
    dao.grant(address(dao), address(parameters.pluginSetupProcessor), dao.ROOT_PERMISSION_ID());
    dao.grant(
      address(parameters.pluginSetupProcessor),
      address(this),
      parameters.pluginSetupProcessor.APPLY_INSTALLATION_PERMISSION_ID()
    );
  }

  function _revokeApplyInstallationPermissions(DAO dao) internal {
    dao.revoke(
      address(parameters.pluginSetupProcessor),
      address(this),
      parameters.pluginSetupProcessor.APPLY_INSTALLATION_PERMISSION_ID()
    );
    dao.revoke(address(dao), address(parameters.pluginSetupProcessor), dao.ROOT_PERMISSION_ID());
  }

  function _revokeOwnerPermission(DAO dao) internal {
    dao.revoke(address(dao), address(this), dao.EXECUTE_PERMISSION_ID());
    dao.revoke(address(dao), address(this), dao.ROOT_PERMISSION_ID());
  }

  function getDeploymentParameters() public view returns (DeploymentParameters memory) {
    return parameters;
  }

  function getDeployment() public view returns (Deployment memory) {
    return deployment;
  }

  // ============================================================
  // IMPORTANT: DO NOT USE THESE GETTERS WITHOUT APPROVAL
  // ============================================================
  // These dedicated getter functions are provided for future use.
  // They are NOT deployed in the current test factory contract.
  // DO NOT call these from scripts until explicitly approved
  // by the developer. Use the temporary helper contract approach
  // in DeploySubDao.s.sol instead.
  // ============================================================

  function getIVotesAdapter() public view returns (address) {
    return address(deployment.veSystem.ivotesAdapter);
  }

  function getTokenVotingPluginRepo() public view returns (address) {
    return address(deployment.tokenVotingPluginRepo);
  }

  function getProposerHatId() public view returns (uint256) {
    return parameters.tokenVotingHats.proposerHatId;
  }

  function getVoterHatId() public view returns (uint256) {
    return parameters.tokenVotingHats.voterHatId;
  }

  function getExecutorHatId() public view returns (uint256) {
    return parameters.tokenVotingHats.executorHatId;
  }

  function getPluginRepoRelease() public view returns (uint8) {
    return parameters.pluginRepoRelease;
  }

  function getPluginRepoBuild() public view returns (uint16) {
    return parameters.pluginRepoBuild;
  }

  function getTokenVotingSetup() public view returns (TokenVotingSetupHats) {
    return parameters.tokenVotingSetup;
  }

  function getDao() public view returns (address) {
    return address(deployment.dao);
  }
}
"
    },
    "lib/osx/packages/contracts/src/core/dao/DAO.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

import {ERC165StorageUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165StorageUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IERC721ReceiverUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
import {IERC1155Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import {IERC1155ReceiverUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol";
import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";

import {IProtocolVersion} from "@aragon/osx-commons-contracts/src/utils/versioning/IProtocolVersion.sol";
import {ProtocolVersion} from "@aragon/osx-commons-contracts/src/utils/versioning/ProtocolVersion.sol";
import {VersionComparisonLib} from "@aragon/osx-commons-contracts/src/utils/versioning/VersionComparisonLib.sol";
import {hasBit, flipBit} from "@aragon/osx-commons-contracts/src/utils/math/BitMap.sol";
import {Action} from "@aragon/osx-commons-contracts/src/executors/Executor.sol";
import {IExecutor} from "@aragon/osx-commons-contracts/src/executors/IExecutor.sol";
import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";

import {PermissionManager} from "../permission/PermissionManager.sol";
import {CallbackHandler} from "../utils/CallbackHandler.sol";
import {IEIP4824} from "./IEIP4824.sol";

/// @title DAO
/// @author Aragon X - 2021-2024
/// @notice This contract is the entry point to the Aragon DAO framework and provides our users a simple and easy to use public interface.
/// @dev Public API of the Aragon DAO framework.
/// @custom:security-contact sirt@aragon.org
contract DAO is
    IEIP4824,
    Initializable,
    IERC1271,
    ERC165StorageUpgradeable,
    IDAO,
    IExecutor,
    UUPSUpgradeable,
    ProtocolVersion,
    PermissionManager,
    CallbackHandler
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using VersionComparisonLib for uint8[3];

    /// @notice The ID of the permission required to call the `execute` function.
    bytes32 public constant EXECUTE_PERMISSION_ID = keccak256("EXECUTE_PERMISSION");

    /// @notice The ID of the permission required to call the `_authorizeUpgrade` function.
    bytes32 public constant UPGRADE_DAO_PERMISSION_ID = keccak256("UPGRADE_DAO_PERMISSION");

    /// @notice The ID of the permission required to call the `setMetadata` function.
    bytes32 public constant SET_METADATA_PERMISSION_ID = keccak256("SET_METADATA_PERMISSION");

    /// @notice The ID of the permission required to call the `setTrustedForwarder` function.
    bytes32 public constant SET_TRUSTED_FORWARDER_PERMISSION_ID =
        keccak256("SET_TRUSTED_FORWARDER_PERMISSION");

    /// @notice The ID of the permission required to call the `registerStandardCallback` function.
    bytes32 public constant REGISTER_STANDARD_CALLBACK_PERMISSION_ID =
        keccak256("REGISTER_STANDARD_CALLBACK_PERMISSION");

    /// @notice The ID of the permission required to validate [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) signatures.
    bytes32 public constant VALIDATE_SIGNATURE_PERMISSION_ID =
        keccak256("VALIDATE_SIGNATURE_PERMISSION");

    /// @notice The internal constant storing the maximal action array length.
    uint256 internal constant MAX_ACTIONS = 256;

    /// @notice The first out of two values to which the `_reentrancyStatus` state variable (used by the `nonReentrant` modifier) can be set indicating that a function was not entered.
    uint256 private constant _NOT_ENTERED = 1;

    /// @notice The second out of two values to which the `_reentrancyStatus` state variable (used by the `nonReentrant` modifier) can be set indicating that a function was entered.
    uint256 private constant _ENTERED = 2;

    /// @notice Removed variable that is left here to maintain the storage layout.
    /// @dev Introduced in v1.0.0. Removed in v1.4.0.
    /// @custom:oz-renamed-from signatureValidator
    address private __removed0;

    /// @notice The address of the trusted forwarder verifying meta transactions.
    /// @dev Added in v1.0.0.
    address private trustedForwarder;

    /// @notice The [EIP-4824](https://eips.ethereum.org/EIPS/eip-4824) DAO URI.
    /// @dev Added in v1.0.0.
    string private _daoURI;

    /// @notice The state variable for the reentrancy guard of the `execute` function.
    /// @dev Added in v1.3.0. The variable can be of value `_NOT_ENTERED = 1` or `_ENTERED = 2` in usage and is initialized with `_NOT_ENTERED`.
    uint256 private _reentrancyStatus;

    /// @notice Thrown if a call is reentrant.
    error ReentrantCall();

    /// @notice Thrown if the action array length is larger than `MAX_ACTIONS`.
    error TooManyActions();

    /// @notice Thrown if action execution has failed.
    /// @param index The index of the action in the action array that failed.
    error ActionFailed(uint256 index);

    /// @notice Thrown if an action has insufficient gas left.
    error InsufficientGas();

    /// @notice Thrown if the deposit amount is zero.
    error ZeroAmount();

    /// @notice Thrown if there is a mismatch between the expected and actually deposited amount of native tokens.
    /// @param expected The expected native token amount.
    /// @param actual The actual native token amount deposited.
    error NativeTokenDepositAmountMismatch(uint256 expected, uint256 actual);

    /// @notice Thrown if an upgrade is not supported from a specific protocol version .
    error ProtocolVersionUpgradeNotSupported(uint8[3] protocolVersion);

    /// @notice Thrown when a function is removed but left to not corrupt the interface ID.
    error FunctionRemoved();

    /// @notice Thrown when initialize is called after it has already been executed.
    error AlreadyInitialized();

    /// @notice Emitted when a new DAO URI is set.
    /// @param daoURI The new URI.
    event NewURI(string daoURI);

    /// @notice A modifier to protect a function from calling itself, directly or indirectly (reentrancy).
    /// @dev Currently, this modifier is only applied to the `execute()` function. If this is used multiple times, private `_beforeNonReentrant()` and `_afterNonReentrant()` functions should be created to prevent code duplication.
    modifier nonReentrant() {
        if (_reentrancyStatus == _ENTERED) {
            revert ReentrantCall();
        }
        _reentrancyStatus = _ENTERED;

        _;

        _reentrancyStatus = _NOT_ENTERED;
    }

    /// @notice This ensures that the initialize function cannot be called during the upgrade process.
    modifier onlyCallAtInitialization() {
        if (_getInitializedVersion() != 0) {
            revert AlreadyInitialized();
        }

        _;
    }

    /// @notice Disables the initializers on the implementation contract to prevent it from being left uninitialized.
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /// @notice Initializes the DAO by
    /// - setting the reentrancy status variable to `_NOT_ENTERED`
    /// - registering the [ERC-165](https://eips.ethereum.org/EIPS/eip-165) interface ID
    /// - setting the trusted forwarder for meta transactions
    /// - giving the `ROOT_PERMISSION_ID` permission to the initial owner (that should be revoked and transferred to the DAO after setup).
    /// @dev This method is required to support [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822).
    /// @param _metadata IPFS hash that points to all the metadata (logo, description, tags, etc.) of a DAO.
    /// @param _initialOwner The initial owner of the DAO having the `ROOT_PERMISSION_ID` permission.
    /// @param _trustedForwarder The trusted forwarder responsible for verifying meta transactions.
    /// @param daoURI_ The DAO URI required to support [ERC-4824](https://eips.ethereum.org/EIPS/eip-4824).
    function initialize(
        bytes calldata _metadata,
        address _initialOwner,
        address _trustedForwarder,
        string calldata daoURI_
    ) external onlyCallAtInitialization reinitializer(3) {
        _reentrancyStatus = _NOT_ENTERED; // added in v1.3.0

        // In addition to the current interfaceId, also support previous version of the interfaceId.
        _registerInterface(type(IDAO).interfaceId ^ IExecutor.execute.selector);

        _registerInterface(type(IDAO).interfaceId);
        _registerInterface(type(IExecutor).interfaceId);
        _registerInterface(type(IERC1271).interfaceId);
        _registerInterface(type(IEIP4824).interfaceId);
        _registerInterface(type(IProtocolVersion).interfaceId); // added in v1.3.0
        _registerTokenInterfaces();

        _setMetadata(_metadata);
        _setTrustedForwarder(_trustedForwarder);
        _setDaoURI(daoURI_);
        __PermissionManager_init(_initialOwner);
    }

    /// @notice Initializes the DAO after an upgrade from a previous protocol version.
    /// @param _previousProtocolVersion The semantic protocol version number of the previous DAO implementation contract this upgrade is transitioning from.
    /// @param _initData The initialization data to be passed to via `upgradeToAndCall` (see [ERC-1967](https://docs.openzeppelin.com/contracts/4.x/api/proxy#ERC1967Upgrade)).
    function initializeFrom(
        uint8[3] calldata _previousProtocolVersion,
        bytes calldata _initData
    ) external reinitializer(3) {
        _initData; // Silences the unused function parameter warning.

        // Check that the contract is not upgrading from a different major release.
        if (_previousProtocolVersion[0] != 1) {
            revert ProtocolVersionUpgradeNotSupported(_previousProtocolVersion);
        }

        // Initialize `_reentrancyStatus` that was added in v1.3.0.
        // Register Interface `ProtocolVersion` that was added in v1.3.0.
        if (_previousProtocolVersion.lt([1, 3, 0])) {
            _reentrancyStatus = _NOT_ENTERED;
            _registerInterface(type(IProtocolVersion).interfaceId);
        }

        // Revoke the `SET_SIGNATURE_VALIDATOR_PERMISSION` that was deprecated in v1.4.0.
        if (_previousProtocolVersion.lt([1, 4, 0])) {
            _revoke({
                _where: address(this),
                _who: address(this),
                _permissionId: keccak256("SET_SIGNATURE_VALIDATOR_PERMISSION")
            });

            _registerInterface(type(IDAO).interfaceId);
            _registerInterface(type(IExecutor).interfaceId);
        }
    }

    /// @inheritdoc PermissionManager
    function isPermissionRestrictedForAnyAddr(
        bytes32 _permissionId
    ) internal pure override returns (bool) {
        return
            _permissionId == EXECUTE_PERMISSION_ID ||
            _permissionId == UPGRADE_DAO_PERMISSION_ID ||
            _permissionId == SET_METADATA_PERMISSION_ID ||
            _permissionId == SET_TRUSTED_FORWARDER_PERMISSION_ID ||
            _permissionId == REGISTER_STANDARD_CALLBACK_PERMISSION_ID;
    }

    /// @notice Internal method authorizing the upgrade of the contract via the [upgradeability mechanism for UUPS proxies](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable) (see [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822)).
    /// @dev The caller must have the `UPGRADE_DAO_PERMISSION_ID` permission.
    function _authorizeUpgrade(address) internal virtual override auth(UPGRADE_DAO_PERMISSION_ID) {}

    /// @inheritdoc IDAO
    function setTrustedForwarder(
        address _newTrustedForwarder
    ) external override auth(SET_TRUSTED_FORWARDER_PERMISSION_ID) {
        _setTrustedForwarder(_newTrustedForwarder);
    }

    /// @inheritdoc IDAO
    function getTrustedForwarder() external view virtual override returns (address) {
        return trustedForwarder;
    }

    /// @inheritdoc IDAO
    function hasPermission(
        address _where,
        address _who,
        bytes32 _permissionId,
        bytes memory _data
    ) external view override returns (bool) {
        return isGranted({_where: _where, _who: _who, _permissionId: _permissionId, _data: _data});
    }

    /// @inheritdoc IDAO
    function setMetadata(
        bytes calldata _metadata
    ) external override auth(SET_METADATA_PERMISSION_ID) {
        _setMetadata(_metadata);
    }

    /// @inheritdoc IExecutor
    function execute(
        bytes32 _callId,
        Action[] calldata _actions,
        uint256 _allowFailureMap
    )
        external
        override
        nonReentrant
        auth(EXECUTE_PERMISSION_ID)
        returns (bytes[] memory execResults, uint256 failureMap)
    {
        // Check that the action array length is within bounds.
        if (_actions.length > MAX_ACTIONS) {
            revert TooManyActions();
        }

        execResults = new bytes[](_actions.length);

        uint256 gasBefore;
        uint256 gasAfter;

        for (uint256 i = 0; i < _actions.length; ) {
            gasBefore = gasleft();

            (bool success, bytes memory result) = _actions[i].to.call{value: _actions[i].value}(
                _actions[i].data
            );
            gasAfter = gasleft();

            // Check if failure is allowed
            if (!hasBit(_allowFailureMap, uint8(i))) {
                // Check if the call failed.
                if (!success) {
                    revert ActionFailed(i);
                }
            } else {
                // Check if the call failed.
                if (!success) {
                    // Make sure that the action call did not fail because 63/64 of `gasleft()` was insufficient to execute the external call `.to.call` (see [ERC-150](https://eips.ethereum.org/EIPS/eip-150)).
                    // In specific scenarios, i.e. proposal execution where the last action in the action array is allowed to fail, the account calling `execute` could force-fail this action by setting a gas limit
                    // where 63/64 is insufficient causing the `.to.call` to fail, but where the remaining 1/64 gas are sufficient to successfully finish the `execute` call.
                    if (gasAfter < gasBefore / 64) {
                        revert InsufficientGas();
                    }

                    // Store that this action failed.
                    failureMap = flipBit(failureMap, uint8(i));
                }
            }

            execResults[i] = result;

            unchecked {
                ++i;
            }
        }

        emit Executed({
            actor: msg.sender,
            callId: _callId,
            actions: _actions,
            allowFailureMap: _allowFailureMap,
            failureMap: failureMap,
            execResults: execResults
        });
    }

    /// @inheritdoc IDAO
    function deposit(
        address _token,
        uint256 _amount,
        string calldata _reference
    ) external payable override {
        if (_amount == 0) revert ZeroAmount();

        if (_token == address(0)) {
            if (msg.value != _amount)
                revert NativeTokenDepositAmountMismatch({expected: _amount, actual: msg.value});
        } else {
            if (msg.value != 0)
                revert NativeTokenDepositAmountMismatch({expected: 0, actual: msg.value});

            IERC20Upgradeable(_token).safeTransferFrom(msg.sender, address(this), _amount);
        }

        emit Deposited(msg.sender, _token, _amount, _reference);
    }

    /// @inheritdoc IDAO
    function setSignatureValidator(address) external pure override {
        revert FunctionRemoved();
    }

    /// @inheritdoc IDAO
    /// @dev Relays the validation logic determining who is allowed to sign on behalf of the DAO to its permission manager.
    /// Caller specific bypassing can be set direct granting (i.e., `grant({_where: dao, _who: specificErc1271Caller, _permissionId: VALIDATE_SIGNATURE_PERMISSION_ID})`).
    /// Caller specific signature validation logic can be set by granting with a `PermissionCondition` (i.e., `grantWithCondition({_where: dao, _who: specificErc1271Caller, _permissionId: VALIDATE_SIGNATURE_PERMISSION_ID, _condition: yourConditionImplementation})`)
    /// Generic signature validation logic can be set for all calling contracts by granting with a `PermissionCondition` to `PermissionManager.ANY_ADDR()` (i.e., `grantWithCondition({_where: dao, _who: PermissionManager.ANY_ADDR(), _permissionId: VALIDATE_SIGNATURE_PERMISSION_ID, _condition: yourConditionImplementation})`).
    function isValidSignature(
        bytes32 _hash,
        bytes memory _signature
    ) external view override(IDAO, IERC1271) returns (bytes4) {
        if (
            isGranted({
                _where: address(this),
                _who: msg.sender,
                _permissionId: VALIDATE_SIGNATURE_PERMISSION_ID,
                _data: abi.encode(_hash, _signature)
            })
        ) {
            return 0x1626ba7e; // `type(IERC1271).interfaceId` = bytes4(keccak256("isValidSignature(bytes32,bytes)")`
        }
        return 0xffffffff; // `bytes4(uint32(type(uint32).max-1))`
    }

    /// @notice Emits the `NativeTokenDeposited` event to track native token deposits that weren't made via the deposit method.
    /// @dev This call is bound by the gas limitations for `send`/`transfer` calls introduced by [ERC-2929](https://eips.ethereum.org/EIPS/eip-2929).
    /// Gas cost increases in future hard forks might break this function. As an alternative, [ERC-2930](https://eips.ethereum.org/EIPS/eip-2930)-type transactions using access lists can be employed.
    receive() external payable {
        emit NativeTokenDeposited(msg.sender, msg.value);
    }

    /// @notice Fallback to handle future versions of the [ERC-165](https://eips.ethereum.org/EIPS/eip-165) standard.
    /// @param _input An alias being equivalent to `msg.data`. This feature of the fallback function was introduced with the [solidity compiler version 0.7.6](https://github.com/ethereum/solidity/releases/tag/v0.7.6)
    /// @return The magic number registered for the function selector triggering the fallback.
    fallback(bytes calldata _input) external returns (bytes memory) {
        bytes4 magicNumber = _handleCallback(msg.sig, _input);
        return abi.encode(magicNumber);
    }

    /// @notice Emits the MetadataSet event if new metadata is set.
    /// @param _metadata Hash of the IPFS metadata object.
    function _setMetadata(bytes calldata _metadata) internal {
        emit MetadataSet(_metadata);
    }

    /// @notice Sets the trusted forwarder on the DAO and emits the associated event.
    /// @param _trustedForwarder The trusted forwarder address.
    function _setTrustedForwarder(address _trustedForwarder) internal {
        trustedForwarder = _trustedForwarder;

        emit TrustedForwarderSet(_trustedForwarder);
    }

    /// @notice Registers the [ERC-721](https://eips.ethereum.org/EIPS/eip-721) and [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155) interfaces and callbacks.
    function _registerTokenInterfaces() private {
        _registerInterface(type(IERC721ReceiverUpgradeable).interfaceId);
        _registerInterface(type(IERC1155ReceiverUpgradeable).interfaceId);

        _registerCallback(
            IERC721ReceiverUpgradeable.onERC721Received.selector,
            IERC721ReceiverUpgradeable.onERC721Received.selector
        );
        _registerCallback(
            IERC1155ReceiverUpgradeable.onERC1155Received.selector,
            IERC1155ReceiverUpgradeable.onERC1155Received.selector
        );
        _registerCallback(
            IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector,
            IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector
        );
    }

    /// @inheritdoc IDAO
    function registerStandardCallback(
        bytes4 _interfaceId,
        bytes4 _callbackSelector,
        bytes4 _magicNumber
    ) external override auth(REGISTER_STANDARD_CALLBACK_PERMISSION_ID) {
        _registerInterface(_interfaceId);
        _registerCallback(_callbackSelector, _magicNumber);
        emit StandardCallbackRegistered(_interfaceId, _callbackSelector, _magicNumber);
    }

    /// @inheritdoc IEIP4824
    function daoURI() external view returns (string memory) {
        return _daoURI;
    }

    /// @notice Updates the set DAO URI to a new value.
    /// @param newDaoURI The new DAO URI to be set.
    function setDaoURI(string calldata newDaoURI) external auth(SET_METADATA_PERMISSION_ID) {
        _setDaoURI(newDaoURI);
    }

    /// @notice Sets the new [ERC-4824](https://eips.ethereum.org/EIPS/eip-4824) DAO URI and emits the associated event.
    /// @param daoURI_ The new DAO URI.
    function _setDaoURI(string calldata daoURI_) internal {
        _daoURI = daoURI_;

        emit NewURI(daoURI_);
    }

    /// @notice This empty reserved space is put in place to allow future versions to add new variables without shifting down storage in the inheritance chain (see [OpenZeppelin's guide about storage gaps](https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps)).
    uint256[46] private __gap;
}
"
    },
    "lib/osx/packages/contracts/src/framework/dao/DAOFactory.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {IProtocolVersion} from "@aragon/osx-commons-contracts/src/utils/versioning/IProtocolVersion.sol";
import {ProtocolVersion} from "@aragon/osx-commons-contracts/src/utils/versioning/ProtocolVersion.sol";
import {IPluginSetup} from "@aragon/osx-commons-contracts/src/plugin/setup/IPluginSetup.sol";
import {PermissionLib} from "@aragon/osx-commons-contracts/src/permission/PermissionLib.sol";
import {ProxyLib} from "@aragon/osx-commons-contracts/src/utils/deployment/ProxyLib.sol";

import {DAO} from "../../core/dao/DAO.sol";
import {PluginRepo} from "../plugin/repo/PluginRepo.sol";
import {PluginSetupProcessor} from "../plugin/setup/PluginSetupProcessor.sol";
import {hashHelpers, PluginSetupRef} from "../plugin/setup/PluginSetupProcessorHelpers.sol";
import {DAORegistry} from "./DAORegistry.sol";

/// @title DAOFactory
/// @author Aragon X - 2022-2023
/// @notice This contract is used to create a DAO.
/// @custom:security-contact sirt@aragon.org
contract DAOFactory is ERC165, ProtocolVersion {
    using ProxyLib for address;

    /// @notice The DAO base contract, to be used for creating new `DAO`s via `createERC1967Proxy` function.
    address public immutable daoBase;

    /// @notice The DAO registry listing the `DAO` contracts created via this contract.
    DAORegistry public immutable daoRegistry;

    /// @notice The plugin setup processor for installing plugins on the newly created `DAO`s.
    PluginSetupProcessor public immutable pluginSetupProcessor;

    // Cache permission IDs for optimized access
    bytes32 internal immutable ROOT_PERMISSION_ID;
    bytes32 internal immutable UPGRADE_DAO_PERMISSION_ID;
    bytes32 internal immutable SET_TRUSTED_FORWARDER_PERMISSION_ID;
    bytes32 internal immutable SET_METADATA_PERMISSION_ID;
    bytes32 internal immutable REGISTER_STANDARD_CALLBACK_PERMISSION_ID;
    bytes32 internal immutable EXECUTE_PERMISSION_ID;
    bytes32 internal immutable APPLY_INSTALLATION_PERMISSION_ID;

    /// @notice The container for the DAO settings to be set during the DAO initialization.
    /// @param trustedForwarder The address of the trusted forwarder required for meta transactions.
    /// @param daoURI The DAO uri used with [EIP-4824](https://eips.ethereum.org/EIPS/eip-4824).
    /// @param subdomain The ENS subdomain to be registered for the DAO contract.
    /// @param metadata The metadata of the DAO.
    struct DAOSettings {
        address trustedForwarder;
        string daoURI;
        string subdomain;
        bytes metadata;
    }

    /// @notice The container with the information required to install a plugin on the DAO.
    /// @param pluginSetupRef The `PluginSetupRepo` address of the plugin and the version tag.
    /// @param data The bytes-encoded data containing the input parameters for the installation as specified in the plugin's build metadata JSON file.
    struct PluginSettings {
        PluginSetupRef pluginSetupRef;
        bytes data;
    }

    /// @notice The container with the information about an installed plugin on a DAO.
    /// @param plugin The address of the deployed plugin instance.
    /// @param preparedSetupData The applied preparedSetupData which contains arrays of helpers and permissions.
    struct InstalledPlugin {
        address plugin;
        IPluginSetup.PreparedSetupData preparedSetupData;
    }

    /// @notice Thrown if `PluginSettings` array is empty, and no plugin is provided.
    error NoPluginProvided();

    /// @notice The constructor setting the registry and plugin setup processor and creating the base contracts for the factory.
    /// @param _registry The DAO registry to register the DAO by its name.
    /// @param _pluginSetupProcessor The address of PluginSetupProcessor.
    constructor(DAORegistry _registry, PluginSetupProcessor _pluginSetupProcessor) {
        daoRegistry = _registry;
        pluginSetupProcessor = _pluginSetupProcessor;

        DAO dao = new DAO();
        daoBase = address(dao);

        // Cache permission IDs for reduced gas usage in future function calls
        ROOT_PERMISSION_ID = dao.ROOT_PERMISSION_ID();
        UPGRADE_DAO_PERMISSION_ID = dao.UPGRADE_DAO_PERMISSION_ID();
        SET_TRUSTED_FORWARDER_PERMISSION_ID = dao.SET_TRUSTED_FORWARDER_PERMISSION_ID();
        SET_METADATA_PERMISSION_ID = dao.SET_METADATA_PERMISSION_ID();
        REGISTER_STANDARD_CALLBACK_PERMISSION_ID = dao.REGISTER_STANDARD_CALLBACK_PERMISSION_ID();
        EXECUTE_PERMISSION_ID = dao.EXECUTE_PERMISSION_ID();
        APPLY_INSTALLATION_PERMISSION_ID = pluginSetupProcessor.APPLY_INSTALLATION_PERMISSION_ID();
    }

    /// @notice Checks if this or the parent contract supports an interface by its ID.
    /// @param _interfaceId The ID of the interface.
    /// @return Returns `true` if the interface is supported.
    function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool) {
        return
            _interfaceId == type(IProtocolVersion).interfaceId ||
            super.supportsInterface(_interfaceId);
    }

    /// @notice Creates a new DAO, registers it in the DAO registry, and optionally installs plugins via the plugin setup processor.
    /// @dev If `_pluginSettings` is empty, the caller is granted `EXECUTE_PERMISSION` on the DAO.
    /// @param _daoSettings The settings to configure during DAO initialization.
    /// @param _pluginSettings An array containing plugin references and settings. If provided, each plugin is installed
    /// after the DAO creation.
    /// @return createdDao The address of the newly created DAO instance.
    /// @return installedPlugins An array of `InstalledPlugin` structs, each containing the plugin address and associated
    /// helper contracts and permissions, if plugins were installed; otherwise, an empty array.
    function createDao(
        DAOSettings calldata _daoSettings,
        PluginSettings[] calldata _pluginSettings
    ) external returns (DAO createdDao, InstalledPlugin[] memory installedPlugins) {
        // Create DAO.
        createdDao = _createDAO(_daoSettings);

        // Register DAO.
        daoRegistry.register(createdDao, msg.sender, _daoSettings.subdomain);

        // Cache address to save gas
        address daoAddress = address(createdDao);

        // Install plugins if plugin settings are provided.
        if (_pluginSettings.length != 0) {
            installedPlugins = new InstalledPlugin[](_pluginSettings.length);

            // Cache address to save gas
            address pluginSetupProcessorAddress = address(pluginSetupProcessor);

            // Grant the temporary permissions.
            // Grant Temporarily `ROOT_PERMISSION` to `pluginSetupProcessor`.
            createdDao.grant(daoAddress, pluginSetupProcessorAddress, ROOT_PERMISSION_ID);

            // Grant Temporarily `APPLY_INSTALLATION_PERMISSION` on `pluginSetupProcessor` to this `DAOFactory`.
            createdDao.grant(
                pluginSetupProcessorAddress,
                address(this),
                APPLY_INSTALLATION_PERMISSION_ID
            );

            // Install plugins on the newly created DAO.
            for (uint256 i; i < _pluginSettings.length; ++i) {
                // Prepare plugin.
                (
                    address plugin,
                    IPluginSetup.PreparedSetupData memory preparedSetupData
                ) = pluginSetupProcessor.prepareInstallation(
                        daoAddress,
                        PluginSetupProcessor.PrepareInstallationParams(
                            _pluginSettings[i].pluginSetupRef,
                            _pluginSettings[i].data
                        )
                    );

                // Apply plugin.
                pluginSetupProcessor.applyInstallation(
                    daoAddress,
                    PluginSetupProcessor.ApplyInstallationParams(
                        _pluginSettings[i].pluginSetupRef,
                        plugin,
                        preparedSetupData.permissions,
                        hashHelpers(preparedSetupData.helpers)
                    )
                );

                installedPlugins[i] = InstalledPlugin(plugin, preparedSetupData);
            }

            // Revoke the temporarily granted permissions.
            // Revoke Temporarily `ROOT_PERMISSION` from `pluginSetupProcessor`.
            createdDao.revoke(daoAddress, pluginSetupProcessorAddress, ROOT_PERMISSION_ID);

            // Revoke `APPLY_INSTALLATION_PERMISSION` on `pluginSetupProcessor` from this `DAOFactory` .
            createdDao.revoke(
                pluginSetupProcessorAddress,
                address(this),
                APPLY_INSTALLATION_PERMISSION_ID
            );
        } else {
            // if no plugin setting is provided, grant EXECUTE_PERMISSION_ID to msg.sender
            createdDao.grant(daoAddress, msg.sender, EXECUTE_PERMISSION_ID);
        }

        // Set the rest of DAO's permissions.
        _setDAOPermissions(daoAddress);

        // Revoke Temporarily `ROOT_PERMISSION_ID` that implicitly granted to this `DaoFactory`
        // at the create dao step `address(this)` being the initial owner of the new created DAO.
        createdDao.revoke(daoAddress, address(this), ROOT_PERMISSION_ID);

        return (createdDao, installedPlugins);
    }

    /// @notice Deploys a new DAO `ERC1967` proxy, and initialize it with this contract as the initial owner.
    /// @param _daoSettings The trusted forwarder, name and metadata hash of the DAO it creates.
    function _createDAO(DAOSettings calldata _daoSettings) internal returns (DAO dao) {
        // Create a DAO proxy and initialize it with the DAOFactory (`address(this)`) as the initial owner.
        // As a result, the DAOFactory has `ROOT_PERMISSION_`ID` permission on the DAO.
        dao = DAO(
            payable(
                daoBase.deployUUPSProxy(
                    abi.encodeCall(
                        DAO.initialize,
                        (
                            _daoSettings.metadata,
                            address(this),
                            _daoSettings.trustedForwarder,
                            _daoSettings.daoURI
                        )
                    )
                )
            )
        );
    }

    /// @notice Sets the required permissions for the new DAO.
    /// @param _daoAddress The address of the DAO just created.
    function _setDAOPermissions(address _daoAddress) internal {
        // set permissionIds on the dao itself.
        PermissionLib.SingleTargetPermission[]
            memory items = new PermissionLib.SingleTargetPermission[](5);

        // Grant DAO all the permissions required
        items[0] = PermissionLib.SingleTargetPermission(
            PermissionLib.Operation.Grant,
            _daoAddress,
            ROOT_PERMISSION_ID
        );
        items[1] = PermissionLib.SingleTargetPermission(
            PermissionLib.Operation.Grant,
            _daoAddress,
            UPGRADE_DAO_PERMISSION_ID
        );
        items[2] = PermissionLib.SingleTargetPermission(
            PermissionLib.Operation.Grant,
            _daoAddress,
            SET_TRUSTED_FORWARDER_PERMISSION_ID
        );
        items[3] = PermissionLib.SingleTargetPermission(
            PermissionLib.Operation.Grant,
            _daoAddress,
            SET_METADATA_PERMISSION_ID
        );
        items[4] = PermissionLib.SingleTargetPermission(
            PermissionLib.Operation.Grant,
            _daoAddress,
            REGISTER_STANDARD_CALLBACK_PERMISSION_ID
        );

        DAO(payable(_daoAddress)).applySingleTargetPermissions(_daoAddress, items);
    }
}
"
    },
    "lib/osx/packages/contracts/src/framework/plugin/setup/PluginSetupProcessor.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";

import {ProtocolVersion} from "@aragon/osx-commons-contracts/src/utils/versioning/ProtocolVersion.sol";
import {IPluginSetup} from "@aragon/osx-commons-contracts/src/plugin/setup/IPluginSetup.sol";
import {PluginSetup} from "@aragon/osx-commons-contracts/src/plugin/setup/PluginSetup.sol";
import {DAO} from "../../../core/dao/DAO.sol";
import {PermissionLib} from "@aragon/osx-commons-contracts/src/permission/PermissionLib.sol";
import {PluginUUPSUpgradeable} from "@aragon/osx-commons-contracts/src/plugin/PluginUUPSUpgradeable.sol";
import {IPlugin} from "@aragon/osx-commons-contracts/src/plugin/IPlugin.sol";

import {PluginRepoRegistry} from "../repo/PluginRepoRegistry.sol";
import {PluginRepo} from "../repo/PluginRepo.sol";

import {PluginSetupRef, hashHelpers, hashPermissions, _getPreparedSetupId, _getAppliedSetupId, _getPluginInstallationId, PreparationType} from "./PluginSetupProcessorHelpers.sol";

/// @title PluginSetupProcessor
/// @author Aragon X - 2022-2023
/// @notice This contract processes the preparation and application of plugin setups (installation, update, uninstallation) on behalf of a requesting DAO.
/// @dev This contract is temporarily granted the `ROOT_PERMISSION_ID` permission on the applying DAO and therefore is highly security critical.
/// @custom:security-contact sirt@aragon.org
contract PluginSetupProcessor is ProtocolVersion {
    using ERC165Checker for address;

    /// @notice The ID of the permission required to call the `applyInstallation` function.
    bytes32 public constant APPLY_INSTALLATION_PERMISSION_ID =
        keccak256("APPLY_INSTALLATION_PERMISSION");

    /// @notice The ID of the permission required to call the `applyUpdate` function.
    bytes32 public constant APPLY_UPDATE_PERMISSION_ID = keccak256("APPLY_UPDATE_PERMISSION");

    /// @notice The ID of the permission required to call the `applyUninstallation` function.
    bytes32 public constant APPLY_UNINSTALLATION_PERMISSION_ID =
        keccak256("APPLY_UNINSTALLATION_PERMISSION");

    /// @notice The hash obtained from the bytes-encoded empty array to be used for UI updates being required to submit an empty permission array.
    /// @dev The hash is computed via `keccak256(abi.encode([]))`.
    bytes32 private constant EMPTY_ARRAY_HASH =
        0x569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd;

    /// @notice The hash obtained from the bytes-encoded zero value.
    /// @dev The hash is computed via `keccak256(abi.encode(0))`.
    bytes32 private constant ZERO_BYTES_HASH =
        0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563;

    /// @notice A struct containing information related to plugin setups that have been applied.
    /// @param blockNumber The block number at which the `applyInstallation`, `applyUpdate` or `applyUninstallation` was executed.
    /// @param currentAppliedSetupId The current setup id that plugin holds. Needed to confirm that `prepareUpdate` or `prepareUninstallation` happens for the plugin's current/valid dependencies.
    /// @param preparedSetupIdToBlockNumber The mapping between prepared setup IDs and block numbers at which `prepareInstallation`, `prepareUpdate` or `prepareUninstallation` was executed.
    struct PluginState {
        uint256 blockNumber;
        bytes32 currentAppliedSetupId;
        mapping(bytes32 => uint256) preparedSetupIdToBlockNumber;
    }

    /// @notice A mapping between the plugin installation ID (obtained from the DAO and plugin address) and the plugin state information.
    /// @dev This variable is public on purpose to allow future versions to access and migrate the storage.
    mapping(bytes32 => PluginState) public states;

    /// @notice The struct containing the parameters for the `prepareInstallation` function.
    /// @param pluginSetupRef The reference to the plugin setup to be used for the installation.
    /// @param data The bytes-encoded data containing the input parameters for the installation preparation as specified in the corresponding ABI on the version's metadata.
    struct PrepareInstallationParams {
        PluginSetupRef pluginSetupRef;
        bytes data;
    }

    /// @notice The struct containing the parameters for the `applyInstallation` function.
    /// @param pluginSetupRef The reference to the plugin setup used for the installation.
    /// @param plugin The address of the plugin contract to be installed.
    /// @param permissions The array of multi-targeted permission operations to be applied by the `PluginSetupProcessor` to the DAO.
    /// @param helpersHash The hash of helpers that were deployed in `prepareInstallation`. This helps to derive the setup ID.
    struct ApplyInstallationParams {
        PluginSetupRef pluginSetupRef;
        address plugin;
        PermissionLib.MultiTargetPermission[] permissions;
        bytes32 helpersHash;
    }

    /// @notice The struct containing the parameters for the `prepareUpdate` function.
    /// @param currentVersionTag The tag of the current plugin version to update from.
    /// @param newVersionTag The tag of the new plugin version to update to.
    /// @param pluginSetupRepo The plugin setup repository address on which the plugin exists.
    /// @param setupPayload The payload containing the plugin and helper contract addresses deployed in a preparation step as well as optional data to be consumed by the plugin setup.
    ///  This includes the bytes-encoded data containing the input parameters for the update preparation as specified in the corresponding ABI on the version's metadata.
    struct PrepareUpdateParams {
        PluginRepo.Tag currentVersionTag;
        PluginRepo.Tag newVersionTag;
        PluginRepo pluginSetupRepo;
        IPluginSetup.SetupPayload setupPayload;
    }

    /// @notice The struct containing the parameters for the `applyUpdate` function.
    /// @param plugin The address of the plugin contract to be updated.
    /// @param pluginSetupRef The reference to the plugin setup used for the update.
    /// @param initData The encoded data (function selector and arguments) to be provided to `upgradeToAndCall`.
    /// @param permissions The array of multi-targeted permission operations to be applied by the `PluginSetupProcessor` to the DAO.
    /// @param helpersHash The hash of helpers that were deployed in `prepareUpdate`. This helps to derive the setup ID.
    struct ApplyUpdateParams {
        address plugin;
        PluginSetupRef pluginSetupRef;
        bytes initData;
        PermissionLib.MultiTargetPermission[] permissions;
        bytes32 helpersHash;
    }

    /// @notice The struct containing the parameters for the `prepareUninstallation` function.
    /// @param pluginSetupRef The reference to the plugin setup to be used for the uninstallation.
    /// @param setupPayload The payload containing the plugin and helper contract addresses deployed in a preparation step as well as optional data to be consumed by the plugin setup.
    ///  This includes the bytes-encoded data containing the input parameters for the uninstallation preparation as specified in the corresponding ABI on the version's metadata.
    struct PrepareUninstallationParams {
        PluginSetupRef pluginSetupRef;
        IPluginSetup.SetupPayload setupPayload;
    }

    /// @notice The struct containing the parameters for the `applyInstallation` function.
    /// @param plugin The address of the plugin contract to be uninstalled.
    /// @param pluginSetupRef The reference to the plugin setup used for the uninstallation.
    /// @param permissions The array of multi-targeted permission operations to be applied by the `PluginSetupProcess.
    struct ApplyUninstallationParams {
        address plugin;
        PluginSetupRef pluginSetupRef;
        PermissionLib.MultiTargetPermission[] permissions;
    }

    /// @notice The plugin repo registry listing the `PluginRepo` contracts versioning the `PluginSetup` contracts.
    PluginRepoRegistry public repoRegistry;

    /// @notice Thrown if a setup is unauthorized and cannot be applied because of a missing permission of the associated DAO.
    /// @param dao The address of the DAO to which the plugin belongs.
    /// @param caller The address (EOA or contract) that requested the application of a setup on the associated DAO.
    /// @param permissionId The permission identifier.
    /// @dev This is thrown if the `APPLY_INSTALLATION_PERMISSION_ID`, `APPLY_UPDATE_PERMISSION_ID`, or APPLY_UNINSTALLATION_PERMISSION_ID is missing.
    error SetupApplicationUnauthorized(address dao, address caller, bytes32 permissionId);

    /// @notice Thrown if a plugin is not upgradeable.
    /// @param plugin The address of the plugin contract.
    error PluginNonupgradeable(address plugin);

    /// @notice Thrown if the upgrade of an `UUPSUpgradeable` proxy contract (see [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822)) failed.
    /// @param proxy The address of the proxy.
    /// @param implementation The address of the implementation contract.
    /// @param initData The initialization data to be passed to the upgradeable plugin contract via `upgradeToAndCall`.
    error PluginProxyUpgradeFailed(address proxy, address implementation, bytes initData);

    /// @notice Thrown if a contract does not support the `IPlugin` interface.
    /// @param plugin The address of the contract.
    error IPluginNotSupported(address plugin);

    /// @notice Thrown if a plugin repository does not exist on the plugin repo registry.
    error PluginRepoNonexistent();

    /// @notice Thrown if a plugin setup was already prepared indicated by the prepared setup ID.
    /// @param preparedSetupId The prepared setup ID.
    error SetupAlreadyPrepared(bytes32 preparedSetupId);

    /// @notice Thrown if a prepared setup ID is not eligible to be applied. This can happen if another setup has been already applied or if the setup wasn't prepared in the first place.
    /// @param preparedSetupId The prepared setup ID.
    error SetupNotApplicable(bytes32 preparedSetupId);

    /// @notice Thrown if the update version is invalid.
    /// @param currentVersionTag The tag of the current version to update from.
    /// @param newVersionTag The tag of the new version to update to.
    error InvalidUpdateVersion(PluginRepo.Tag currentVersionTag, PluginRepo.Tag newVersionTag);

    /// @notice Thrown if plugin is already installed and one tries to prepare or apply install on it.
    error PluginAlreadyInstalled();

    /// @notice Thrown if the applied setup ID resulting from the supplied setup payload does not match with the current applied setup ID.
    /// @param currentAppliedSetupId The current applied setup ID with which the data in the supplied payload must match.
    /// @param appliedSetupId The applied setup ID obtained from the data in the supplied setup payload.
    error InvalidAppliedSetupId(bytes32 currentAppliedSetupId, bytes32 appliedSetupId);

    /// @notice Emitted with a prepared plugin installation to store data relevant for the application step.
    /// @param sender The sender that prepared the plugin installation.
    /// @param dao The address of the DAO to which the plugin belongs.
    /// @param preparedSetupId The prepared setup ID obtained from the supplied data.
    /// @param pluginSetupRepo The repository storing the `PluginSetup` contracts of all versions of a plugin.
    /// @param versionTag The version tag of the plugin setup of the prepared installation.
    /// @param data The bytes-encoded data containing the input parameters for the preparation as specified in the corresponding ABI on the version's metadata.
    /// @param plugin The address of the plugin contract.
    /// @param preparedSetupData The deployed plugin's relevant data which consists of helpers and permissions.
    event InstallationPrepared(
        address indexed sender,
        address indexed dao,
        bytes32 preparedSetupId,
        PluginRepo indexed pluginSetupRepo,
        PluginRepo.Tag versionTag,
        bytes data,
        address plugin,
        IPluginSetup.PreparedSetupData preparedSetupData
    );

    /// @notice Emitted after a plugin installation was applied.
    /// @param dao The address of the DAO to which the plugin belongs.
    /// @param plugin The address of the plugin contract.
    /// @param preparedSetupId The prepared setup ID.
    /// @param appliedSetupId The applied setup ID.
    event InstallationApplied(
        address indexed dao,
        address indexed plugin,
        bytes32 preparedSetupId,
        bytes32 appliedSetupId
    );

    /// @notice Emitted with a prepared plugin update to store data relevant for the application step.
    /// @param sender The sender that prepared the plugin update.
    /// @param dao The address of the DAO to which the plugin belongs.
    /// @param preparedSetupId The prepared setup ID.
    /// @param pluginSetupRepo The repository storing the `PluginSetup` contracts of all versions of a plugin.
    /// @param versionTag The version tag of the plugin setup of the prepared update.
    /// @param setupPayload The payload containing the plugin and helper contract addresses deployed in a preparation step as well as optional data to be consumed by the plugin setup.
    /// @param preparedSetupData The deployed plugin's relevant data which consists of helpers and permissio

Tags:
ERC20, ERC721, ERC1155, ERC165, Multisig, Mintable, Burnable, Pausable, Non-Fungible, Swap, Yield, Voting, Upgradeable, Multi-Signature, Factory|addr:0xe1d75dcf943e61de662fbc8ed845a2e1e3a92422|verified:true|block:23749283|tx:0xa1dfcf0ccd2ffebe0adb7dff863c239f78cfd6caa05c4ebd049d99237bc29e23|first_check:1762545506

Submitted on: 2025-11-07 20:58:28

Comments

Log in to comment.

No comments yet.