TokenVotingSetupHats

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": {
    "lib/token-voting-plugin/src/TokenVotingSetupHats.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IVotesUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/utils/IVotesUpgradeable.sol";

import {GovernanceERC20} from "./erc20/GovernanceERC20.sol";
import {GovernanceWrappedERC20} from "./erc20/GovernanceWrappedERC20.sol";
import {TokenVoting, TokenVotingHats} from "./TokenVotingHats.sol";
import {HatsCondition} from "./condition/HatsCondition.sol";
import {MajorityVotingBase} from "./base/MajorityVotingBase.sol";

import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {PermissionLib} from "@aragon/osx-commons-contracts/src/permission/PermissionLib.sol";
import {IPlugin} from "@aragon/osx-commons-contracts/src/plugin/IPlugin.sol";
import {IPluginSetup} from "@aragon/osx-commons-contracts/src/plugin/setup/IPluginSetup.sol";
import {PluginUpgradeableSetup} from "@aragon/osx-commons-contracts/src/plugin/setup/PluginUpgradeableSetup.sol";

import {ProxyLib} from "@aragon/osx-commons-contracts/src/utils/deployment/ProxyLib.sol";

/// @title TokenVotingSetupHats
/// @notice Plugin setup contract for the Hats-gated TokenVoting variant.
contract TokenVotingSetupHats is PluginUpgradeableSetup {
    using Address for address;
    using Clones for address;
    using ERC165Checker for address;
    using ProxyLib for address;


    bytes32 private constant EXECUTE_PERMISSION_ID = keccak256("EXECUTE_PERMISSION");
    bytes32 private constant SET_TARGET_CONFIG_PERMISSION_ID = keccak256("SET_TARGET_CONFIG_PERMISSION");
    bytes32 private constant SET_METADATA_PERMISSION_ID = keccak256("SET_METADATA_PERMISSION");
    bytes32 private constant UPGRADE_PLUGIN_PERMISSION_ID = keccak256("UPGRADE_PLUGIN_PERMISSION");
    bytes32 private constant EXECUTE_PROPOSAL_PERMISSION_ID = keccak256("EXECUTE_PROPOSAL_PERMISSION");

    address private constant ANY_ADDR = address(type(uint160).max);

    /// @notice The Hats-aware TokenVoting implementation used for proxy deployments.
    TokenVotingHats private immutable tokenVotingHatsBase;

    /// @notice Address of the GovernanceERC20 implementation used for token cloning.
    address public immutable governanceERC20Base;

    /// @notice Address of the GovernanceWrappedERC20 implementation used for token wrapping.
    address public immutable governanceWrappedERC20Base;

    /// @notice Token configuration identical to the vanilla TokenVoting setup.
    struct TokenSettings {
        address addr;
        string name;
        string symbol;
    }

    /// @notice Hats-specific configuration.
    struct HatsConfig {
        uint256 proposerHatId;
        uint256 voterHatId;
        uint256 executorHatId;
    }

    /// @notice Aggregated installation parameters, extending the vanilla setup with Hats configuration.
    struct InstallationParameters {
        MajorityVotingBase.VotingSettings votingSettings;
        TokenSettings tokenSettings;
        GovernanceERC20.MintSettings mintSettings;
        IPlugin.TargetConfig targetConfig;
        uint256 minApprovals;
        bytes pluginMetadata;
        address[] excludedAccounts;
        HatsConfig hatsConfig;
    }

    /// @notice Thrown if the provided token address is not a contract.
    error TokenNotContract(address token);

    /// @notice Thrown if the provided token address does not behave like an ERC20.
    error TokenNotERC20(address token);

    constructor(GovernanceERC20 _governanceERC20Base, GovernanceWrappedERC20 _governanceWrappedERC20Base)
        PluginUpgradeableSetup(address(new TokenVotingHats()))
    {
        tokenVotingHatsBase = TokenVotingHats(IMPLEMENTATION);
        governanceERC20Base = address(_governanceERC20Base);
        governanceWrappedERC20Base = address(_governanceWrappedERC20Base);
    }

    /// @inheritdoc IPluginSetup
    function prepareInstallation(address _dao, bytes calldata _data)
        external
        override
        returns (address plugin, PreparedSetupData memory preparedSetupData)
    {
        InstallationParameters memory params = decodeInstallationParametersHats(_data);
        TokenSettings memory tokenSettings = params.tokenSettings;
        HatsConfig memory hatsConfig = params.hatsConfig;
        address token = tokenSettings.addr;

        if (token != address(0)) {
            if (!token.isContract()) {
                revert TokenNotContract(token);
            }

            if (!_isERC20(token)) {
                revert TokenNotERC20(token);
            }

            if (!supportsIVotesInterface(token)) {
                token = governanceWrappedERC20Base.clone();
                GovernanceWrappedERC20(token)
                    .initialize(IERC20Upgradeable(tokenSettings.addr), tokenSettings.name, tokenSettings.symbol);
            }
        } else {
            token = governanceERC20Base.clone();
            GovernanceERC20(token).initialize(IDAO(_dao), tokenSettings.name, tokenSettings.symbol, params.mintSettings);
        }

        plugin = address(tokenVotingHatsBase)
            .deployUUPSProxy(
                abi.encodeCall(
                    tokenVotingHatsBase.initialize,
                    (
                        IDAO(_dao),
                        params.votingSettings,
                        IVotesUpgradeable(token),
                        params.targetConfig,
                        params.minApprovals,
                        params.pluginMetadata,
                        params.excludedAccounts
                    )
                )
            );

        address hatsCondition =
            address(new HatsCondition(hatsConfig.proposerHatId, hatsConfig.voterHatId, hatsConfig.executorHatId));

        preparedSetupData.helpers = new address[](2);
        preparedSetupData.helpers[0] = hatsCondition;
        preparedSetupData.helpers[1] = token;

        uint256 permissionEntries = tokenSettings.addr != address(0) ? 7 : 8;
        preparedSetupData.permissions = new PermissionLib.MultiTargetPermission[](permissionEntries);

        // Grant update voting settings permission to the DAO.
        preparedSetupData.permissions[0] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Grant,
            where: plugin,
            who: _dao,
            condition: PermissionLib.NO_CONDITION,
            permissionId: tokenVotingHatsBase.UPDATE_VOTING_SETTINGS_PERMISSION_ID()
        });

        // Grant DAO execute permission to the plugin.
        preparedSetupData.permissions[1] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Grant,
            where: _dao,
            who: plugin,
            condition: PermissionLib.NO_CONDITION,
            permissionId: EXECUTE_PERMISSION_ID
        });

        // Gate proposal creation by the proposer hat.
        preparedSetupData.permissions[2] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.GrantWithCondition,
            where: plugin,
            who: ANY_ADDR,
            condition: hatsCondition,
            permissionId: tokenVotingHatsBase.CREATE_PROPOSAL_PERMISSION_ID()
        });

        // Gate vote casting by the voter hat.
        preparedSetupData.permissions[3] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.GrantWithCondition,
            where: plugin,
            who: ANY_ADDR,
            condition: hatsCondition,
            permissionId: tokenVotingHatsBase.CAST_VOTE_PERMISSION_ID()
        });

        // Allow the DAO to manage target config.
        preparedSetupData.permissions[4] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Grant,
            where: plugin,
            who: _dao,
            condition: PermissionLib.NO_CONDITION,
            permissionId: SET_TARGET_CONFIG_PERMISSION_ID
        });

        // Allow the DAO to update metadata.
        preparedSetupData.permissions[5] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Grant,
            where: plugin,
            who: _dao,
            condition: PermissionLib.NO_CONDITION,
            permissionId: SET_METADATA_PERMISSION_ID
        });

        // Gate proposal execution by the executor hat.
        preparedSetupData.permissions[6] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.GrantWithCondition,
            where: plugin,
            who: ANY_ADDR,
            condition: hatsCondition,
            permissionId: tokenVotingHatsBase.EXECUTE_PROPOSAL_PERMISSION_ID()
        });

        if (tokenSettings.addr == address(0)) {
            bytes32 tokenMintPermission = GovernanceERC20(token).MINT_PERMISSION_ID();
            preparedSetupData.permissions[7] = PermissionLib.MultiTargetPermission({
                operation: PermissionLib.Operation.Grant,
                where: token,
                who: _dao,
                condition: PermissionLib.NO_CONDITION,
                permissionId: tokenMintPermission
            });
        }
    }

    /// @inheritdoc IPluginSetup
    function prepareUpdate(address _dao, uint16 _fromBuild, SetupPayload calldata _payload)
        external
        pure
        override
        returns (
            bytes memory, /* initData */
            PreparedSetupData memory /* preparedSetupData */
        )
    {
        (_dao, _fromBuild, _payload);
        revert InvalidUpdatePath({fromBuild: _fromBuild, thisBuild: 1});
    }

    /// @inheritdoc IPluginSetup
    function prepareUninstallation(address _dao, SetupPayload calldata _payload)
        external
        view
        override
        returns (PermissionLib.MultiTargetPermission[] memory permissions)
    {
        // Check if MINT permission was granted to the DAO on the token
        address token = _payload.currentHelpers[1];
        bool hasMintPermission = _hasMintPermission(token, _dao);
        uint256 permissionCount = hasMintPermission ? 8 : 7;

        permissions = new PermissionLib.MultiTargetPermission[](permissionCount);

        permissions[0] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Revoke,
            where: _payload.plugin,
            who: _dao,
            condition: PermissionLib.NO_CONDITION,
            permissionId: tokenVotingHatsBase.UPDATE_VOTING_SETTINGS_PERMISSION_ID()
        });

        permissions[1] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Revoke,
            where: _dao,
            who: _payload.plugin,
            condition: PermissionLib.NO_CONDITION,
            permissionId: EXECUTE_PERMISSION_ID
        });

        permissions[2] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Revoke,
            where: _payload.plugin,
            who: _dao,
            condition: PermissionLib.NO_CONDITION,
            permissionId: SET_TARGET_CONFIG_PERMISSION_ID
        });

        permissions[3] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Revoke,
            where: _payload.plugin,
            who: _dao,
            condition: PermissionLib.NO_CONDITION,
            permissionId: SET_METADATA_PERMISSION_ID
        });

        permissions[4] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Revoke,
            where: _payload.plugin,
            who: ANY_ADDR,
            condition: PermissionLib.NO_CONDITION,
            permissionId: tokenVotingHatsBase.CAST_VOTE_PERMISSION_ID()
        });

        permissions[5] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Revoke,
            where: _payload.plugin,
            who: ANY_ADDR,
            condition: PermissionLib.NO_CONDITION,
            permissionId: tokenVotingHatsBase.CREATE_PROPOSAL_PERMISSION_ID()
        });

        permissions[6] = PermissionLib.MultiTargetPermission({
            operation: PermissionLib.Operation.Revoke,
            where: _payload.plugin,
            who: ANY_ADDR,
            condition: PermissionLib.NO_CONDITION,
            permissionId: tokenVotingHatsBase.EXECUTE_PROPOSAL_PERMISSION_ID()
        });

        // If MINT permission was granted during installation, revoke it
        if (hasMintPermission) {
            bytes32 tokenMintPermission = GovernanceERC20(token).MINT_PERMISSION_ID();
            permissions[7] = PermissionLib.MultiTargetPermission({
                operation: PermissionLib.Operation.Revoke,
                where: token,
                who: _dao,
                condition: PermissionLib.NO_CONDITION,
                permissionId: tokenMintPermission
            });
        }
    }

    /// @notice Encodes installation parameters including Hats configuration.
    function encodeInstallationParametersHats(
        MajorityVotingBase.VotingSettings memory votingSettings,
        TokenSettings memory tokenSettings,
        GovernanceERC20.MintSettings memory mintSettings,
        IPlugin.TargetConfig memory targetConfig,
        uint256 minApprovals,
        bytes memory pluginMetadata,
        address[] memory excludedAccounts,
        HatsConfig memory hatsConfig
    ) external pure returns (bytes memory) {
        InstallationParameters memory params = InstallationParameters({
            votingSettings: votingSettings,
            tokenSettings: tokenSettings,
            mintSettings: mintSettings,
            targetConfig: targetConfig,
            minApprovals: minApprovals,
            pluginMetadata: pluginMetadata,
            excludedAccounts: excludedAccounts,
            hatsConfig: hatsConfig
        });

        return abi.encode(params);
    }

    /// @notice Decodes installation parameters including Hats configuration.
    function decodeInstallationParametersHats(bytes memory _data)
        public
        pure
        returns (InstallationParameters memory params)
    {
        return abi.decode(_data, (InstallationParameters));
    }

    /// @notice Checks if the DAO has MINT permission on the token.
    /// @dev Directly checks the permission rather than inferring from token type.
    /// @param token The token address to check.
    /// @param dao The DAO address.
    /// @return True if the DAO has MINT permission on the token, false otherwise.
    function _hasMintPermission(address token, address dao) internal view returns (bool) {
        try GovernanceERC20(token).MINT_PERMISSION_ID() returns (bytes32 mintPermission) {
            return IDAO(dao).hasPermission(token, dao, mintPermission, "");
        } catch {
            // If MINT_PERMISSION_ID() call fails, it's not a GovernanceERC20
            return false;
        }
    }

    /// @notice Checks whether the provided token exposes the required IVotes interface.
    function supportsIVotesInterface(address token) public view returns (bool) {
        (bool success1, bytes memory data1) =
            token.staticcall(abi.encodeWithSelector(IVotesUpgradeable.getPastTotalSupply.selector, 0));
        (bool success2, bytes memory data2) =
            token.staticcall(abi.encodeWithSelector(IVotesUpgradeable.getVotes.selector, address(this)));
        (bool success3, bytes memory data3) =
            token.staticcall(abi.encodeWithSelector(IVotesUpgradeable.getPastVotes.selector, address(this), 0));

        return
            (success1 && data1.length == 0x20 && success2 && data2.length == 0x20 && success3 && data3.length == 0x20);
    }

    /// @notice Lightweight ERC20 interface check used during installation.
    function _isERC20(address token) private view returns (bool) {
        (bool success, bytes memory data) =
            token.staticcall(abi.encodeCall(IERC20Upgradeable.balanceOf, (address(this))));
        return success && data.length == 0x20;
    }
}
"
    },
    "lib/token-voting-plugin/lib/openzeppelin-contracts/contracts/proxy/Clones.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}
"
    },
    "lib/token-voting-plugin/lib/openzeppelin-contracts/contracts/utils/Address.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
"
    },
    "lib/token-voting-plugin/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Checker.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface.
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
            !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool[] memory) {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     *
     * Some precompiled contracts will falsely indicate support for a given interface, so caution
     * should be exercised when using this function.
     *
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}
"
    },
    "lib/ve-governance/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
"
    },
    "lib/ve-governance/lib/openzeppelin-contracts-upgradeable/contracts/governance/utils/IVotesUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;

/**
 * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
 *
 * _Available since v4.5._
 */
interface IVotesUpgradeable {
    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);

    /**
     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     */
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /**
     * @dev Returns the current amount of votes that `account` has.
     */
    function getVotes(address account) external view returns (uint256);

    /**
     * @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value at the end of the corresponding block.
     */
    function getPastVotes(address account, uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value at the end of the corresponding block.
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the delegate that `account` has chosen.
     */
    function delegates(address account) external view returns (address);

    /**
     * @dev Delegates votes from the sender to `delegatee`.
     */
    function delegate(address delegatee) external;

    /**
     * @dev Delegates votes from signer to `delegatee`.
     */
    function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}
"
    },
    "lib/token-voting-plugin/src/erc20/GovernanceERC20.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

/* solhint-disable max-line-length */
import {IERC20PermitUpgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IERC20MetadataUpgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
import {ERC20VotesUpgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import {IVotesUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/utils/IVotesUpgradeable.sol";

import {DaoAuthorizableUpgradeable} from
    "@aragon/osx-commons-contracts/src/permission/auth/DaoAuthorizableUpgradeable.sol";
import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {IERC20MintableUpgradeable} from "./IERC20MintableUpgradeable.sol";

/* solhint-enable max-line-length */

/// @title GovernanceERC20
/// @author Aragon X
/// @notice An [OpenZeppelin `Votes`](https://docs.openzeppelin.com/contracts/4.x/api/governance#Votes)
/// compatible [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token, used for voting and managed by a DAO.
/// @custom:security-contact sirt@aragon.org
contract GovernanceERC20 is
    IERC20MintableUpgradeable,
    Initializable,
    ERC165Upgradeable,
    ERC20VotesUpgradeable,
    DaoAuthorizableUpgradeable
{
    /// @notice The permission identifier to mint new tokens
    bytes32 public constant MINT_PERMISSION_ID = keccak256("MINT_PERMISSION");

    /// @notice Whether mint() has been permanently disabled.
    bool private mintingFrozen;

    /// @notice Whether mint() should enable self delegation if the receiver has no delegate.
    bool private ensureDelegationOnMint;

    /// @notice The settings for the initial mint of the token.
    /// @param receivers The receivers of the tokens. On initialization only.
    /// @param amounts The amounts of tokens to be minted for each receiver. On initialization only.
    /// @param ensureDelegationOnMint Whether mint() calls should self delegate if the receiver doesn't have one.
    /// @dev The lengths of `receivers` and `amounts` must match.
    struct MintSettings {
        address[] receivers;
        uint256[] amounts;
        bool ensureDelegationOnMint;
    }

    /// @notice Emitted when minting is frozen permanently
    event MintingFrozen();

    /// @notice Thrown if the number of receivers and amounts specified in the mint settings do not match.
    /// @param receiversArrayLength The length of the `receivers` array.
    /// @param amountsArrayLength The length of the `amounts` array.
    error MintSettingsArrayLengthMismatch(uint256 receiversArrayLength, uint256 amountsArrayLength);

    /// @notice Thrown when attempting to mint when minting is permanently disabled
    error MintingIsFrozen();

    /// @notice Calls the initialize function.
    /// @param _dao The managing DAO.
    /// @param _name The name of the [ERC-20](https://eips.ethereum.org/EIPS/eip-20) governance token.
    /// @param _symbol The symbol of the [ERC-20](https://eips.ethereum.org/EIPS/eip-20) governance token.
    /// @param _mintSettings The token mint settings struct containing the `receivers`, the `amounts` and `ensureDelegationOnMint`.
    constructor(IDAO _dao, string memory _name, string memory _symbol, MintSettings memory _mintSettings) {
        initialize(_dao, _name, _symbol, _mintSettings);
    }

    /// @notice Initializes the contract and mints tokens to a list of receivers.
    /// @param _dao The managing DAO.
    /// @param _name The name of the [ERC-20](https://eips.ethereum.org/EIPS/eip-20) governance token.
    /// @param _symbol The symbol of the [ERC-20](https://eips.ethereum.org/EIPS/eip-20) governance token.
    /// @param _mintSettings The token mint settings struct containing the `receivers`, the `amounts` and `ensureDelegationOnMint`.
    function initialize(IDAO _dao, string memory _name, string memory _symbol, MintSettings memory _mintSettings)
        public
        initializer
    {
        // Check mint settings
        if (_mintSettings.receivers.length != _mintSettings.amounts.length) {
            revert MintSettingsArrayLengthMismatch({
                receiversArrayLength: _mintSettings.receivers.length,
                amountsArrayLength: _mintSettings.amounts.length
            });
        }

        __ERC20_init(_name, _symbol);
        __ERC20Permit_init(_name);
        __DaoAuthorizableUpgradeable_init(_dao);

        // Mint
        ensureDelegationOnMint = _mintSettings.ensureDelegationOnMint;

        for (uint256 i; i < _mintSettings.receivers.length;) {
            address receiver = _mintSettings.receivers[i];
            if (_mintSettings.ensureDelegationOnMint) {
                _delegate(receiver, receiver);
            }
            _mint(receiver, _mintSettings.amounts[i]);

            unchecked {
                ++i;
            }
        }
    }

    /// @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(IERC20Upgradeable).interfaceId
            || _interfaceId == type(IERC20PermitUpgradeable).interfaceId
            || _interfaceId == type(IERC20MetadataUpgradeable).interfaceId
            || _interfaceId == type(IVotesUpgradeable).interfaceId
            || _interfaceId == type(IERC20MintableUpgradeable).interfaceId || super.supportsInterface(_interfaceId);
    }

    /// @notice Mints tokens to an address.
    /// @param to The address receiving the tokens.
    /// @param amount The amount of tokens to be minted.
    function mint(address to, uint256 amount) public virtual override auth(MINT_PERMISSION_ID) {
        if (getMintingFrozen()) {
            revert MintingIsFrozen();
        }

        if (getEnsureDelegationOnMint() && delegates(to) == address(0)) {
            _delegate(to, to);
        }
        _mint(to, amount);
    }

    /// @notice Disables the mint() function permanently
    function freezeMinting() public virtual auth(MINT_PERMISSION_ID) {
        if (getMintingFrozen()) return;

        mintingFrozen = true;
        emit MintingFrozen();
    }

    /// @notice Returns true if the ability to mint tokens has been frozen
    function getMintingFrozen() public view virtual returns (bool) {
        return mintingFrozen;
    }

    /// @notice Whether mint() enables self delegation if the receiver has no delegate.
    function getEnsureDelegationOnMint() public view virtual returns (bool) {
        return ensureDelegationOnMint;
    }
}
"
    },
    "lib/token-voting-plugin/src/erc20/GovernanceWrappedERC20.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

/* solhint-disable max-line-length */
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {ERC20WrapperUpgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20WrapperUpgradeable.sol";
import {IVotesUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/utils/IVotesUpgradeable.sol";
import {IERC20PermitUpgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IERC20MetadataUpgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
import {ERC20VotesUpgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import {IGovernanceWrappedERC20} from "./IGovernanceWrappedERC20.sol";

/* solhint-enable max-line-length */

/// @title GovernanceWrappedERC20
/// @author Aragon X
/// @notice Wraps an existing [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token by
/// inheriting from `ERC20WrapperUpgradeable` and allows using it for voting by inheriting from `ERC20VotesUpgradeable`.
/// The latter is compatible with
/// [OpenZeppelin's `Votes`](https://docs.openzeppelin.com/contracts/4.x/api/governance#Votes) interface.
/// The contract supports meta transactions. To use an `amount` of underlying tokens for voting, the token owner must:
/// 1. call `approve` for the tokens to be used by this contract
/// 2. call `depositFor` to wrap them, which safely transfers the underlying
/// [ERC-20](https://eips.ethereum.org/EIPS/eip-20) tokens to the contract and mints wrapped
/// [ERC-20](https://eips.ethereum.org/EIPS/eip-20) tokens.
/// To get the [ERC-20](https://eips.ethereum.org/EIPS/eip-20) tokens back, the owner of the wrapped tokens can call
/// `withdrawFor`, which  burns the wrapped [ERC-20](https://eips.ethereum.org/EIPS/eip-20) tokens and
/// safely transfers the underlying tokens back to the owner.
/// @dev This contract intentionally has no public mint functionality because this is the
///      responsibility of the underlying [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token contract.
/// @custom:security-contact sirt@aragon.org
contract GovernanceWrappedERC20 is
    IGovernanceWrappedERC20,
    Initializable,
    ERC165Upgradeable,
    ERC20VotesUpgradeable,
    ERC20WrapperUpgradeable
{
    /// @notice Calls the initialize function.
    /// @param _token The underlying [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token.
    /// @param _name The name of the wrapped token.
    /// @param _symbol The symbol of the wrapped token.
    constructor(IERC20Upgradeable _token, string memory _name, string memory _symbol) {
        initialize(_token, _name, _symbol);
    }

    /// @notice Initializes the contract.
    /// @param _token The underlying [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token.
    /// @param _name The name of the wrapped token.
    /// @param _symbol The symbol of the wrapped token.
    function initialize(IERC20Upgradeable _token, string memory _name, string memory _symbol) public initializer {
        __ERC20_init(_name, _symbol);
        __ERC20Permit_init(_name);
        __ERC20Wrapper_init(_token);
    }

    /// @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(IGovernanceWrappedERC20).interfaceId
            || _interfaceId == type(IERC20Upgradeable).interfaceId
            || _interfaceId == type(IERC20PermitUpgradeable).interfaceId
            || _interfaceId == type(IERC20MetadataUpgradeable).interfaceId
            || _interfaceId == type(IVotesUpgradeable).interfaceId || super.supportsInterface(_interfaceId);
    }

    /// @inheritdoc ERC20WrapperUpgradeable
    /// @dev Uses the `decimals` of the underlying [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token.
    function decimals() public view override(ERC20Upgradeable, ERC20WrapperUpgradeable) returns (uint8) {
        return ERC20WrapperUpgradeable.decimals();
    }

    /// @inheritdoc IGovernanceWrappedERC20
    function depositFor(address account, uint256 amount)
        public
        override(IGovernanceWrappedERC20, ERC20WrapperUpgradeable)
        returns (bool)
    {
        return ERC20WrapperUpgradeable.depositFor(account, amount);
    }

    /// @inheritdoc IGovernanceWrappedERC20
    function withdrawTo(address account, uint256 amount)
        public
        override(IGovernanceWrappedERC20, ERC20WrapperUpgradeable)
        returns (bool)
    {
        return ERC20WrapperUpgradeable.withdrawTo(account, amount);
    }

    // https://forum.openzeppelin.com/t/self-delegation-in-erc20votes/17501/12?u=novaknole
    /// @inheritdoc ERC20VotesUpgradeable
    function _afterTokenTransfer(address from, address to, uint256 amount)
        internal
        override(ERC20VotesUpgradeable, ERC20Upgradeable)
    {
        super._afterTokenTransfer(from, to, amount);

        // Automatically turn on delegation on mint/transfer if not delegating to anyone yet.
        if (to != address(0) && delegates(to) == address(0)) {
            _delegate(to, to);
        }
    }

    /// @inheritdoc ERC20VotesUpgradeable
    function _mint(address to, uint256 amount) internal override(ERC20VotesUpgradeable, ERC20Upgradeable) {
        super._mint(to, amount);
    }

    /// @inheritdoc ERC20VotesUpgradeable
    function _burn(address account, uint256 amount) internal override(ERC20VotesUpgradeable, ERC20Upgradeable) {
        super._burn(account, amount);
    }
}
"
    },
    "lib/token-voting-plugin/src/TokenVotingHats.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

import {TokenVoting, MajorityVotingBase} from "./TokenVoting.sol";

/// @title TokenVotingHats
/// @notice Token voting plugin variant that relies on Hats-based permission conditions.
/// @dev v1.0 (Release 1, Build 1). However, to support inheritence from TokenVoting (which is on Build 4), the initializer version of this contract is set to 3.
contract TokenVotingHats is TokenVoting {
    /// @notice Permission identifier required to cast votes.
    bytes32 public constant CAST_VOTE_PERMISSION_ID = keccak256("CAST_VOTE_PERMISSION");

    /// @inheritdoc MajorityVotingBase
    function vote(uint256 _proposalId, VoteOption _voteOption, bool _tryEarlyExecution)
        public
        override
        auth(CAST_VOTE_PERMISSION_ID)
    {
        super.vote(_proposalId, _voteOption, _tryEarlyExecution);
    }
}
"
    },
    "lib/token-voting-plugin/src/condition/HatsCondition.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

import {IHats} from "../interfaces/IHats.sol";

import {IPermissionCondition} from "@aragon/osx-commons-contracts/src/permission/condition/IPermissionCondition.sol";
import {PermissionCondition} from "@aragon/osx-commons-contracts/src/permission/condition/PermissionCondition.sol";

/// @title HatsCondition
/// @notice Permission condition that authorizes calls based on configured Hats wearers.
contract HatsCondition is PermissionCondition {
    /// @dev Sentinel value that bypasses the hat check when used.
    uint256 internal constant PUBLIC_SENTINEL = uint256(1);

    /// @notice The permission identifiers that can be checked through this condition.
    bytes32 internal constant CREATE_PROPOSAL_PERMISSION_ID = keccak256("CREATE_PROPOSAL_PERMISSION");
    bytes32 internal constant CAST_VOTE_PERMISSION_ID = keccak256("CAST_VOTE_PERMISSION");
    bytes32 internal constant EXECUTE_PROPOSAL_PERMISSION_ID = keccak256("EXECUTE_PROPOSAL_PERMISSION");

    /// @notice Hats protocol contract consulted for membership checks.
    IHats private constant HATS = IHats(0x3bc1A0Ad72417f2d411118085256fC53CBdDd137);

    /// @notice Mapping from permission identifier to the hat id that must be worn.
    mapping(bytes32 => uint256) public hatForPermission;

    /// @param _proposerHatId Hat required to create proposals.
    /// @param _voterHatId Hat required to cast votes.
    /// @param _executorHatId Hat required to execute proposals.
    constructor(uint256 _proposerHatId, uint256 _voterHatId, uint256 _executorHatId) {
        hatForPermission[CREATE_PROPOSAL_PERMISSION_ID] = _proposerHatId;
        hatForPermission[CAST_VOTE_PERMISSION_ID] = _voterHatId;
        hatForPermission[EXECUTE_PROPOSAL_PERMISSION_ID] = _executorHatId;
    }

    /// @inheritdoc IPermissionCondition
    function isGranted(address, /* _where */ address _who, bytes32 _permissionId, bytes calldata /* _data */ )
        public
        view
        override
        returns (bool)
    {
        uint256 hatId = hatForPermission[_permissionId];

        if (hatId == PUBLIC_SENTINEL) {
            return true;
        }

        if (hatId == 0) {
            return false;
        }

        return HATS.isWearerOfHat(_who, hatId);
    }
}
"
    },
    "lib/token-voting-plugin/src/base/MajorityVotingBase.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

/* solhint-disable max-line-length */

import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";

import {ProposalUpgradeable} from "@aragon/osx-commons-contracts/src/plugin/extensions/proposal/ProposalUpgradeable.sol";
import {RATIO_BASE, RatioOutOfBounds} from "@aragon/osx-commons-contracts/src/utils/math/Ratio.sol";
import {PluginUUPSUpgradeable} from "@aragon/osx-commons-contracts/src/plugin/PluginUUPSUpgradeable.sol";
import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {IProposal} from "@aragon/osx-commons-contracts/src/plugin/extensions/proposal/IProposal.sol";
import {Action} from "@aragon/osx-commons-contracts/src/executors/IExecutor.sol";
import {MetadataExtensionUpgradeable} from
    "@aragon/osx-commons-contracts/src/utils/metadata/MetadataExtensionUpgradeable.sol";

import {IMajorityVoting} from "./IMajorityVoting.sol";

/* solhint-enable max-line-length */

/// @title MajorityVotingBase
/// @author Aragon X - 2022-2025
/// @notice The abstract implementation of majority voting plugins.
///
/// ### Parameterization
///
/// We define two parameters
/// $$\	exttt{support} = \frac{N_\	ext{yes}}{N_\	ext{yes} + N_\	ext{no}} \in [0,1]$$
/// and
/// $$\	exttt{participation} = \frac{N_\	ext{yes} + N_\	ext{no} + N_\	ext{abstain}}{N_\	ext{total}} \in [0,1],$$
/// where $N_\	ext{yes}$, $N_\	ext{no}$, and $N_\	ext{abstain}$ are the yes, no, and abstain votes that have been
/// cast and $N_\	ext{total}$ is the total voting power available at proposal creation time.
///
/// #### Limit Values: Support Threshold & Minimum Participation
///
/// Two limit values are associated with these parameters and decide if a proposal execution should be possible:
/// $\	exttt{supportThreshold} \in [0,1)$ and $\	exttt{minParticipation} \in [0,1]$.
///
/// For threshold values, $>$ comparison is used. This **does not** include the threshold value.
/// E.g., for $\	exttt{supportThreshold} = 50\%$,
/// the criterion is fulfilled if there is at least one more yes than no votes ($N_\	ext{yes} = N_\	ext{no} + 1$).
/// For minimum values, $\ge{}$ comparison is used. This **does** include the minimum participation value.
/// E.g., for $\	exttt{minParticipation} = 40\%$ and $N_\	ext{total} = 10$,
/// the criterion is fulfilled if 4 out of 10 votes were casted.
///
/// Majority voting implies that the support threshold is set with
/// $$\	exttt{supportThreshold} \ge 50\% .$$
/// However, this is not enforced by the contract code and developers can make unsafe parameters and
/// only the frontend will warn about bad parameter settings.
///
/// ### Execution Criteria
///
/// After the vote is closed, two criteria decide if the proposal passes.
///
/// #### The Support Criterion
///
/// For a proposal to pass, the required ratio of yes and no votes must be met:
/// $$(1- \	exttt{supportThreshold}) \cdot N_\	ext{yes} > \	exttt{supportThreshold} \cdot N_\	ext{no}.$$
/// Note, that the inequality yields the simple majority voting condition for $\	exttt{supportThreshold}=\frac{1}{2}$.
///
/// #### The Participation Criterion
///
/// For a proposal to pass, the minimum voting power must have been cast:
/// $$N_\	ext{yes} + N_\	ext{no} + N_\	ext{abstain} \ge \	exttt{minVotingPower},$$
/// where $\	exttt{minVotingPower} = \	exttt{minParticipation} \cdot N_\	ext{total}$.
///
/// ### Vote Replacement
///
/// The contract allows votes to be replaced. Voters can vote multiple times
/// and only the latest voteOption is tallied.
///
/// ### Early Execution
///
/// This contract allows a proposal to be executed early,
/// iff the vote outcome cannot change anymore by more people voting.
/// Accordingly, vote replacement and early execution are mutually exclusive options.
/// The outcome cannot change anymore
/// iff the support threshold is met even if all remaining votes are no votes.
/// We call this number the worst-case number of no votes and define it as
///
/// $$N_\	ext{no, worst-case} = N_\	ext{no} + \	exttt{remainingVotes}$$
///
/// where
///
/// $$\	exttt{remainingVotes} =
/// N_\	ext{total}-\underbrace{(N_\	ext{yes}+N_\	ext{no}+N_\	ext{abstain})}_{\	ext{turnout}}.$$
///
/// We can use this quantity to calculate the worst-case support that would be obtained
/// if all remaining votes are casted with no:
///
/// $$
/// \begin{align*}
///   \	exttt{worstCaseSupport}
///   &= \frac{N_\	ext{yes}}{N_\	ext{yes} + (N_\	ext{no, worst-case})} \\[3mm]
///   &= \frac{N_\	ext{yes}}{N_\	ext{yes} + (N_\	ext{no} + \	exttt{remainingVotes})} \\[3mm]
///   &= \frac{N_\	ext{yes}}{N_\	ext{yes} +  N_\	ext{no} + N_\	ext{total}
///      - (N_\	ext{yes} + N_\	ext{no} + N_\	ext{abstain})} \\[3mm]
///   &= \frac{N_\	ext{yes}}{N_\	ext{total} - N_\	ext{abstain}}
/// \end{align*}
/// $$
///
/// In analogy, we can modify [the support criterion](#the-support-criterion)
/// from above to allow for early execution:
///
/// $$
/// \begin{align*}
///   (1 - \	exttt{supportThreshold}) \cdot N_\	ext{yes}
///   &> \	exttt{supportThreshold} \cdot  N_\	ext{no, worst-case} \\[3mm]
///   &> \	exttt{supportThreshold} \cdot (N_\	ext{no} + \	exttt{remainingVotes}) \\[3mm]
///   &> \	exttt{supportThreshold} \cdot (N_\	ext{no}
///     + N_\	ext{total}-(N_\	ext{yes}+N_\	ext{no}+N_\	ext{abstain})) \\[3mm]
///   &> \	exttt{supportThreshold} \cdot (N_\	ext{total} - N_\	ext{yes} - N_\	ext{abstain})
/// \end{align*}
/// $$
///
/// Accordingly, early execution is possible when the vote is open,
///     the modified support criterion, and the particicpation criterion are met.
/// @dev This contract implements the `IMajorityVoting` interface.
/// @custom:security-contact sirt@aragon.org
abstract contract MajorityVotingBase is
    IMajorityVoting,
    Initializable,
    ERC165Upgradeable,
    MetadataExtensionUpgradeable,
    PluginUUPSUpgradeable,
    ProposalUpgradeable
{
    using SafeCastUpgradeable for uint256;

    /// @notice The different voting modes available.
    /// @param Standard In standard mode, early execution and vote replacement are disabled.
    /// @param EarlyExecution 

Tags:
ERC20, ERC1155, ERC165, Multisig, Mintable, Non-Fungible, Swap, Yield, Voting, Upgradeable, Multi-Signature, Factory|addr:0x52ee0f78111565c6f41f3b4c1763dafa286432ed|verified:true|block:23749436|tx:0x0f21657b7a775b973e3f489a9c94d42c5af45777ce80e81289c0e7fceb6c6bfd|first_check:1762543246

Submitted on: 2025-11-07 20:20:48

Comments

Log in to comment.

No comments yet.