ConditionFactory

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/factory/ConditionFactory.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.22;

import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {ExecuteSelectorCondition} from "../ExecuteSelectorCondition.sol";
import {SelectorCondition} from "../SelectorCondition.sol";
import {SafeOwnerCondition, IOwnerManager} from "../SafeOwnerCondition.sol";

/// @title ConditionFactory
/// @author AragonX 2025
/// @notice A factory used to deploy new condition instances
contract ConditionFactory {
    event ExecuteSelectorConditionDeployed(ExecuteSelectorCondition newContract);
    event SelectorConditionDeployed(SelectorCondition newContract);
    event SafeOwnerConditionDeployed(SafeOwnerCondition newContract);

    function deployExecuteSelectorCondition(IDAO _dao, ExecuteSelectorCondition.SelectorTarget[] memory _initialEntries)
        public
        returns (ExecuteSelectorCondition newContract)
    {
        newContract = new ExecuteSelectorCondition(_dao, _initialEntries);
        emit ExecuteSelectorConditionDeployed(newContract);
    }

    function deploySelectorCondition(IDAO _dao, bytes4[] memory _initialSelectors)
        public
        returns (SelectorCondition newContract)
    {
        newContract = new SelectorCondition(_dao, _initialSelectors);
        emit SelectorConditionDeployed(newContract);
    }

    function deploySafeOwnerCondition(address _safe) public returns (SafeOwnerCondition newContract) {
        newContract = new SafeOwnerCondition(IOwnerManager(_safe));
        emit SafeOwnerConditionDeployed(newContract);
    }
}
"
    },
    "lib/osx-commons/contracts/src/dao/IDAO.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

/// @title IDAO
/// @author Aragon X - 2022-2024
/// @notice The interface required for DAOs within the Aragon App DAO framework.
/// @custom:security-contact sirt@aragon.org
interface IDAO {
    /// @notice Checks if an address has permission on a contract via a permission identifier and considers if `ANY_ADDRESS` was used in the granting process.
    /// @param _where The address of the contract.
    /// @param _who The address of a EOA or contract to give the permissions.
    /// @param _permissionId The permission identifier.
    /// @param _data The optional data passed to the `PermissionCondition` registered.
    /// @return Returns true if the address has permission, false if not.
    function hasPermission(
        address _where,
        address _who,
        bytes32 _permissionId,
        bytes memory _data
    ) external view returns (bool);

    /// @notice Updates the DAO metadata (e.g., an IPFS hash).
    /// @param _metadata The IPFS hash of the new metadata object.
    function setMetadata(bytes calldata _metadata) external;

    /// @notice Emitted when the DAO metadata is updated.
    /// @param metadata The IPFS hash of the new metadata object.
    event MetadataSet(bytes metadata);

    /// @notice Emitted when a standard callback is registered.
    /// @param interfaceId The ID of the interface.
    /// @param callbackSelector The selector of the callback function.
    /// @param magicNumber The magic number to be registered for the callback function selector.
    event StandardCallbackRegistered(
        bytes4 interfaceId,
        bytes4 callbackSelector,
        bytes4 magicNumber
    );

    /// @notice Deposits (native) tokens to the DAO contract with a reference string.
    /// @param _token The address of the token or address(0) in case of the native token.
    /// @param _amount The amount of tokens to deposit.
    /// @param _reference The reference describing the deposit reason.
    function deposit(address _token, uint256 _amount, string calldata _reference) external payable;

    /// @notice Emitted when a token deposit has been made to the DAO.
    /// @param sender The address of the sender.
    /// @param token The address of the deposited token.
    /// @param amount The amount of tokens deposited.
    /// @param _reference The reference describing the deposit reason.
    event Deposited(
        address indexed sender,
        address indexed token,
        uint256 amount,
        string _reference
    );

    /// @notice Emitted when a native token deposit has been made to the DAO.
    /// @dev This event is intended to be emitted in the `receive` function and is therefore bound by the gas limitations for `send`/`transfer` calls introduced by [ERC-2929](https://eips.ethereum.org/EIPS/eip-2929).
    /// @param sender The address of the sender.
    /// @param amount The amount of native tokens deposited.
    event NativeTokenDeposited(address sender, uint256 amount);

    /// @notice Setter for the trusted forwarder verifying the meta transaction.
    /// @param _trustedForwarder The trusted forwarder address.
    function setTrustedForwarder(address _trustedForwarder) external;

    /// @notice Getter for the trusted forwarder verifying the meta transaction.
    /// @return The trusted forwarder address.
    function getTrustedForwarder() external view returns (address);

    /// @notice Emitted when a new TrustedForwarder is set on the DAO.
    /// @param forwarder the new forwarder address.
    event TrustedForwarderSet(address forwarder);

    /// @notice Checks whether a signature is valid for a provided hash according to [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271).
    /// @param _hash The hash of the data to be signed.
    /// @param _signature The signature byte array associated with `_hash`.
    /// @return Returns the `bytes4` magic value `0x1626ba7e` if the signature is valid and `0xffffffff` if not.
    function isValidSignature(bytes32 _hash, bytes memory _signature) external returns (bytes4);

    /// @notice Registers an ERC standard having a callback by registering its [ERC-165](https://eips.ethereum.org/EIPS/eip-165) interface ID and callback function signature.
    /// @param _interfaceId The ID of the interface.
    /// @param _callbackSelector The selector of the callback function.
    /// @param _magicNumber The magic number to be registered for the function signature.
    function registerStandardCallback(
        bytes4 _interfaceId,
        bytes4 _callbackSelector,
        bytes4 _magicNumber
    ) external;

    /// @notice Removed function being left here to not corrupt the IDAO interface ID. Any call will revert.
    /// @dev Introduced in v1.0.0. Removed in v1.4.0.
    function setSignatureValidator(address) external;
}
"
    },
    "src/ExecuteSelectorCondition.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.22;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {DaoAuthorizable} from "@aragon/osx-commons-contracts/src/permission/auth/DaoAuthorizable.sol";
import {IPermissionCondition} from "@aragon/osx-commons-contracts/src/permission/condition/IPermissionCondition.sol";
import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {IExecutor, Action} from "@aragon/osx-commons-contracts/src/executors/IExecutor.sol";
import {getSelector} from "./lib/common.sol";

/// @title ExecuteSelectorCondition
/// @author AragonX 2025
/// @notice A permission that only allows a specified group of function selectors to be invoked within DAO.execute()
contract ExecuteSelectorCondition is ERC165, IPermissionCondition, DaoAuthorizable {
    /// @notice Contains a list of selectors for the given target (where) address
    struct SelectorTarget {
        /// @notice The address where the selectors below can be invoked
        address where;
        /// @notice The list of function selectors that can be invoked within an execute() call.
        /// @notice Plain native transfers should contain 0 as the selector.
        bytes4[] selectors;
    }

    /// @notice Stores whether the given address and selector are allowed
    /// @dev allowedSelectors[where][selector]
    mapping(address => mapping(bytes4 => bool)) public allowedSelectors;

    /// @notice Stores whether native transfers are allowed to the given target address
    mapping(address => bool) public allowedNativeTransfers;

    bytes32 public constant MANAGE_SELECTORS_PERMISSION_ID = keccak256("MANAGE_SELECTORS_PERMISSION");

    /// @notice Emitted when a new selector is allowed.
    event SelectorAllowed(bytes4 selector, address where);
    /// @notice Emitted when a selector is disallowed.
    event SelectorDisallowed(bytes4 selector, address where);
    /// @notice Emitted when native transfers are allowed to the given address
    event NativeTransfersAllowed(address where);
    /// @notice Emitted when native transfers are disallowed to the given address
    event NativeTransfersDisallowed(address where);

    /// @notice Configures a new instance with the given set of allowed selectors
    /// @param _dao The address of the DAO where the contract should read the permissions from
    /// @param _initialEntries The list of allowed selectors and the addresses where they can be invoked
    constructor(IDAO _dao, SelectorTarget[] memory _initialEntries) DaoAuthorizable(_dao) {
        for (uint256 i; i < _initialEntries.length; i++) {
            _allowSelectors(_initialEntries[i]);
        }
    }

    /// @notice Marks the given selectors as allowed on the given where address
    /// @param _newEntry The new selectors and the address where they can be invoked
    function allowSelectors(SelectorTarget memory _newEntry) public virtual auth(MANAGE_SELECTORS_PERMISSION_ID) {
        _allowSelectors(_newEntry);
    }

    /// @notice Marks the given selector(s) as disallowed
    /// @param _entry The selectors to remove and the address where they can no longer be invoked
    function disallowSelectors(SelectorTarget memory _entry) public virtual auth(MANAGE_SELECTORS_PERMISSION_ID) {
        _disallowSelectors(_entry);
    }

    /// @notice Allows actions with a non-zero `value` to pass for the given target address
    /// @param _where The target address
    function allowNativeTransfers(address _where) public virtual auth(MANAGE_SELECTORS_PERMISSION_ID) {
        if (allowedNativeTransfers[_where]) return;

        _allowNativeTransfers(_where);
    }

    /// @notice Restricts actions with a non-zero `value` for the given target address
    /// @param _where The target address
    function disallowNativeTransfers(address _where) public virtual auth(MANAGE_SELECTORS_PERMISSION_ID) {
        if (!allowedNativeTransfers[_where]) return;

        _disallowNativeTransfers(_where);
    }

    /// @inheritdoc IPermissionCondition
    function isGranted(address _where, address _who, bytes32 _permissionId, bytes calldata _data)
        public
        view
        virtual
        returns (bool isPermitted)
    {
        (_where, _who, _permissionId);

        // Calling execute()?
        if (getSelector(_data) != IExecutor.execute.selector) {
            return false;
        }

        // Decode proposal params
        (, Action[] memory _actions,) = abi.decode(_data[4:], (bytes32, Action[], uint256));
        for (uint256 i; i < _actions.length; i++) {
            if (_actions[i].data.length == 0) {
                if (_actions[i].value == 0) return false;
                else if (!allowedNativeTransfers[_actions[i].to]) return false;
            } else if (_actions[i].data.length < 4) {
                return false;
            } else if (!allowedSelectors[_actions[i].to][getSelector(_actions[i].data)]) {
                return false;
            } else if (_actions[i].value != 0 && !allowedNativeTransfers[_actions[i].to]) {
                return false;
            }
        }
        return true;
    }

    /// @notice Checks if an interface is supported by this or its parent contract.
    /// @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(IPermissionCondition).interfaceId || super.supportsInterface(_interfaceId);
    }

    // Internal helpers

    function _allowSelectors(SelectorTarget memory _newEntry) internal virtual {
        for (uint256 i; i < _newEntry.selectors.length; i++) {
            if (allowedSelectors[_newEntry.where][_newEntry.selectors[i]]) {
                // The requested state is already in place
                continue;
            }
            allowedSelectors[_newEntry.where][_newEntry.selectors[i]] = true;
            emit SelectorAllowed(_newEntry.selectors[i], _newEntry.where);
        }
    }

    function _disallowSelectors(SelectorTarget memory _entry) internal virtual {
        for (uint256 i; i < _entry.selectors.length; i++) {
            if (!allowedSelectors[_entry.where][_entry.selectors[i]]) {
                // The requested state is already in place
                continue;
            }
            allowedSelectors[_entry.where][_entry.selectors[i]] = false;
            emit SelectorDisallowed(_entry.selectors[i], _entry.where);
        }
    }

    function _allowNativeTransfers(address _where) internal virtual {
        allowedNativeTransfers[_where] = true;
        emit NativeTransfersAllowed(_where);
    }

    function _disallowNativeTransfers(address _where) internal virtual {
        allowedNativeTransfers[_where] = false;
        emit NativeTransfersDisallowed(_where);
    }
}
"
    },
    "src/SelectorCondition.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.22;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {DaoAuthorizable} from "@aragon/osx-commons-contracts/src/permission/auth/DaoAuthorizable.sol";
import {IPermissionCondition} from "@aragon/osx-commons-contracts/src/permission/condition/IPermissionCondition.sol";
import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {getSelector} from "./lib/common.sol";

/// @title SelectorCondition
/// @author AragonX 2025
/// @notice A permission that only allows a specified group of function selectors to be invoked within DAO.execute()
contract SelectorCondition is ERC165, IPermissionCondition, DaoAuthorizable {
    mapping(bytes4 => bool) public allowedSelectors;

    bytes32 public constant MANAGE_SELECTORS_PERMISSION_ID = keccak256("MANAGE_SELECTORS_PERMISSION");

    error AlreadyAllowed(bytes4 selector);
    error AlreadyDisallowed(bytes4 selector);

    event SelectorAllowed(bytes4 selector);
    event SelectorDisallowed(bytes4 selector);

    constructor(IDAO _dao, bytes4[] memory _initialSelectors) DaoAuthorizable(_dao) {
        for (uint256 i; i < _initialSelectors.length; i++) {
            allowedSelectors[_initialSelectors[i]] = true;
            emit SelectorAllowed(_initialSelectors[i]);
        }
    }

    /// @notice Marks the given selector as allowed
    /// @param _selector The function selector to start allowing
    function allowSelector(bytes4 _selector) public virtual auth(MANAGE_SELECTORS_PERMISSION_ID) {
        if (allowedSelectors[_selector]) revert AlreadyAllowed(_selector);
        allowedSelectors[_selector] = true;

        emit SelectorAllowed(_selector);
    }

    /// @notice Marks the given selector as disallowed
    /// @param _selector The function selector to stop allowing
    function disallowSelector(bytes4 _selector) public virtual auth(MANAGE_SELECTORS_PERMISSION_ID) {
        if (!allowedSelectors[_selector]) revert AlreadyDisallowed(_selector);
        allowedSelectors[_selector] = false;

        emit SelectorDisallowed(_selector);
    }

    /// @inheritdoc IPermissionCondition
    function isGranted(address _where, address _who, bytes32 _permissionId, bytes calldata _data)
        public
        view
        virtual
        returns (bool isPermitted)
    {
        (_where, _who, _permissionId);

        return allowedSelectors[getSelector(_data)];
    }

    /// @notice Checks if an interface is supported by this or its parent contract.
    /// @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(IPermissionCondition).interfaceId || super.supportsInterface(_interfaceId);
    }
}
"
    },
    "src/SafeOwnerCondition.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.22;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IPermissionCondition} from "@aragon/osx-commons-contracts/src/permission/condition/IPermissionCondition.sol";
import {IOwnerManager} from "./interfaces/IOwnerManager.sol";

/// @title SafeOwnerCondition
/// @author AragonX 2025
/// @notice A permission that only allows Safe owners to make use of a granted permission.
contract SafeOwnerCondition is ERC165, IPermissionCondition {
    IOwnerManager public safe;

    /// @notice Thrown when given address is not a compatible Safe.
    /// @param invalidAddress The address received.
    error InvalidSafe(address invalidAddress);

    constructor(IOwnerManager _safe) {
        // Check if the given address is compatible with a Safe
        (bool success, bytes memory result) =
            address(_safe).staticcall(abi.encodeWithSelector(IOwnerManager.isOwner.selector, address(0)));
        if (!success || result.length != 32) {
            revert InvalidSafe(address(_safe));
        }

        safe = _safe;
    }

    /// @inheritdoc IPermissionCondition
    function isGranted(address _where, address _who, bytes32 _permissionId, bytes calldata _data)
        public
        view
        virtual
        returns (bool)
    {
        (_where, _permissionId, _data);

        (bool success, bytes memory result) =
            address(safe).staticcall(abi.encodeWithSelector(IOwnerManager.isOwner.selector, _who));

        // If the call failed or returned malformed data, treat as "not owner"
        if (!success || result.length != 32) {
            return false;
        }

        return abi.decode(result, (bool));
    }

    /// @notice Checks if an interface is supported by this or its parent contract.
    /// @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(IPermissionCondition).interfaceId || super.supportsInterface(_interfaceId);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
"
    },
    "lib/osx-commons/contracts/src/permission/auth/DaoAuthorizable.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

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

import {IDAO} from "../../dao/IDAO.sol";
import {_auth} from "./auth.sol";

/// @title DaoAuthorizable
/// @author Aragon X - 2022-2023
/// @notice An abstract contract providing a meta-transaction compatible modifier for non-upgradeable contracts instantiated via the `new` keyword to authorize function calls through an associated DAO.
/// @custom:security-contact sirt@aragon.org
abstract contract DaoAuthorizable is Context {
    /// @notice The associated DAO managing the permissions of inheriting contracts.
    IDAO private immutable DAO;

    /// @notice Constructs the contract by setting the associated DAO.
    /// @param _dao The associated DAO address.
    constructor(IDAO _dao) {
        DAO = _dao;
    }

    /// @notice Returns the DAO contract.
    /// @return The DAO contract.
    function dao() public view returns (IDAO) {
        return DAO;
    }

    /// @notice A modifier to make functions on inheriting contracts authorized. Permissions to call the function are checked through the associated DAO's permission manager.
    /// @param _permissionId The permission identifier required to call the method this modifier is applied to.
    modifier auth(bytes32 _permissionId) {
        _auth(DAO, address(this), _msgSender(), _permissionId, _msgData());
        _;
    }
}
"
    },
    "lib/osx-commons/contracts/src/permission/condition/IPermissionCondition.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

/// @title IPermissionCondition
/// @author Aragon X - 2021-2023
/// @notice An interface to be implemented to support custom permission logic.
/// @dev To attach a condition to a permission, the `grantWithCondition` function must be used and refer to the implementing contract's address with the `condition` argument.
/// @custom:security-contact sirt@aragon.org
interface IPermissionCondition {
    /// @notice Checks if a call is permitted.
    /// @param _where The address of the target contract.
    /// @param _who The address (EOA or contract) for which the permissions are checked.
    /// @param _permissionId The permission identifier.
    /// @param _data Optional data passed to the `PermissionCondition` implementation.
    /// @return isPermitted Returns true if the call is permitted.
    function isGranted(
        address _where,
        address _who,
        bytes32 _permissionId,
        bytes calldata _data
    ) external view returns (bool isPermitted);
}
"
    },
    "lib/osx-commons/contracts/src/executors/IExecutor.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

/// @notice The action struct to be consumed by the DAO's `execute` function resulting in an external call.
/// @param to The address to call.
/// @param value The native token value to be sent with the call.
/// @param data The bytes-encoded function selector and calldata for the call.
struct Action {
    address to;
    uint256 value;
    bytes data;
}

/// @title IExecutor
/// @author Aragon X - 2024
/// @notice The interface required for Executors within the Aragon App DAO framework.
/// @custom:security-contact sirt@aragon.org
interface IExecutor {
    /// @notice Emitted when a proposal is executed.
    /// @dev The value of `callId` is defined by the component/contract calling the execute function.
    ///      A `Plugin` implementation can use it, for example, as a nonce.
    /// @param actor The address of the caller.
    /// @param callId The ID of the call.
    /// @param actions The array of actions executed.
    /// @param allowFailureMap The allow failure map encoding which actions are allowed to fail.
    /// @param failureMap The failure map encoding which actions have failed.
    /// @param execResults The array with the results of the executed actions.
    event Executed(
        address indexed actor,
        bytes32 callId,
        Action[] actions,
        uint256 allowFailureMap,
        uint256 failureMap,
        bytes[] execResults
    );

    /// @notice Executes a list of actions. If a zero allow-failure map is provided, a failing action reverts the entire execution. If a non-zero allow-failure map is provided, allowed actions can fail without the entire call being reverted.
    /// @param _callId The ID of the call. The definition of the value of `callId` is up to the calling contract and can be used, e.g., as a nonce.
    /// @param _actions The array of actions.
    /// @param _allowFailureMap A bitmap allowing execution to succeed, even if individual actions might revert. If the bit at index `i` is 1, the execution succeeds even if the `i`th action reverts. A failure map value of 0 requires every action to not revert.
    /// @return The array of results obtained from the executed actions in `bytes`.
    /// @return The resulting failure map containing the actions have actually failed.
    function execute(
        bytes32 _callId,
        Action[] memory _actions,
        uint256 _allowFailureMap
    ) external returns (bytes[] memory, uint256);
}
"
    },
    "src/lib/common.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.22;

/// @notice Extracts the selector given the calldata. If no calldata is passed, it returns zero
function getSelector(bytes memory _data) pure returns (bytes4 selector) {
    if (_data.length < 4) revert("Data is too short");

    // Slices are only supported for bytes calldata, not bytes memory
    // Bytes memory requires an assembly block
    assembly {
        selector := mload(add(_data, 0x20)) // 32
    }
}
"
    },
    "src/interfaces/IOwnerManager.sol": {
      "content": "// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

/**
 * @title Owner Manager Interface
 * @notice Interface for managing Safe owners and a threshold to authorize transactions.
 * @author @safe-global/safe-protocol
 */
interface IOwnerManager {
    /**
     * @notice An owner was added.
     * @param owner The address of the new owner.
     */
    event AddedOwner(address indexed owner);

    /**
     * @notice An owner was removed.
     * @param owner The address of the old owner.
     */
    event RemovedOwner(address indexed owner);

    /**
     * @notice The signature threshold changed.
     * @param threshold The new threshold for authorizing Safe transactions.
     */
    event ChangedThreshold(uint256 threshold);

    /**
     * @notice Adds the owner `owner` to the Safe and updates the threshold to `_threshold`.
     * @dev This can only be done via a Safe transaction.
     *      ⚠️⚠️⚠️ A Safe can set itself as an owner which is a valid setup for EIP-7702 delegations.
     *      However, if address of the accounts is not an EOA and cannot sign for itself, you can
     *      potentially block access to the account completely. For example, if you have a `n/n`
     *      Safe (so `threshold == ownerCount`) and one of the owners is the Safe itself and not
     *      an EIP-7702 delegated account, then it will not be possible to produce a valid
     *      signature for the Safe. ⚠️⚠️⚠️
     * @param owner New owner address.
     * @param _threshold New threshold.
     */
    function addOwnerWithThreshold(address owner, uint256 _threshold) external;

    /**
     * @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`.
     * @dev This can only be done via a Safe transaction.
     * @param prevOwner Owner that pointed to the `owner` to be removed in the linked list.
     *        If the owner to be removed is the first (or only) element of the list,
     *        `prevOwner` MUST be set to the sentinel address `0x1` (referred to as
     *        `SENTINEL_OWNERS` in the implementation).
     * @param owner Owner address to be removed.
     * @param _threshold New threshold.
     */
    function removeOwner(address prevOwner, address owner, uint256 _threshold) external;

    /**
     * @notice Replaces the owner `oldOwner` in the Safe with `newOwner`.
     * @dev This can only be done via a Safe transaction.
     *      ⚠️⚠️⚠️ A Safe can set itself as an owner which is a valid setup for EIP-7702 delegations.
     *      However, if address of the accounts is not an EOA and cannot sign for itself, you can
     *      potentially block access to the account completely. For example, if you have a `n/n`
     *      Safe (so `threshold == ownerCount`) and one of the owners is the Safe itself and not
     *      an EIP-7702 delegated account, then it will not be possible to produce a valid
     *      signature for the Safe. ⚠️⚠️⚠️
     * @param prevOwner Owner that pointed to the `oldOwner` to be replaced in the linked list.
     *        If the owner to be replaced is the first (or only) element of the list,
     *        `prevOwner` MUST be set to the sentinel address `0x1` (referred to as
     *        `SENTINEL_OWNERS` in the implementation).
     * @param oldOwner Owner address to be replaced.
     * @param newOwner New owner address.
     */
    function swapOwner(address prevOwner, address oldOwner, address newOwner) external;

    /**
     * @notice Changes the threshold of the Safe to `_threshold`.
     * @dev This can only be done via a Safe transaction.
     * @param _threshold New threshold.
     */
    function changeThreshold(uint256 _threshold) external;

    /**
     * @notice Returns the number of required confirmations for a Safe transaction aka the threshold.
     * @return Threshold number.
     */
    function getThreshold() external view returns (uint256);

    /**
     * @notice Returns if `owner` is an owner of the Safe.
     * @return Boolean if `owner` is an owner of the Safe.
     */
    function isOwner(address owner) external view returns (bool);

    /**
     * @notice Returns a list of Safe owners.
     * @return Array of Safe owners.
     */
    function getOwners() external view returns (address[] memory);
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
"
    },
    "lib/osx-commons/contracts/src/permission/auth/auth.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.8;

import {IDAO} from "../../dao/IDAO.sol";

/// @title DAO Authorization Utilities
/// @author Aragon X - 2022-2024
/// @notice Provides utility functions for verifying if a caller has specific permissions in an associated DAO.
/// @custom:security-contact sirt@aragon.org

/// @notice Thrown if a call is unauthorized in the associated DAO.
/// @param dao The associated DAO.
/// @param where The context in which the authorization reverted.
/// @param who The address (EOA or contract) missing the permission.
/// @param permissionId The permission identifier.
error DaoUnauthorized(address dao, address where, address who, bytes32 permissionId);

/// @notice A free function checking if a caller is granted permissions on a target contract via a permission identifier that redirects the approval to a `PermissionCondition` if this was specified in the setup.
/// @param _where The address of the target contract for which `who` receives permission.
/// @param _who The address (EOA or contract) owning the permission.
/// @param _permissionId The permission identifier.
/// @param _data The optional data passed to the `PermissionCondition` registered.
function _auth(
    IDAO _dao,
    address _where,
    address _who,
    bytes32 _permissionId,
    bytes calldata _data
) view {
    if (!_dao.hasPermission(_where, _who, _permissionId, _data))
        revert DaoUnauthorized({
            dao: address(_dao),
            where: _where,
            who: _who,
            permissionId: _permissionId
        });
}
"
    }
  },
  "settings": {
    "remappings": [
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
      "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
      "@aragon/osx/=lib/osx/packages/contracts/src/",
      "@aragon/osx-commons-contracts/=lib/osx-commons/contracts/",
      "forge-std/=lib/forge-std/src/",
      "ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
      "ens-contracts.git/=lib/ens-contracts.git/contracts/",
      "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
      "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/",
      "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
      "osx-commons/=lib/osx-commons/",
      "osx/=lib/osx/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "shanghai",
    "viaIR": false
  }
}}

Tags:
ERC165, Multisig, Voting, Upgradeable, Multi-Signature, Factory|addr:0x988b0e1542d5e9494897778ebc444e9ffda58ddb|verified:true|block:23474478|tx:0x976cc8e6b3c83f841c0e0ef9035773f34548c9193a768b6a574089f086807d1b|first_check:1759224828

Submitted on: 2025-09-30 11:33:48

Comments

Log in to comment.

No comments yet.