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
pragma solidity 0.8.20;

/**
 * @title MultiSig
 * @dev Multi-signature contract that automatically allows registered contracts from Registry
 * Supports multiple pending transactions from different contracts simultaneously
 */
contract MultiSig {
    
    // =============================================================
    //                          STRUCTS
    // =============================================================
    
    struct Transaction {
        address targetContract;      // Contract to call
        bytes data;                 // Function call data
        uint256 approvals;          // Current approval count
        bool executed;              // Execution status
        address proposer;           // Who proposed the transaction
        uint256 timestamp;          // When proposed
        string description;         // Human readable description
    }
    
    // =============================================================
    //                          STATE VARIABLES
    // =============================================================
    
    /// @notice Array of the 5 authorized signers
    address[5] public signers;
    
    /// @notice Maps transaction ID to transaction details
    mapping(uint256 => Transaction) public transactions;
    
    /// @notice Maps transaction ID to signer address to approval status
    mapping(uint256 => mapping(address => bool)) public hasApproved;
    
    /// @notice Next transaction ID to use
    uint256 public nextTransactionId;
    
    /// @notice Minimum approvals required (3 out of 5)
    uint256 public constant REQUIRED_APPROVALS = 3;
    
    /// @notice Reference to the Registry contract
    address public registry;
    
    /// @notice Maps contract addresses that can delegate to this Multi-Sig
    mapping(address => bool) public authorizedContracts;
    
    // =============================================================
    //                          EVENTS
    // =============================================================
    
    /**
     * @notice Emitted when a transaction is proposed
     * @param txId The unique transaction ID
     * @param targetContract The contract the transaction will call
     * @param proposer The address that proposed the transaction
     * @param description Human readable description
     */
    event TransactionProposed(
        uint256 indexed txId,
        address indexed targetContract,
        address indexed proposer,
        string description
    );
    
    /**
     * @notice Emitted when a signer approves a transaction
     * @param txId The transaction ID
     * @param signer The address of the approving signer
     * @param approvalCount Current approval count
     */
    event TransactionApproved(
        uint256 indexed txId,
        address indexed signer,
        uint256 approvalCount
    );
    
    /**
     * @notice Emitted when a transaction is executed
     * @param txId The transaction ID
     * @param executor The address that executed the transaction
     * @param targetContract The contract that was called
     */
    event TransactionExecuted(
        uint256 indexed txId,
        address indexed executor,
        address indexed targetContract
    );
    
    /**
     * @notice Emitted when a contract is authorized to use Multi-Sig
     * @param contractAddress The address of the authorized contract
     * @param authorized Whether the contract is authorized
     */
    event ContractAuthorized(
        address indexed contractAddress,
        bool authorized
    );
    
    /**
     * @notice Emitted when signers are updated
     * @param oldSigners The previous array of signers
     * @param newSigners The new array of signers
     */
    event SignersUpdated(
        address[5] oldSigners,
        address[5] newSigners
    );
    
    // =============================================================
    //                          MODIFIERS
    // =============================================================
    
    /**
     * @dev Modifier to check if the caller is one of the authorized signers
     */
    modifier onlySigner() {
        require(isSigner(msg.sender), "MultiSig: Not authorized signer");
        _;
    }
    
    /**
     * @dev Modifier to check if the caller is an authorized contract
     */
    modifier onlyAuthorizedContract() {
        require(_isAuthorizedContract(msg.sender), "MultiSig: Not authorized contract");
        _;
    }
    
    /**
     * @dev Modifier to check if transaction exists and hasn't been executed
     */
    modifier validTransaction(uint256 txId) {
        require(txId < nextTransactionId, "MultiSig: Transaction does not exist");
        require(!transactions[txId].executed, "MultiSig: Transaction already executed");
        _;
    }
    
    // =============================================================
    //                          CONSTRUCTOR
    // =============================================================
    
    /**
     * @notice Initialize the multi-sig with 5 authorized signers and registry
     * @param _signer1 First authorized signer address
     * @param _signer2 Second authorized signer address
     * @param _signer3 Third authorized signer address
     * @param _signer4 Fourth authorized signer address
     * @param _signer5 Fifth authorized signer address
     * @param _registry Address of the Registry contract
     */
    constructor(
        address _signer1,
        address _signer2,
        address _signer3,
        address _signer4,
        address _signer5,
        address _registry
    ) {
        require(_signer1 != address(0), "MultiSig: Invalid signer1");
        require(_signer2 != address(0), "MultiSig: Invalid signer2");
        require(_signer3 != address(0), "MultiSig: Invalid signer3");
        require(_signer4 != address(0), "MultiSig: Invalid signer4");
        require(_signer5 != address(0), "MultiSig: Invalid signer5");

        require(_signer1 != _signer2 && _signer1 != _signer3 && _signer1 != _signer4 && _signer1 != _signer5 &&
                _signer2 != _signer3 && _signer2 != _signer4 && _signer2 != _signer5 &&
                _signer3 != _signer4 && _signer3 != _signer5 &&
                _signer4 != _signer5,
                "MultiSig: Duplicate signers not allowed");

        signers[0] = _signer1;
        signers[1] = _signer2;
        signers[2] = _signer3;
        signers[3] = _signer4;
        signers[4] = _signer5;
        registry = _registry;
        nextTransactionId = 1; // Start from 1 to avoid confusion with default values
    }
    
    // =============================================================
    //                          ADMIN FUNCTIONS
    // =============================================================
    
    /**
     * @notice Authorize a contract to delegate transactions to this Multi-Sig
     * @param contractAddress The address of the contract to authorize
     * @param authorized Whether to authorize or deauthorize
     */
    function setAuthorizedContract(address contractAddress, bool authorized) external onlySigner {
        require(contractAddress != address(0), "MultiSig: Invalid contract address");
        require(contractAddress != address(this), "MultiSig: Cannot authorize self");

        // This needs Multi-Sig approval too
        bytes memory data = abi.encodeWithSignature("_setAuthorizedContract(address,bool)", contractAddress, authorized);
        uint256 txId = _createTransaction(
            address(this),
            data,
            string(abi.encodePacked("Authorize contract: ", _addressToString(contractAddress)))
        );
        
        // Auto-approve from the proposer
        hasApproved[txId][msg.sender] = true;
        transactions[txId].approvals++;
        
        emit TransactionProposed(txId, address(this), msg.sender, string(abi.encodePacked("Authorize contract: ", _addressToString(contractAddress))));
        emit TransactionApproved(txId, msg.sender, transactions[txId].approvals);
    }
    
    /**
     * @dev Internal function to actually set authorized contract (called after Multi-Sig approval)
     */
    function _setAuthorizedContract(address contractAddress, bool authorized) external {
        require(msg.sender == address(this), "MultiSig: Only self can call");
        authorizedContracts[contractAddress] = authorized;
        emit ContractAuthorized(contractAddress, authorized);
    }

    /**
     * @notice Propose to replace the current signers with new ones (requires Multi-Sig approval)
     * @param _newSigner1 New first signer address
     * @param _newSigner2 New second signer address
     * @param _newSigner3 New third signer address
     * @param _newSigner4 New fourth signer address
     * @param _newSigner5 New fifth signer address
     */
    function proposeSignerReplacement(
        address _newSigner1,
        address _newSigner2,
        address _newSigner3,
        address _newSigner4,
        address _newSigner5
    ) external onlySigner {
        require(_newSigner1 != address(0), "MultiSig: Invalid signer1");
        require(_newSigner2 != address(0), "MultiSig: Invalid signer2");
        require(_newSigner3 != address(0), "MultiSig: Invalid signer3");
        require(_newSigner4 != address(0), "MultiSig: Invalid signer4");
        require(_newSigner5 != address(0), "MultiSig: Invalid signer5");

        require(_newSigner1 != _newSigner2 && _newSigner1 != _newSigner3 && _newSigner1 != _newSigner4 && _newSigner1 != _newSigner5 &&
                _newSigner2 != _newSigner3 && _newSigner2 != _newSigner4 && _newSigner2 != _newSigner5 &&
                _newSigner3 != _newSigner4 && _newSigner3 != _newSigner5 &&
                _newSigner4 != _newSigner5,
                "MultiSig: Duplicate signers not allowed");

        bytes memory data = abi.encodeWithSignature(
            "_replaceSigners(address,address,address,address,address)",
            _newSigner1,
            _newSigner2,
            _newSigner3,
            _newSigner4,
            _newSigner5
        );

        uint256 txId = _createTransaction(
            address(this),
            data,
            string(abi.encodePacked(
                "Replace signers with: ",
                _addressToString(_newSigner1), ", ",
                _addressToString(_newSigner2), ", ",
                _addressToString(_newSigner3), ", ",
                _addressToString(_newSigner4), ", ",
                _addressToString(_newSigner5)
            ))
        );
        
        // Auto-approve from the proposer
        hasApproved[txId][msg.sender] = true;
        transactions[txId].approvals++;
        
        emit TransactionProposed(
            txId, 
            address(this), 
            msg.sender, 
            "Replace current signers"
        );
        emit TransactionApproved(txId, msg.sender, transactions[txId].approvals);
    }

    /**
     * @dev Internal function to actually replace signers (called after Multi-Sig approval)
     */
    function _replaceSigners(
        address _newSigner1,
        address _newSigner2,
        address _newSigner3,
        address _newSigner4,
        address _newSigner5
    ) external {
        require(msg.sender == address(this), "MultiSig: Only self can call");

        address[5] memory oldSigners = signers;

        signers[0] = _newSigner1;
        signers[1] = _newSigner2;
        signers[2] = _newSigner3;
        signers[3] = _newSigner4;
        signers[4] = _newSigner5;

        emit SignersUpdated(oldSigners, signers);
    }
    
    // =============================================================
    //                          DELEGATION FUNCTIONS
    // =============================================================
    
    /**
     * @notice Called by authorized contracts to delegate a transaction to Multi-Sig
     * @param targetContract The contract to call after approval
     * @param data The function call data
     * @param description Human readable description of the transaction
     * @return txId The unique transaction ID for tracking
     */
    function delegateTransaction(
        address targetContract,
        bytes calldata data,
        string calldata description
    ) external onlyAuthorizedContract returns (uint256 txId) {
        require(targetContract != address(0), "MultiSig: Invalid target contract");
        require(data.length > 0, "MultiSig: Empty transaction data");
        require(bytes(description).length > 0, "MultiSig: Empty description");

        txId = _createTransaction(targetContract, data, description);

        emit TransactionProposed(txId, targetContract, msg.sender, description);

        return txId;
    }
    
    // =============================================================
    //                          APPROVAL FUNCTIONS
    // =============================================================
    
    /**
     * @notice Approve a pending transaction
     * @param txId The transaction ID to approve
     */
    function approveTransaction(uint256 txId) external onlySigner validTransaction(txId) {
        require(!hasApproved[txId][msg.sender], "MultiSig: Already approved");

        hasApproved[txId][msg.sender] = true;
        transactions[txId].approvals++;

        emit TransactionApproved(txId, msg.sender, transactions[txId].approvals);

        // Auto-execute if we have enough approvals
        if (transactions[txId].approvals >= REQUIRED_APPROVALS) {
            _executeTransaction(txId);
        }
    }
    
    /**
     * @notice Execute a transaction that has received sufficient approvals
     * @param txId The transaction ID to execute
     */
    function executeTransaction(uint256 txId) external onlySigner validTransaction(txId) {
        require(transactions[txId].approvals >= REQUIRED_APPROVALS, "MultiSig: Insufficient approvals");
        _executeTransaction(txId);
    }
    
    // =============================================================
    //                          INTERNAL FUNCTIONS
    // =============================================================
    
    /**
     * @dev Internal function to create a new transaction
     */
    function _createTransaction(
        address targetContract,
        bytes memory data,
        string memory description
    ) internal returns (uint256 txId) {
        txId = nextTransactionId++;
        
        transactions[txId] = Transaction({
            targetContract: targetContract,
            data: data,
            approvals: 0,
            executed: false,
            proposer: msg.sender,
            timestamp: block.timestamp,
            description: description
        });
        
        return txId;
    }
    
    /**
     * @dev Internal function to check if a contract is authorized
     * Checks manual authorization, Registry registration, and allows Registry itself
     */
    function _isAuthorizedContract(address contractAddress) internal view returns (bool) {
        // Check manual authorization first
        if (authorizedContracts[contractAddress]) {
            return true;
        }

        // Allow Registry itself to call
        if (registry != address(0) && contractAddress == registry) {
            return true;
        }

        // Check Registry registration if registry is set
        if (registry != address(0)) {
            // Call the Registry's isRegistered function
            (bool success, bytes memory data) = registry.staticcall(
                abi.encodeWithSignature("isRegistered(address)", contractAddress)
            );

            if (success && data.length == 32) {
                return abi.decode(data, (bool));
            }
        }

        return false;
    }
    
    /**
     * @dev Internal function to execute a transaction
     */
    function _executeTransaction(uint256 txId) internal {
        Transaction storage txn = transactions[txId];
        txn.executed = true;
        
        (bool success, bytes memory returnData) = txn.targetContract.call(txn.data);
        require(success, string(abi.encodePacked("MultiSig: Transaction failed: ", string(returnData))));
        
        emit TransactionExecuted(txId, msg.sender, txn.targetContract);
    }
    
    /**
     * @dev Convert address to string for descriptions
     */
    function _addressToString(address addr) internal pure returns (string memory) {
        bytes32 value = bytes32(uint256(uint160(addr)));
        bytes memory alphabet = "0123456789abcdef";
        bytes memory str = new bytes(42);
        str[0] = '0';
        str[1] = 'x';
        for (uint256 i = 0; i < 20; i++) {
            str[2+i*2] = alphabet[uint8(value[i + 12] >> 4)];
            str[3+i*2] = alphabet[uint8(value[i + 12] & 0x0f)];
        }
        return string(str);
    }
    
    // =============================================================
    //                          READ FUNCTIONS
    // =============================================================
    
    /**
     * @notice Check if an address is one of the authorized signers
     * @param _address The address to check
     * @return True if the address is a signer, false otherwise
     */
    function isSigner(address _address) public view returns (bool) {
        for (uint256 i = 0; i < 5; i++) {
            if (signers[i] == _address) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * @notice Get transaction details
     * @param txId The transaction ID
     * @return Transaction details
     */
    function getTransaction(uint256 txId) external view returns (Transaction memory) {
        require(txId < nextTransactionId, "MultiSig: Transaction does not exist");
        return transactions[txId];
    }
    
    /**
     * @notice Check if a signer has approved a specific transaction
     * @param txId The transaction ID
     * @param signer The address of the signer to check
     * @return True if the signer has approved, false otherwise
     */
    function hasSignerApproved(uint256 txId, address signer) external view returns (bool) {
        return hasApproved[txId][signer];
    }
    
    /**
     * @notice Get all pending transactions (not executed)
     * @return Array of transaction IDs that are pending
     */
    function getPendingTransactions() external view returns (uint256[] memory) {
        uint256[] memory pending = new uint256[](nextTransactionId - 1);
        uint256 count = 0;
        
        for (uint256 i = 1; i < nextTransactionId; i++) {
            if (!transactions[i].executed) {
                pending[count] = i;
                count++;
            }
        }
        
        // Resize array to actual count
        uint256[] memory result = new uint256[](count);
        for (uint256 i = 0; i < count; i++) {
            result[i] = pending[i];
        }
        
        return result;
    }
    
    /**
     * @notice Get all signer addresses
     * @return Array of the 5 signer addresses
     */
    function getSigners() external view returns (address[5] memory) {
        return signers;
    }
    
    /**
     * @notice Check if a contract is authorized to use this Multi-Sig
     * @param contractAddress The contract address to check
     * @return True if authorized, false otherwise
     */
    function isAuthorizedContract(address contractAddress) external view returns (bool) {
        return _isAuthorizedContract(contractAddress);
    }
}"
    },
    "contracts/utils/Registry.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./MultiSig.sol";

/**
 * @title Registry
 * @dev The bean registry contract for mapping contract names to addresses and tracking registration status.
 */
contract Registry is Ownable(msg.sender) {
    /// @notice Maps contract names to their addresses.
    mapping(string => address) public registry;
    /// @notice Tracks whether an address is registered.
    mapping(address => bool) public registered;

    // =============================================================
    //                          EVENTS
    // =============================================================

    /**
     * @notice Emitted when a contract address is set in the registry.
     * @param name The name of the contract.
     * @param contractAddress The address of the contract.
     * @param setter The address that set the registry entry.
     */
    event RegistryAddressSet(
        string indexed name,
        address indexed contractAddress,
        address indexed setter
    );


    /**
     * @notice Emitted when a transaction is delegated to Multi-Sig
     * @param txId The transaction ID from Multi-Sig
     * @param caller The address that initiated the call
     * @param functionName The function being called
     */
    event TransactionDelegated(
        uint256 indexed txId,
        address indexed caller,
        string functionName
    );

    // =============================================================
    //                          WRITE FUNCTIONS
    // =============================================================

    /**
     * @notice Gets the MultiSig contract instance from registry
     * @return The MultiSig contract instance, or address(0) if not set
     */
    function _getMultiSig() internal view returns (MultiSig) {
        address multiSigAddress = registry["MultiSig"];
        if (multiSigAddress == address(0)) {
            return MultiSig(address(0));
        }
        return MultiSig(multiSigAddress);
    }

    /**
     * @notice Sets the contract address for a given name.
     * @dev Delegates to Multi-Sig if called by user, executes directly if called by Multi-Sig.
     * @param _name The name of the contract.
     * @param _address The address associated with the contract.
     */
    function setContractAddress(
        string memory _name,
        address _address
    ) external {
        MultiSig multiSig = _getMultiSig();
        if (address(multiSig) != address(0) && msg.sender != address(multiSig)) {
            // User called this function - delegate to Multi-Sig
            bytes memory data = abi.encodeWithSignature("setContractAddress(string,address)", _name, _address);
            string memory description = string(abi.encodePacked("Set contract address: ", _name));

            uint256 txId = multiSig.delegateTransaction(address(this), data, description);

            emit TransactionDelegated(txId, msg.sender, "setContractAddress");
            return;
        }

        if (address(multiSig) == address(0)) {
            require(msg.sender == owner(), "Registry: Only owner can call when Multi-Sig not set");
        } else {
            require(msg.sender == address(multiSig), "Registry: Only Multi-Sig can call this function");
        }

        require(_address != address(0), "Registry: Cannot set zero address");
        require(bytes(_name).length > 0, "Registry: Contract name cannot be empty");

        address existingAddress = registry[_name];

        if (existingAddress != address(0) && existingAddress != _address) {
            registered[existingAddress] = false;
        }

        registry[_name] = _address;
        registered[_address] = true;

        emit RegistryAddressSet(_name, _address, msg.sender);
    }

    /**
     * @notice Toggles the registration status of an address.
     * @dev Delegates to Multi-Sig if called by user, executes directly if called by Multi-Sig.
     * @param _address The address to toggle registration status.
     */
    function toggleRegistration(address _address) external {
        MultiSig multiSig = _getMultiSig();
        if (address(multiSig) != address(0) && msg.sender != address(multiSig)) {
            bytes memory data = abi.encodeWithSignature("toggleRegistration(address)", _address);
            string memory description = string(abi.encodePacked("Toggle registration for: ", _addressToString(_address)));

            uint256 txId = multiSig.delegateTransaction(address(this), data, description);

            emit TransactionDelegated(txId, msg.sender, "toggleRegistration");
            return;
        }

        if (address(multiSig) == address(0)) {
            require(msg.sender == owner(), "Registry: Only owner can call when Multi-Sig not set");
        } else {
            require(msg.sender == address(multiSig), "Registry: Only Multi-Sig can call this function");
        }

        require(_address != address(0), "Registry: Cannot toggle registration for zero address");

        registered[_address] = !registered[_address];
    }

    // =============================================================
    //                          READ FUNCTIONS
    // =============================================================

    /**
     * @notice Retrieves the contract address associated with a given name.
     * @param _name The name of the contract.
     * @return The address associated with the provided name.
     */
    function getContractAddress(
        string memory _name
    ) external view returns (address) {
        require(
            registry[_name] != address(0),
            string(abi.encodePacked("Registry: Does not exist ", _name))
        );
        return registry[_name];
    }

    /**
     * @notice Checks if a given address is registered.
     * @param _address The address to check.
     * @return True if the address is registered, false otherwise.
     */
    function isRegistered(address _address) external view returns (bool) {
        return registered[_address];
    }

    /**
     * @notice Get the Multi-Sig contract address
     * @return The address of the Multi-Sig contract
     */
    function getMultiSig() external view returns (address) {
        return registry["MultiSig"];
    }

    // =============================================================
    //                          UTILITY FUNCTIONS
    // =============================================================

    /**
     * @dev Convert address to string for descriptions
     */
    function _addressToString(address addr) internal pure returns (string memory) {
        bytes32 value = bytes32(uint256(uint160(addr)));
        bytes memory alphabet = "0123456789abcdef";
        bytes memory str = new bytes(42);
        str[0] = '0';
        str[1] = 'x';
        for (uint256 i = 0; i < 20; i++) {
            str[2+i*2] = alphabet[uint8(value[i + 12] >> 4)];
            str[3+i*2] = alphabet[uint8(value[i + 12] & 0x0f)];
        }
        return string(str);
    }
}"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "evmVersion": "paris",
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
Multisig, Voting, Multi-Signature, Factory|addr:0x69d57e9677164d8486101ebeb4cb7e752f69d315|verified:true|block:23446005|tx:0x9a4d8641bbf7550d36b8f9aa77682dfb4acdecedd7712b3380f6a93286ee5f0d|first_check:1758879169

Submitted on: 2025-09-26 11:32:52

Comments

Log in to comment.

No comments yet.