Registry

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": {
    "@openzeppelin/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "@openzeppelin/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }
}
"
    },
    "contracts/utils/MultiSig.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0\r
pragma solidity 0.8.20;\r
\r
/**\r
 * @title MultiSig\r
 * @dev Multi-signature contract that automatically allows registered contracts from Registry\r
 * Supports multiple pending transactions from different contracts simultaneously\r
 */\r
contract MultiSig {\r
    \r
    // =============================================================\r
    //                          STRUCTS\r
    // =============================================================\r
    \r
    struct Transaction {\r
        address targetContract;      // Contract to call\r
        bytes data;                 // Function call data\r
        uint256 approvals;          // Current approval count\r
        bool executed;              // Execution status\r
        address proposer;           // Who proposed the transaction\r
        uint256 timestamp;          // When proposed\r
        string description;         // Human readable description\r
    }\r
    \r
    // =============================================================\r
    //                          STATE VARIABLES\r
    // =============================================================\r
    \r
    /// @notice Array of the 5 authorized signers\r
    address[5] public signers;\r
    \r
    /// @notice Maps transaction ID to transaction details\r
    mapping(uint256 => Transaction) public transactions;\r
    \r
    /// @notice Maps transaction ID to signer address to approval status\r
    mapping(uint256 => mapping(address => bool)) public hasApproved;\r
    \r
    /// @notice Next transaction ID to use\r
    uint256 public nextTransactionId;\r
    \r
    /// @notice Minimum approvals required (3 out of 5)\r
    uint256 public constant REQUIRED_APPROVALS = 3;\r
    \r
    /// @notice Reference to the Registry contract\r
    address public registry;\r
    \r
    /// @notice Maps contract addresses that can delegate to this Multi-Sig\r
    mapping(address => bool) public authorizedContracts;\r
    \r
    // =============================================================\r
    //                          EVENTS\r
    // =============================================================\r
    \r
    /**\r
     * @notice Emitted when a transaction is proposed\r
     * @param txId The unique transaction ID\r
     * @param targetContract The contract the transaction will call\r
     * @param proposer The address that proposed the transaction\r
     * @param description Human readable description\r
     */\r
    event TransactionProposed(\r
        uint256 indexed txId,\r
        address indexed targetContract,\r
        address indexed proposer,\r
        string description\r
    );\r
    \r
    /**\r
     * @notice Emitted when a signer approves a transaction\r
     * @param txId The transaction ID\r
     * @param signer The address of the approving signer\r
     * @param approvalCount Current approval count\r
     */\r
    event TransactionApproved(\r
        uint256 indexed txId,\r
        address indexed signer,\r
        uint256 approvalCount\r
    );\r
    \r
    /**\r
     * @notice Emitted when a transaction is executed\r
     * @param txId The transaction ID\r
     * @param executor The address that executed the transaction\r
     * @param targetContract The contract that was called\r
     */\r
    event TransactionExecuted(\r
        uint256 indexed txId,\r
        address indexed executor,\r
        address indexed targetContract\r
    );\r
    \r
    /**\r
     * @notice Emitted when a contract is authorized to use Multi-Sig\r
     * @param contractAddress The address of the authorized contract\r
     * @param authorized Whether the contract is authorized\r
     */\r
    event ContractAuthorized(\r
        address indexed contractAddress,\r
        bool authorized\r
    );\r
    \r
    /**\r
     * @notice Emitted when signers are updated\r
     * @param oldSigners The previous array of signers\r
     * @param newSigners The new array of signers\r
     */\r
    event SignersUpdated(\r
        address[5] oldSigners,\r
        address[5] newSigners\r
    );\r
    \r
    // =============================================================\r
    //                          MODIFIERS\r
    // =============================================================\r
    \r
    /**\r
     * @dev Modifier to check if the caller is one of the authorized signers\r
     */\r
    modifier onlySigner() {\r
        require(isSigner(msg.sender), "MultiSig: Not authorized signer");\r
        _;\r
    }\r
    \r
    /**\r
     * @dev Modifier to check if the caller is an authorized contract\r
     */\r
    modifier onlyAuthorizedContract() {\r
        require(_isAuthorizedContract(msg.sender), "MultiSig: Not authorized contract");\r
        _;\r
    }\r
    \r
    /**\r
     * @dev Modifier to check if transaction exists and hasn't been executed\r
     */\r
    modifier validTransaction(uint256 txId) {\r
        require(txId < nextTransactionId, "MultiSig: Transaction does not exist");\r
        require(!transactions[txId].executed, "MultiSig: Transaction already executed");\r
        _;\r
    }\r
    \r
    // =============================================================\r
    //                          CONSTRUCTOR\r
    // =============================================================\r
    \r
    /**\r
     * @notice Initialize the multi-sig with 5 authorized signers and registry\r
     * @param _signer1 First authorized signer address\r
     * @param _signer2 Second authorized signer address\r
     * @param _signer3 Third authorized signer address\r
     * @param _signer4 Fourth authorized signer address\r
     * @param _signer5 Fifth authorized signer address\r
     * @param _registry Address of the Registry contract\r
     */\r
    constructor(\r
        address _signer1,\r
        address _signer2,\r
        address _signer3,\r
        address _signer4,\r
        address _signer5,\r
        address _registry\r
    ) {\r
        require(_signer1 != address(0), "MultiSig: Invalid signer1");\r
        require(_signer2 != address(0), "MultiSig: Invalid signer2");\r
        require(_signer3 != address(0), "MultiSig: Invalid signer3");\r
        require(_signer4 != address(0), "MultiSig: Invalid signer4");\r
        require(_signer5 != address(0), "MultiSig: Invalid signer5");\r
\r
        require(_signer1 != _signer2 && _signer1 != _signer3 && _signer1 != _signer4 && _signer1 != _signer5 &&\r
                _signer2 != _signer3 && _signer2 != _signer4 && _signer2 != _signer5 &&\r
                _signer3 != _signer4 && _signer3 != _signer5 &&\r
                _signer4 != _signer5,\r
                "MultiSig: Duplicate signers not allowed");\r
\r
        signers[0] = _signer1;\r
        signers[1] = _signer2;\r
        signers[2] = _signer3;\r
        signers[3] = _signer4;\r
        signers[4] = _signer5;\r
        registry = _registry;\r
        nextTransactionId = 1; // Start from 1 to avoid confusion with default values\r
    }\r
    \r
    // =============================================================\r
    //                          ADMIN FUNCTIONS\r
    // =============================================================\r
    \r
    /**\r
     * @notice Authorize a contract to delegate transactions to this Multi-Sig\r
     * @param contractAddress The address of the contract to authorize\r
     * @param authorized Whether to authorize or deauthorize\r
     */\r
    function setAuthorizedContract(address contractAddress, bool authorized) external onlySigner {\r
        require(contractAddress != address(0), "MultiSig: Invalid contract address");\r
        require(contractAddress != address(this), "MultiSig: Cannot authorize self");\r
\r
        // This needs Multi-Sig approval too\r
        bytes memory data = abi.encodeWithSignature("_setAuthorizedContract(address,bool)", contractAddress, authorized);\r
        uint256 txId = _createTransaction(\r
            address(this),\r
            data,\r
            string(abi.encodePacked("Authorize contract: ", _addressToString(contractAddress)))\r
        );\r
        \r
        // Auto-approve from the proposer\r
        hasApproved[txId][msg.sender] = true;\r
        transactions[txId].approvals++;\r
        \r
        emit TransactionProposed(txId, address(this), msg.sender, string(abi.encodePacked("Authorize contract: ", _addressToString(contractAddress))));\r
        emit TransactionApproved(txId, msg.sender, transactions[txId].approvals);\r
    }\r
    \r
    /**\r
     * @dev Internal function to actually set authorized contract (called after Multi-Sig approval)\r
     */\r
    function _setAuthorizedContract(address contractAddress, bool authorized) external {\r
        require(msg.sender == address(this), "MultiSig: Only self can call");\r
        authorizedContracts[contractAddress] = authorized;\r
        emit ContractAuthorized(contractAddress, authorized);\r
    }\r
\r
    /**\r
     * @notice Propose to replace the current signers with new ones (requires Multi-Sig approval)\r
     * @param _newSigner1 New first signer address\r
     * @param _newSigner2 New second signer address\r
     * @param _newSigner3 New third signer address\r
     * @param _newSigner4 New fourth signer address\r
     * @param _newSigner5 New fifth signer address\r
     */\r
    function proposeSignerReplacement(\r
        address _newSigner1,\r
        address _newSigner2,\r
        address _newSigner3,\r
        address _newSigner4,\r
        address _newSigner5\r
    ) external onlySigner {\r
        require(_newSigner1 != address(0), "MultiSig: Invalid signer1");\r
        require(_newSigner2 != address(0), "MultiSig: Invalid signer2");\r
        require(_newSigner3 != address(0), "MultiSig: Invalid signer3");\r
        require(_newSigner4 != address(0), "MultiSig: Invalid signer4");\r
        require(_newSigner5 != address(0), "MultiSig: Invalid signer5");\r
\r
        require(_newSigner1 != _newSigner2 && _newSigner1 != _newSigner3 && _newSigner1 != _newSigner4 && _newSigner1 != _newSigner5 &&\r
                _newSigner2 != _newSigner3 && _newSigner2 != _newSigner4 && _newSigner2 != _newSigner5 &&\r
                _newSigner3 != _newSigner4 && _newSigner3 != _newSigner5 &&\r
                _newSigner4 != _newSigner5,\r
                "MultiSig: Duplicate signers not allowed");\r
\r
        bytes memory data = abi.encodeWithSignature(\r
            "_replaceSigners(address,address,address,address,address)",\r
            _newSigner1,\r
            _newSigner2,\r
            _newSigner3,\r
            _newSigner4,\r
            _newSigner5\r
        );\r
\r
        uint256 txId = _createTransaction(\r
            address(this),\r
            data,\r
            string(abi.encodePacked(\r
                "Replace signers with: ",\r
                _addressToString(_newSigner1), ", ",\r
                _addressToString(_newSigner2), ", ",\r
                _addressToString(_newSigner3), ", ",\r
                _addressToString(_newSigner4), ", ",\r
                _addressToString(_newSigner5)\r
            ))\r
        );\r
        \r
        // Auto-approve from the proposer\r
        hasApproved[txId][msg.sender] = true;\r
        transactions[txId].approvals++;\r
        \r
        emit TransactionProposed(\r
            txId, \r
            address(this), \r
            msg.sender, \r
            "Replace current signers"\r
        );\r
        emit TransactionApproved(txId, msg.sender, transactions[txId].approvals);\r
    }\r
\r
    /**\r
     * @dev Internal function to actually replace signers (called after Multi-Sig approval)\r
     */\r
    function _replaceSigners(\r
        address _newSigner1,\r
        address _newSigner2,\r
        address _newSigner3,\r
        address _newSigner4,\r
        address _newSigner5\r
    ) external {\r
        require(msg.sender == address(this), "MultiSig: Only self can call");\r
\r
        address[5] memory oldSigners = signers;\r
\r
        signers[0] = _newSigner1;\r
        signers[1] = _newSigner2;\r
        signers[2] = _newSigner3;\r
        signers[3] = _newSigner4;\r
        signers[4] = _newSigner5;\r
\r
        emit SignersUpdated(oldSigners, signers);\r
    }\r
    \r
    // =============================================================\r
    //                          DELEGATION FUNCTIONS\r
    // =============================================================\r
    \r
    /**\r
     * @notice Called by authorized contracts to delegate a transaction to Multi-Sig\r
     * @param targetContract The contract to call after approval\r
     * @param data The function call data\r
     * @param description Human readable description of the transaction\r
     * @return txId The unique transaction ID for tracking\r
     */\r
    function delegateTransaction(\r
        address targetContract,\r
        bytes calldata data,\r
        string calldata description\r
    ) external onlyAuthorizedContract returns (uint256 txId) {\r
        require(targetContract != address(0), "MultiSig: Invalid target contract");\r
        require(data.length > 0, "MultiSig: Empty transaction data");\r
        require(bytes(description).length > 0, "MultiSig: Empty description");\r
\r
        txId = _createTransaction(targetContract, data, description);\r
\r
        emit TransactionProposed(txId, targetContract, msg.sender, description);\r
\r
        return txId;\r
    }\r
    \r
    // =============================================================\r
    //                          APPROVAL FUNCTIONS\r
    // =============================================================\r
    \r
    /**\r
     * @notice Approve a pending transaction\r
     * @param txId The transaction ID to approve\r
     */\r
    function approveTransaction(uint256 txId) external onlySigner validTransaction(txId) {\r
        require(!hasApproved[txId][msg.sender], "MultiSig: Already approved");\r
\r
        hasApproved[txId][msg.sender] = true;\r
        transactions[txId].approvals++;\r
\r
        emit TransactionApproved(txId, msg.sender, transactions[txId].approvals);\r
\r
        // Auto-execute if we have enough approvals\r
        if (transactions[txId].approvals >= REQUIRED_APPROVALS) {\r
            _executeTransaction(txId);\r
        }\r
    }\r
    \r
    /**\r
     * @notice Execute a transaction that has received sufficient approvals\r
     * @param txId The transaction ID to execute\r
     */\r
    function executeTransaction(uint256 txId) external onlySigner validTransaction(txId) {\r
        require(transactions[txId].approvals >= REQUIRED_APPROVALS, "MultiSig: Insufficient approvals");\r
        _executeTransaction(txId);\r
    }\r
    \r
    // =============================================================\r
    //                          INTERNAL FUNCTIONS\r
    // =============================================================\r
    \r
    /**\r
     * @dev Internal function to create a new transaction\r
     */\r
    function _createTransaction(\r
        address targetContract,\r
        bytes memory data,\r
        string memory description\r
    ) internal returns (uint256 txId) {\r
        txId = nextTransactionId++;\r
        \r
        transactions[txId] = Transaction({\r
            targetContract: targetContract,\r
            data: data,\r
            approvals: 0,\r
            executed: false,\r
            proposer: msg.sender,\r
            timestamp: block.timestamp,\r
            description: description\r
        });\r
        \r
        return txId;\r
    }\r
    \r
    /**\r
     * @dev Internal function to check if a contract is authorized\r
     * Checks manual authorization, Registry registration, and allows Registry itself\r
     */\r
    function _isAuthorizedContract(address contractAddress) internal view returns (bool) {\r
        // Check manual authorization first\r
        if (authorizedContracts[contractAddress]) {\r
            return true;\r
        }\r
\r
        // Allow Registry itself to call\r
        if (registry != address(0) && contractAddress == registry) {\r
            return true;\r
        }\r
\r
        // Check Registry registration if registry is set\r
        if (registry != address(0)) {\r
            // Call the Registry's isRegistered function\r
            (bool success, bytes memory data) = registry.staticcall(\r
                abi.encodeWithSignature("isRegistered(address)", contractAddress)\r
            );\r
\r
            if (success && data.length == 32) {\r
                return abi.decode(data, (bool));\r
            }\r
        }\r
\r
        return false;\r
    }\r
    \r
    /**\r
     * @dev Internal function to execute a transaction\r
     */\r
    function _executeTransaction(uint256 txId) internal {\r
        Transaction storage txn = transactions[txId];\r
        txn.executed = true;\r
        \r
        (bool success, bytes memory returnData) = txn.targetContract.call(txn.data);\r
        require(success, string(abi.encodePacked("MultiSig: Transaction failed: ", string(returnData))));\r
        \r
        emit TransactionExecuted(txId, msg.sender, txn.targetContract);\r
    }\r
    \r
    /**\r
     * @dev Convert address to string for descriptions\r
     */\r
    function _addressToString(address addr) internal pure returns (string memory) {\r
        bytes32 value = bytes32(uint256(uint160(addr)));\r
        bytes memory alphabet = "0123456789abcdef";\r
        bytes memory str = new bytes(42);\r
        str[0] = '0';\r
        str[1] = 'x';\r
        for (uint256 i = 0; i < 20; i++) {\r
            str[2+i*2] = alphabet[uint8(value[i + 12] >> 4)];\r
            str[3+i*2] = alphabet[uint8(value[i + 12] & 0x0f)];\r
        }\r
        return string(str);\r
    }\r
    \r
    // =============================================================\r
    //                          READ FUNCTIONS\r
    // =============================================================\r
    \r
    /**\r
     * @notice Check if an address is one of the authorized signers\r
     * @param _address The address to check\r
     * @return True if the address is a signer, false otherwise\r
     */\r
    function isSigner(address _address) public view returns (bool) {\r
        for (uint256 i = 0; i < 5; i++) {\r
            if (signers[i] == _address) {\r
                return true;\r
            }\r
        }\r
        return false;\r
    }\r
    \r
    /**\r
     * @notice Get transaction details\r
     * @param txId The transaction ID\r
     * @return Transaction details\r
     */\r
    function getTransaction(uint256 txId) external view returns (Transaction memory) {\r
        require(txId < nextTransactionId, "MultiSig: Transaction does not exist");\r
        return transactions[txId];\r
    }\r
    \r
    /**\r
     * @notice Check if a signer has approved a specific transaction\r
     * @param txId The transaction ID\r
     * @param signer The address of the signer to check\r
     * @return True if the signer has approved, false otherwise\r
     */\r
    function hasSignerApproved(uint256 txId, address signer) external view returns (bool) {\r
        return hasApproved[txId][signer];\r
    }\r
    \r
    /**\r
     * @notice Get all pending transactions (not executed)\r
     * @return Array of transaction IDs that are pending\r
     */\r
    function getPendingTransactions() external view returns (uint256[] memory) {\r
        uint256[] memory pending = new uint256[](nextTransactionId - 1);\r
        uint256 count = 0;\r
        \r
        for (uint256 i = 1; i < nextTransactionId; i++) {\r
            if (!transactions[i].executed) {\r
                pending[count] = i;\r
                count++;\r
            }\r
        }\r
        \r
        // Resize array to actual count\r
        uint256[] memory result = new uint256[](count);\r
        for (uint256 i = 0; i < count; i++) {\r
            result[i] = pending[i];\r
        }\r
        \r
        return result;\r
    }\r
    \r
    /**\r
     * @notice Get all signer addresses\r
     * @return Array of the 5 signer addresses\r
     */\r
    function getSigners() external view returns (address[5] memory) {\r
        return signers;\r
    }\r
    \r
    /**\r
     * @notice Check if a contract is authorized to use this Multi-Sig\r
     * @param contractAddress The contract address to check\r
     * @return True if authorized, false otherwise\r
     */\r
    function isAuthorizedContract(address contractAddress) external view returns (bool) {\r
        return _isAuthorizedContract(contractAddress);\r
    }\r
}"
    },
    "contracts/utils/Registry.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0\r
pragma solidity 0.8.20;\r
\r
import "@openzeppelin/contracts/access/Ownable.sol";\r
import "./MultiSig.sol";\r
\r
/**\r
 * @title Registry\r
 * @dev The bean registry contract for mapping contract names to addresses and tracking registration status.\r
 */\r
contract Registry is Ownable(msg.sender) {\r
    /// @notice Maps contract names to their addresses.\r
    mapping(string => address) public registry;\r
    /// @notice Tracks whether an address is registered.\r
    mapping(address => bool) public registered;\r
\r
    // =============================================================\r
    //                          EVENTS\r
    // =============================================================\r
\r
    /**\r
     * @notice Emitted when a contract address is set in the registry.\r
     * @param name The name of the contract.\r
     * @param contractAddress The address of the contract.\r
     * @param setter The address that set the registry entry.\r
     */\r
    event RegistryAddressSet(\r
        string indexed name,\r
        address indexed contractAddress,\r
        address indexed setter\r
    );\r
\r
\r
    /**\r
     * @notice Emitted when a transaction is delegated to Multi-Sig\r
     * @param txId The transaction ID from Multi-Sig\r
     * @param caller The address that initiated the call\r
     * @param functionName The function being called\r
     */\r
    event TransactionDelegated(\r
        uint256 indexed txId,\r
        address indexed caller,\r
        string functionName\r
    );\r
\r
    // =============================================================\r
    //                          WRITE FUNCTIONS\r
    // =============================================================\r
\r
    /**\r
     * @notice Gets the MultiSig contract instance from registry\r
     * @return The MultiSig contract instance, or address(0) if not set\r
     */\r
    function _getMultiSig() internal view returns (MultiSig) {\r
        address multiSigAddress = registry["MultiSig"];\r
        if (multiSigAddress == address(0)) {\r
            return MultiSig(address(0));\r
        }\r
        return MultiSig(multiSigAddress);\r
    }\r
\r
    /**\r
     * @notice Sets the contract address for a given name.\r
     * @dev Delegates to Multi-Sig if called by user, executes directly if called by Multi-Sig.\r
     * @param _name The name of the contract.\r
     * @param _address The address associated with the contract.\r
     */\r
    function setContractAddress(\r
        string memory _name,\r
        address _address\r
    ) external {\r
        MultiSig multiSig = _getMultiSig();\r
        if (address(multiSig) != address(0) && msg.sender != address(multiSig)) {\r
            // User called this function - delegate to Multi-Sig\r
            bytes memory data = abi.encodeWithSignature("setContractAddress(string,address)", _name, _address);\r
            string memory description = string(abi.encodePacked("Set contract address: ", _name));\r
\r
            uint256 txId = multiSig.delegateTransaction(address(this), data, description);\r
\r
            emit TransactionDelegated(txId, msg.sender, "setContractAddress");\r
            return;\r
        }\r
\r
        if (address(multiSig) == address(0)) {\r
            require(msg.sender == owner(), "Registry: Only owner can call when Multi-Sig not set");\r
        } else {\r
            require(msg.sender == address(multiSig), "Registry: Only Multi-Sig can call this function");\r
        }\r
\r
        require(_address != address(0), "Registry: Cannot set zero address");\r
        require(bytes(_name).length > 0, "Registry: Contract name cannot be empty");\r
\r
        address existingAddress = registry[_name];\r
\r
        if (existingAddress != address(0) && existingAddress != _address) {\r
            registered[existingAddress] = false;\r
        }\r
\r
        registry[_name] = _address;\r
        registered[_address] = true;\r
\r
        emit RegistryAddressSet(_name, _address, msg.sender);\r
    }\r
\r
    /**\r
     * @notice Toggles the registration status of an address.\r
     * @dev Delegates to Multi-Sig if called by user, executes directly if called by Multi-Sig.\r
     * @param _address The address to toggle registration status.\r
     */\r
    function toggleRegistration(address _address) external {\r
        MultiSig multiSig = _getMultiSig();\r
        if (address(multiSig) != address(0) && msg.sender != address(multiSig)) {\r
            bytes memory data = abi.encodeWithSignature("toggleRegistration(address)", _address);\r
            string memory description = string(abi.encodePacked("Toggle registration for: ", _addressToString(_address)));\r
\r
            uint256 txId = multiSig.delegateTransaction(address(this), data, description);\r
\r
            emit TransactionDelegated(txId, msg.sender, "toggleRegistration");\r
            return;\r
        }\r
\r
        if (address(multiSig) == address(0)) {\r
            require(msg.sender == owner(), "Registry: Only owner can call when Multi-Sig not set");\r
        } else {\r
            require(msg.sender == address(multiSig), "Registry: Only Multi-Sig can call this function");\r
        }\r
\r
        require(_address != address(0), "Registry: Cannot toggle registration for zero address");\r
\r
        registered[_address] = !registered[_address];\r
    }\r
\r
    // =============================================================\r
    //                          READ FUNCTIONS\r
    // =============================================================\r
\r
    /**\r
     * @notice Retrieves the contract address associated with a given name.\r
     * @param _name The name of the contract.\r
     * @return The address associated with the provided name.\r
     */\r
    function getContractAddress(\r
        string memory _name\r
    ) external view returns (address) {\r
        require(\r
            registry[_name] != address(0),\r
            string(abi.encodePacked("Registry: Does not exist ", _name))\r
        );\r
        return registry[_name];\r
    }\r
\r
    /**\r
     * @notice Checks if a given address is registered.\r
     * @param _address The address to check.\r
     * @return True if the address is registered, false otherwise.\r
     */\r
    function isRegistered(address _address) external view returns (bool) {\r
        return registered[_address];\r
    }\r
\r
    /**\r
     * @notice Get the Multi-Sig contract address\r
     * @return The address of the Multi-Sig contract\r
     */\r
    function getMultiSig() external view returns (address) {\r
        return registry["MultiSig"];\r
    }\r
\r
    // =============================================================\r
    //                          UTILITY FUNCTIONS\r
    // =============================================================\r
\r
    /**\r
     * @dev Convert address to string for descriptions\r
     */\r
    function _addressToString(address addr) internal pure returns (string memory) {\r
        bytes32 value = bytes32(uint256(uint160(addr)));\r
        bytes memory alphabet = "0123456789abcdef";\r
        bytes memory str = new bytes(42);\r
        str[0] = '0';\r
        str[1] = 'x';\r
        for (uint256 i = 0; i < 20; i++) {\r
            str[2+i*2] = alphabet[uint8(value[i + 12] >> 4)];\r
            str[3+i*2] = alphabet[uint8(value[i + 12] & 0x0f)];\r
        }\r
        return string(str);\r
    }\r
}"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "evmVersion": "paris",
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
Multisig, Voting, Multi-Signature, Factory|addr:0x937917d9fa9de1ef0a01a1f20298d816d255dc7c|verified:true|block:23452880|tx:0x9db477768f805caf95b164ce95a968818a4afd1e38c93d1c90a0993f478d4916|first_check:1758967986

Submitted on: 2025-09-27 12:13:07

Comments

Log in to comment.

No comments yet.