ETH Price: $3,704.56 (-6.11%)

Contract

0x36aacA9FCFAdecC309Fd0C82D247c374ab83D9b5
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
187265242023-12-06 9:55:11369 days ago1701856511  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SafeModerator

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 20000 runs

Other Settings:
paris EvmVersion
File 1 of 16 : SafeModerator.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {IERC165, Guard, Enum} from "interfaces/external/ISafeWallet.sol";
import {AddressProviderService} from "src/core/AddressProviderService.sol";
import {TransactionValidator} from "src/core/TransactionValidator.sol";

/**
 * @title SafeModerator
 * @author Brahma.fi
 * @notice A guard that validates transactions and allows only policy abiding txns, on Brahma console account
 */
contract SafeModerator is AddressProviderService, Guard {
    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /**
     * @notice Inherited from Guard, function is called before executing a Safe transaction during execTransaction
     * @param to target address
     * @param value txn value
     * @param data txn callData
     * @param operation type of operation
     * @param safeTxGas gas that should be used for safe txn
     * @param baseGas gas cost independent of txn cost
     * @param gasPrice gas price in current block
     * @param gasToken address of token used for gas
     * @param refundReceiver address of receiver of gas payment
     * @param signatures user signatures appended with validation signature
     * @param msgSender address of msg.sender of original txn
     */
    function checkTransaction(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address payable refundReceiver,
        bytes memory signatures,
        address msgSender
    ) external view override {
        TransactionValidator(AddressProviderService._getAuthorizedAddress(_TRANSACTION_VALIDATOR_HASH))
            .validatePreTransaction(
            TransactionValidator.SafeTransactionParams({
                from: msg.sender,
                to: to,
                value: value,
                data: data,
                operation: operation,
                safeTxGas: safeTxGas,
                baseGas: baseGas,
                gasPrice: gasPrice,
                gasToken: gasToken,
                refundReceiver: refundReceiver,
                signatures: signatures,
                msgSender: msgSender
            })
        );
    }

    /**
     * @notice Inherited from Guard, function is called after executing a Safe transaction during execTransaction
     * @param txHash tx hash, computed from transaction
     * @param success boolean indicating success
     */
    function checkAfterExecution(bytes32 txHash, bool success) external view override {
        TransactionValidator(AddressProviderService._getAuthorizedAddress(_TRANSACTION_VALIDATOR_HASH))
            .validatePostTransaction(txHash, success, msg.sender);
    }

    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
        return interfaceId == type(Guard).interfaceId || interfaceId == type(IERC165).interfaceId;
    }
}

File 2 of 16 : ISafeWallet.sol
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.19;

import {IERC165} from "openzeppelin-contracts/utils/introspection/IERC165.sol";

/// @title Enum - Collection of enums
/// @author Richard Meissner - <[email protected]>
contract Enum {
    enum Operation {
        Call,
        DelegateCall
    }
}

interface ISafeWallet {
    /// @dev Allows a Module to execute a Safe transaction without any further confirmations.
    /// @param to Destination address of module transaction.
    /// @param value Ether value of module transaction.
    /// @param data Data payload of module transaction.
    /// @param operation Operation type of module transaction.
    function execTransactionFromModule(address to, uint256 value, bytes calldata data, Enum.Operation operation)
        external
        returns (bool success);

    /// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data
    /// @param to Destination address of module transaction.
    /// @param value Ether value of module transaction.
    /// @param data Data payload of module transaction.
    /// @param operation Operation type of module transaction.
    function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation)
        external
        returns (bool success, bytes memory returnData);
    function getStorageAt(uint256 offset, uint256 length) external view returns (bytes memory);
    function isOwner(address owner) external view returns (bool);
    function nonce() external view returns (uint256);
    function getThreshold() external view returns (uint256);
    function isModuleEnabled(address module) external view returns (bool);
    function enableModule(address module) external;
    function disableModule(address prevModule, address module) external;
    function removeOwner(address prevOwner, address owner, uint256 _threshold) external;
    function swapOwner(address prevOwner, address oldOwner, address newOwner) external;
    function getOwners() external view returns (address[] memory);
    function approveHash(bytes32 hashToApprove) external;
    function signedMessages(bytes32 _dataHash) external returns (uint256 _signatures);
    function execTransaction(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address payable refundReceiver,
        bytes memory signatures
    ) external payable returns (bool);

    function setup(
        address[] memory _owners,
        uint256 _threshold,
        address to,
        bytes memory data,
        address fallbackHandler,
        address paymentToken,
        uint256 payment,
        address paymentReceiver
    ) external;

    function addOwnerWithThreshold(address owner, uint256 _threshold) external;
    function domainSeparator() external view returns (bytes32);
    function setFallbackHandler(address _fallbackHandler) external;
    function setGuard(address guard) external;
    function encodeTransactionData(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    ) external view returns (bytes memory);
}

interface Guard is IERC165 {
    function checkTransaction(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address payable refundReceiver,
        bytes memory signatures,
        address msgSender
    ) external;

    function checkAfterExecution(bytes32 txHash, bool success) external;
}

File 3 of 16 : AddressProviderService.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {IAddressProviderService} from "../../interfaces/IAddressProviderService.sol";
import {AddressProvider} from "../core/AddressProvider.sol";
import {Constants} from "./Constants.sol";

/**
 * @title AddressProviderService
 * @author Brahma.fi
 * @notice Provides a base contract for services to resolve other services through AddressProvider
 * @dev This contract is designed to be inheritable by other contracts
 *  Provides quick and easy access to all contracts in Console Ecosystem
 */
abstract contract AddressProviderService is IAddressProviderService, Constants {
    error InvalidAddressProvider();
    error NotGovernance(address);
    error InvalidAddress();

    /// @notice address of addressProvider
    // solhint-disable-next-line immutable-vars-naming
    AddressProvider public immutable addressProvider;
    address public immutable walletRegistry;
    address public immutable policyRegistry;
    address public immutable executorRegistry;

    constructor(address _addressProvider) {
        if (_addressProvider == address(0)) revert InvalidAddressProvider();
        addressProvider = AddressProvider(_addressProvider);

        walletRegistry = addressProvider.getRegistry(_WALLET_REGISTRY_HASH);
        policyRegistry = addressProvider.getRegistry(_POLICY_REGISTRY_HASH);
        executorRegistry = addressProvider.getRegistry(_EXECUTOR_REGISTRY_HASH);

        _notNull(walletRegistry);
        _notNull(policyRegistry);
        _notNull(executorRegistry);
    }

    /**
     * @inheritdoc IAddressProviderService
     */
    function addressProviderTarget() external view override returns (address) {
        return address(addressProvider);
    }

    /**
     * @notice Helper to get authorized address from address provider
     * @param _key keccak256 key corresponding to authorized address
     * @return authorizedAddress
     */
    function _getAuthorizedAddress(bytes32 _key) internal view returns (address authorizedAddress) {
        authorizedAddress = addressProvider.getAuthorizedAddress(_key);
        _notNull(authorizedAddress);
    }

    /**
     * @notice Helper to revert if address is null
     * @param _addr address to check
     */
    function _notNull(address _addr) internal pure {
        if (_addr == address(0)) revert InvalidAddress();
    }
}

File 4 of 16 : TransactionValidator.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {ISafeWallet, Enum} from "interfaces/external/ISafeWallet.sol";
import {PolicyValidator} from "src/core/PolicyValidator.sol";
import {SafeHelper} from "src/libraries/SafeHelper.sol";
import {AddressProviderService} from "src/core/AddressProviderService.sol";
import {WalletRegistry} from "src/core/registries/WalletRegistry.sol";
import {TypeHashHelper} from "src/libraries/TypeHashHelper.sol";

/**
 * @title TransactionValidator
 * @author Brahma.fi
 * @notice Allows validation of transactions pre and post execution
 */
contract TransactionValidator is AddressProviderService {
    error AccountNotFound(address);
    error InvalidGuard();
    error InvalidFallbackHandler();
    error InvalidModule();
    error InvalidExecutorPlugin();
    error TxnUnAuthorized();

    /**
     * @notice datatype for safe transaction params
     * @param from address of safe
     * @param to target address
     * @param value txn value
     * @param data txn callData
     * @param operation type of operation
     * @param safeTxGas gas that should be used for safe txn
     * @param baseGas gas cost independent of txn cost
     * @param gasPrice gas price in current block
     * @param gasToken address of token used for gas
     * @param refundReceiver address of receiver of gas payment
     * @param signatures user signatures appended with validation signature
     * @param msgSender address of msg.sender of original txn
     */
    struct SafeTransactionParams {
        Enum.Operation operation;
        address from;
        address to;
        address payable refundReceiver;
        address gasToken;
        address msgSender;
        uint256 value;
        uint256 safeTxGas;
        uint256 baseGas;
        uint256 gasPrice;
        bytes data;
        bytes signatures;
    }

    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   CONSOLE GUARD HOOKS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    /**
     * @notice Validates a txn on guard before execution, for Brahma console accounts
     * @dev checks for possible console overrides and then performs policy validation
     * @param txParams params of transaction
     */
    function validatePreTransactionOverridable(SafeTransactionParams memory txParams) external view {
        // Validate policy
        _validatePolicySignature(
            TypeHashHelper.ExecutionParams({
                to: txParams.to,
                value: txParams.value,
                data: txParams.data,
                operation: uint8(txParams.operation),
                account: txParams.from,
                executor: address(0),
                nonce: ISafeWallet(txParams.from).nonce(),
                safeTxGas: txParams.safeTxGas,
                baseGas: txParams.baseGas,
                gasPrice: txParams.gasPrice,
                gasToken: txParams.gasToken,
                refundReceiver: txParams.refundReceiver
            }),
            txParams.signatures
        );
    }

    /* solhint-disable no-empty-blocks */
    /**
     * @notice Provides on-chain guarantees on security critical expected states of a Brhma console account
     * @dev Empty hook available for future use
     */
    function validatePostTransactionOverridable(bytes32, /*txHash */ bool, /*success */ address /*console */ )
        external
        view
    {}
    /* solhint-enable no-empty-blocks */

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   SUBACCOUNT GUARD HOOKS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    /**
     * @notice Validates a txn on guard before execution, for subAccounts
     * @dev calls policy validator to check if policy signature is valid
     * @param txParams params of transaction
     */
    function validatePreTransaction(SafeTransactionParams memory txParams) external view {
        _validatePolicySignature(
            TypeHashHelper.ExecutionParams({
                to: txParams.to,
                value: txParams.value,
                data: txParams.data,
                operation: uint8(txParams.operation),
                account: txParams.from,
                executor: address(0),
                nonce: ISafeWallet(txParams.from).nonce(),
                safeTxGas: txParams.safeTxGas,
                baseGas: txParams.baseGas,
                gasPrice: txParams.gasPrice,
                gasToken: txParams.gasToken,
                refundReceiver: txParams.refundReceiver
            }),
            txParams.signatures
        );
    }

    /**
     * @notice Provides on-chain guarantees on security critical expected states of subAccount for guard
     * @param subAccount address of subAccount to validate
     */
    function validatePostTransaction(bytes32, /*txHash */ bool, /*success */ address subAccount) external view {
        _checkSubAccountSecurityConfig(subAccount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EXECUTOR PLUGIN GUARD HOOKS              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    /**
     * @notice Validates a module txn before execution
     * @dev calls policy validator to check if policy signature is valid
     * @param from address of safe
     * @param executionStructHash execution struct hash
     * @param signatures user signatures appended with validation signature
     */
    function validatePreExecutorTransaction(
        address, /*msgSender */
        address from,
        bytes32 executionStructHash,
        bytes calldata signatures
    ) external view {
        _validatePolicySignature(from, executionStructHash, signatures);
    }

    /**
     * @notice Provides on-chain guarantees on security critical expected states of account for executor plugin
     * @param account address of account to validate
     */
    function validatePostExecutorTransaction(address, /*msgSender */ address account) external view {
        // Check if account has executor plugin still enabled as a module on it
        if (!ISafeWallet(account).isModuleEnabled(AddressProviderService._getAuthorizedAddress(_EXECUTOR_PLUGIN_HASH)))
        {
            revert InvalidExecutorPlugin();
        }

        if (WalletRegistry(walletRegistry).isWallet(account)) {
            _checkConsoleAccountSecurityConfig(account);
        } else if (WalletRegistry(walletRegistry).subAccountToWallet(account) != address(0)) {
            _checkSubAccountSecurityConfig(account);
        } else {
            revert AccountNotFound(account);
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   INTERNAL METHODS                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    /**
     * @notice Internal helper to validate the module, guard and fallback handler for a subaccount
     * @dev Ensures that guard has not been disabled/updated and the owner console as a module has not been disabled
     * @param _subAccount address of subAccount
     */
    function _checkSubAccountSecurityConfig(address _subAccount) internal view {
        address guard = SafeHelper._getGuard(_subAccount);
        address fallbackHandler = SafeHelper._getFallbackHandler(_subAccount);

        // Ensure guard has not been disabled
        if (guard != AddressProviderService._getAuthorizedAddress(_SAFE_MODERATOR_HASH)) revert InvalidGuard();

        // Ensure fallback handler has not been altered
        if (fallbackHandler != AddressProviderService._getAuthorizedAddress(_CONSOLE_FALLBACK_HANDLER_HASH)) {
            revert InvalidFallbackHandler();
        }

        address ownerConsole = WalletRegistry(walletRegistry).subAccountToWallet(_subAccount);

        // Ensure owner console as a module has not been disabled
        if (!ISafeWallet(_subAccount).isModuleEnabled(ownerConsole)) revert InvalidModule();
    }

    /**
     * @notice Internal helper to validate the module, guard and fallback handler for a console account
     * @dev Ensures that guard has not been disabled/updated
     * @param _consoleAccount address of consoleAccount
     */
    function _checkConsoleAccountSecurityConfig(address _consoleAccount) internal view {
        address guard = SafeHelper._getGuard(_consoleAccount);
        address fallbackHandler = SafeHelper._getFallbackHandler(_consoleAccount);

        // Ensure guard has not been disabled
        if (guard != AddressProviderService._getAuthorizedAddress(_SAFE_MODERATOR_OVERRIDABLE_HASH)) {
            revert InvalidGuard();
        }

        // Ensure fallback handler has not been altered
        if (fallbackHandler != AddressProviderService._getAuthorizedAddress(_CONSOLE_FALLBACK_HANDLER_HASH)) {
            revert InvalidFallbackHandler();
        }
    }

    /**
     * @notice Internal helper to validate policy signature for a safe txn
     * @dev Calls policy validator to check if policy signature is valid
     * @param _executionParams execution params struct
     * @param _signatures user signatures appended with validation signature
     */
    function _validatePolicySignature(TypeHashHelper.ExecutionParams memory _executionParams, bytes memory _signatures)
        internal
        view
    {
        if (
            !PolicyValidator(AddressProviderService._getAuthorizedAddress(_POLICY_VALIDATOR_HASH)).isPolicySignatureValid(
                _executionParams, _signatures
            )
        ) {
            revert TxnUnAuthorized();
        }
    }

    /**
     * @notice Internal helper to validate policy signature for a module txn
     * @dev Calls policy validator to check if policy signature is valid
     * @param _from address of safe
     * @param _executionStructHash execution struct hash
     * @param _signatures user signatures appended with validation signature
     */
    function _validatePolicySignature(address _from, bytes32 _executionStructHash, bytes memory _signatures)
        internal
        view
    {
        if (
            !PolicyValidator(AddressProviderService._getAuthorizedAddress(_POLICY_VALIDATOR_HASH)).isPolicySignatureValid(
                _from, _executionStructHash, _signatures
            )
        ) {
            revert TxnUnAuthorized();
        }
    }
}

File 5 of 16 : IERC165.sol
// 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);
}

File 6 of 16 : IAddressProviderService.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

interface IAddressProviderService {
    /// @notice Returns the address of the AddressProvider
    function addressProviderTarget() external view returns (address);
}

File 7 of 16 : AddressProvider.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {IAddressProviderService} from "interfaces/IAddressProviderService.sol";
import {Constants} from "src/core/Constants.sol";

/**
 * @title AddressProvider
 * @author Brahma.fi
 * @notice Single source of truth for resolving addresses of core components and external contracts
 */
contract AddressProvider is Constants {
    error RegistryAlreadyExists();
    error AddressProviderUnsupported();
    error NotGovernance(address);
    error NotPendingGovernance(address);
    error NullAddress();

    event RegistryInitialised(address indexed registry, bytes32 indexed key);
    event AuthorizedAddressInitialised(address indexed authorizedAddress, bytes32 indexed key);
    event GovernanceTransferRequested(address indexed previousGovernance, address indexed newGovernance);
    event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);

    /// @notice address of governance
    address public governance;
    /// @notice address of pending governance before accepting
    address public pendingGovernance;

    /**
     * @notice keccak256 hash of authorizedAddress keys mapped to their addresses
     * @dev Core & Roles are used as keys for this mapping. These addresses are mutable
     * @dev authorizedAddresses are updatable by governance
     */
    mapping(bytes32 => address) internal authorizedAddresses;

    /**
     * @notice keccak256 hash of registry keys mapped to their addresses
     * @dev registries are only set once by governance and immutable
     */
    mapping(bytes32 => address) internal registries;

    constructor(address _governance, address walletRegistry, address policyRegistry, address executorRegistry) {
        _notNull(_governance);
        governance = _governance;

        _notNull(walletRegistry);
        _notNull(policyRegistry);
        _notNull(executorRegistry);
        registries[_WALLET_REGISTRY_HASH] = walletRegistry;
        registries[_POLICY_REGISTRY_HASH] = policyRegistry;
        registries[_EXECUTOR_REGISTRY_HASH] = executorRegistry;
    }

    /**
     * @notice Governance setter
     * @param _newGovernance address of new governance
     */
    function setGovernance(address _newGovernance) external {
        _notNull(_newGovernance);
        _onlyGov();
        emit GovernanceTransferRequested(governance, _newGovernance);
        pendingGovernance = _newGovernance;
    }

    /**
     * @notice Governance accepter
     */
    function acceptGovernance() external {
        if (msg.sender != pendingGovernance) {
            revert NotPendingGovernance(msg.sender);
        }
        emit GovernanceTransferred(governance, msg.sender);
        governance = msg.sender;
        delete pendingGovernance;
    }

    /**
     * @notice Authorized address setter
     * @param _key key of authorizedAddress
     * @param _authorizedAddress address to set
     * @param _overrideCheck overrides check for supported address provider
     */
    function setAuthorizedAddress(bytes32 _key, address _authorizedAddress, bool _overrideCheck) external {
        _onlyGov();
        _notNull(_authorizedAddress);

        /// @dev skips checks for supported `addressProvider()` if `_overrideCheck` is true
        if (!_overrideCheck) {
            /// @dev skips checks for supported `addressProvider()` if `_authorizedAddress` is an EOA
            if (_authorizedAddress.code.length != 0) _ensureAddressProvider(_authorizedAddress);
        }

        authorizedAddresses[_key] = _authorizedAddress;

        emit AuthorizedAddressInitialised(_authorizedAddress, _key);
    }

    /**
     * @notice Registry address setter
     * @param _key key of registry address
     * @param _registry address to set
     */
    function setRegistry(bytes32 _key, address _registry) external {
        _onlyGov();
        _ensureAddressProvider(_registry);

        if (registries[_key] != address(0)) revert RegistryAlreadyExists();
        registries[_key] = _registry;

        emit RegistryInitialised(_registry, _key);
    }

    /**
     * @notice Authorized address getter
     * @param _key key of authorized address
     * @return address of authorized address
     */
    function getAuthorizedAddress(bytes32 _key) external view returns (address) {
        return authorizedAddresses[_key];
    }

    /**
     * @notice Registry address getter
     * @param _key key of registry address
     * @return address of registry address
     */
    function getRegistry(bytes32 _key) external view returns (address) {
        return registries[_key];
    }

    /**
     * @notice Ensures that the new address supports the AddressProviderService interface
     * and is pointing to this AddressProvider
     * @param _newAddress address to check
     */
    function _ensureAddressProvider(address _newAddress) internal view {
        if (IAddressProviderService(_newAddress).addressProviderTarget() != address(this)) {
            revert AddressProviderUnsupported();
        }
    }

    /**
     * @notice Checks if msg.sender is governance
     */
    function _onlyGov() internal view {
        if (msg.sender != governance) revert NotGovernance(msg.sender);
    }

    /**
     * @notice Checks and reverts if address is null
     * @param addr address to check if null
     */
    function _notNull(address addr) internal pure {
        if (addr == address(0)) revert NullAddress();
    }
}

File 8 of 16 : Constants.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

/**
 * @title Constants
 * @author Brahma.fi
 * @notice Contains constants used by multiple contracts
 * @dev Inflates bytecode size by approximately 560 bytes on deployment, but saves gas on runtime
 */
abstract contract Constants {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        REGISTRIES                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    /// @notice Key to map address of ExecutorRegistry
    bytes32 internal constant _EXECUTOR_REGISTRY_HASH = bytes32(uint256(keccak256("console.core.ExecutorRegistry")) - 1);

    /// @notice Key to map address of WalletRegistry
    bytes32 internal constant _WALLET_REGISTRY_HASH = bytes32(uint256(keccak256("console.core.WalletRegistry")) - 1);

    /// @notice Key to map address of PolicyRegistry
    bytes32 internal constant _POLICY_REGISTRY_HASH = bytes32(uint256(keccak256("console.core.PolicyRegistry")) - 1);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          CORE                              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    /// @notice Key to map address of ExecutorPlugin
    bytes32 internal constant _EXECUTOR_PLUGIN_HASH = bytes32(uint256(keccak256("console.core.ExecutorPlugin")) - 1);

    /// @notice Key to map address of ConsoleFallbackHandler
    bytes32 internal constant _CONSOLE_FALLBACK_HANDLER_HASH =
        bytes32(uint256(keccak256("console.core.FallbackHandler")) - 1);

    /// @notice Key to map address of Safe FallbackHandler
    bytes32 internal constant _SAFE_FALLBACK_HANDLER_HASH = bytes32(uint256(keccak256("safe.FallbackHandler")) - 1);

    /// @notice Key to map address of Safe MultiSend
    bytes32 internal constant _SAFE_MULTI_SEND_HASH = bytes32(uint256(keccak256("safe.MultiSend")) - 1);

    /// @notice Key to map address of SafeProxyFactory
    bytes32 internal constant _SAFE_PROXY_FACTORY_HASH = bytes32(uint256(keccak256("safe.ProxyFactory")) - 1);

    /// @notice Key to map address of SafeSingleton
    bytes32 internal constant _SAFE_SINGLETON_HASH = bytes32(uint256(keccak256("safe.Singleton")) - 1);

    /// @notice Key to map address of PolicyValidator
    bytes32 internal constant _POLICY_VALIDATOR_HASH = bytes32(uint256(keccak256("console.core.PolicyValidator")) - 1);

    /// @notice Key to map address of SafeDeployer
    bytes32 internal constant _SAFE_DEPLOYER_HASH = bytes32(uint256(keccak256("console.core.SafeDeployer")) - 1);

    /// @notice Key to map address of SafeEnabler
    bytes32 internal constant _SAFE_ENABLER_HASH = bytes32(uint256(keccak256("console.core.SafeEnabler")) - 1);

    /// @notice Key to map address of SafeModerator
    bytes32 internal constant _SAFE_MODERATOR_HASH = bytes32(uint256(keccak256("console.core.SafeModerator")) - 1);

    /// @notice Key to map address of SafeModeratorOverridable
    bytes32 internal constant _SAFE_MODERATOR_OVERRIDABLE_HASH =
        bytes32(uint256(keccak256("console.core.SafeModeratorOverridable")) - 1);

    /// @notice Key to map address of TransactionValidator
    bytes32 internal constant _TRANSACTION_VALIDATOR_HASH =
        bytes32(uint256(keccak256("console.core.TransactionValidator")) - 1);

    /// @notice Key to map address of ConsoleOpBuilder
    bytes32 internal constant _CONSOLE_OP_BUILDER_HASH =
        bytes32(uint256(keccak256("console.core.ConsoleOpBuilder")) - 1);

    /// @notice Key to map address of ExecutionBlocker
    bytes32 internal constant _EXECUTION_BLOCKER_HASH = bytes32(uint256(keccak256("console.core.ExecutionBlocker")) - 1);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          ROLES                             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Key to map address of Role PolicyAuthenticator
    bytes32 internal constant _POLICY_AUTHENTICATOR_HASH =
        bytes32(uint256(keccak256("console.roles.PolicyAuthenticator")) - 1);
}

File 9 of 16 : PolicyValidator.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
import {EIP712} from "solady/utils/EIP712.sol";
import {AddressProviderService} from "src/core/AddressProviderService.sol";
import {PolicyRegistry} from "src/core/registries/PolicyRegistry.sol";
import {TypeHashHelper} from "src/libraries/TypeHashHelper.sol";
import {ISafeWallet, Enum} from "interfaces/external/ISafeWallet.sol";

/**
 * @title PolicyValidator
 * @author Brahma.fi
 * @notice Responsible for validating policy signatures for safe transactions
 */
contract PolicyValidator is AddressProviderService, EIP712 {
    error InvalidSignature();
    error NoPolicyCommit();
    error TxnExpired(uint32 expiryEpoch);
    error InvalidSignatures();

    /// @notice EIP712 domain name
    string private constant _NAME = "PolicyValidator";
    /// @notice EIP712 domain version
    string private constant _VERSION = "1.0";

    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /**
     * @notice generates digest and validates signature against policies for safe transaction
     * @dev The POLICY_AUTHENTICATOR is expected to sign EIP712 digest generated from the following struct:
     * TypeHashHelper.ValidationParams, where -
     *  txnDigest = EIP712 digest generated from struct: TypeHashHelper.ExecutionParams
     *  policyHash = policy commit hash of the safe account
     *  expiryEpoch = expiry timestamp
     *
     * @dev signatures = abi.encodePacked(safeSignature, validatorSignature, validatorSignatureLength, expiryEpoch)
     *  safeSignature = safe owners signatures (arbitrary bytes length)
     *  validatorSignature = EIP 712 digest signature (arbitrary bytes length)
     *  validatorSignatureLength = length of `validatorSignature` (4 bytes)
     *  expiryEpoch = expiry timestamp (4 bytes)
     *
     * @param executionParams execution params struct
     * @param signatures user signatures appended with validation signature
     * @return isSignatureValid boolean
     */
    function isPolicySignatureValid(TypeHashHelper.ExecutionParams memory executionParams, bytes calldata signatures)
        external
        view
        returns (bool)
    {
        // Build transaction struct hash
        bytes32 executionStructHash = TypeHashHelper._buildExecutionParamsHash(executionParams);

        // Validate signature
        return isPolicySignatureValid(executionParams.account, executionStructHash, signatures);
    }

    /**
     * @notice generates digest and validates signature against policies for module execution
     * @dev signatures = abi.encodePacked(safeSignature, validatorSignature, validatorSignatureLength, expiryEpoch)
     *      safeSignature = safe owners signatures (arbitrary bytes length)
     *      validatorSignature = EIP 712 digest signed by `POLICY_AUTHENTICATOR`(arbitrary bytes length)
     *      validatorSignatureLength = length of `validatorSignature` (4 bytes)
     *      expiryEpoch = expiry timestamp (4 bytes)
     *  Here,
     *  The `POLICY_AUTHENTICATOR` is expected to sign the EIP 712 digest generated from following struct:
     *  TypeHashHelper.ValidationParams -
     *      txnDigest = EIP712 digest generated from struct: TypeHashHelper.ExecutionParams, with valid executor
     *      policyHash = policy commit hash of the safe account
     *      expiryEpoch = expiry timestamp
     *
     * @param account address of account to validate txn for
     * @param executionStructHash execution digest from ExecutorPlugin
     * @param signatures user signatures appended with validation signature
     * @return isSignatureValid boolean
     */
    function isPolicySignatureValid(address account, bytes32 executionStructHash, bytes calldata signatures)
        public
        view
        returns (bool)
    {
        // Get policy hash from registry
        bytes32 policyHash = PolicyRegistry(policyRegistry).commitments(account);
        if (policyHash == bytes32(0)) {
            revert NoPolicyCommit();
        }

        // Get expiry epoch and validator signature from signatures
        (uint32 expiryEpoch, bytes memory validatorSignature) = _decompileSignatures(signatures);

        // Ensure transaction has not expired
        if (expiryEpoch < uint32(block.timestamp)) {
            revert TxnExpired(expiryEpoch);
        }

        // Build validation struct hash
        bytes32 validationStructHash = TypeHashHelper._buildValidationParamsHash(
            TypeHashHelper.ValidationParams({
                executionStructHash: executionStructHash,
                policyHash: policyHash,
                expiryEpoch: expiryEpoch
            })
        );

        // Build EIP712 digest with validation struct hash
        bytes32 txnValidityDigest = _hashTypedData(validationStructHash);

        address policyAuthenticator = AddressProviderService._getAuthorizedAddress(_POLICY_AUTHENTICATOR_HASH);

        // Empty Signature check for EOA signer
        if (validatorSignature.length == 0) {
            uint256 _codesize;
            assembly {
                _codesize := extcodesize(policyAuthenticator)
            }
            if (_codesize == 0) {
                // PolicyAuthenticator is an EOA and no policyAuthenticator signature is provided
                revert InvalidSignature();
            }
        }

        // Validate signature
        return SignatureCheckerLib.isValidSignatureNow(policyAuthenticator, txnValidityDigest, validatorSignature);
    }

    /**
     * @notice Internal helper to extract validity signature from overall safe transaction signature
     * @dev _signatures = abi.encodePacked(safeSignature, validatorSignature, validatorSignatureLength, expiryEpoch)
     *  safeSignature = safe owners signatures (arbitrary bytes length)
     *  validatorSignature = EIP 712 digest signed (arbitrary bytes length)
     *  validatorSignatureLength = length of `validatorSignature` (4 bytes)
     *  expiryEpoch = expiry timestamp (4 bytes)
     *
     * @param _signatures packed transaction signature
     * @return expiryEpoch extracted expiry epoch signed by brahma backend
     * @return validatorSignature extracted validity signature
     */
    function _decompileSignatures(bytes calldata _signatures)
        internal
        pure
        returns (uint32 expiryEpoch, bytes memory validatorSignature)
    {
        if (_signatures.length < 8) revert InvalidSignatures();

        uint32 sigLength = uint32(bytes4(_signatures[_signatures.length - 8:_signatures.length - 4]));
        if (_signatures.length - 8 < sigLength) revert InvalidSignatures();

        expiryEpoch = uint32(bytes4(_signatures[_signatures.length - 4:_signatures.length]));
        validatorSignature = _signatures[_signatures.length - 8 - sigLength:_signatures.length - 8];
    }

    /**
     * @notice Internal helper to get EIP712 domain name and version
     * @return name domainName
     * @return version domainVersion
     */
    function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
        return (_NAME, _VERSION);
    }
}

File 10 of 16 : SafeHelper.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {Enum, ISafeWallet} from "interfaces/external/ISafeWallet.sol";
import {Types} from "interfaces/Types.sol";

/**
 * @title SafeHelper
 * @author Brahma.fi
 * @notice Helper library containing functions to interact with safe wallet
 */
library SafeHelper {
    error InvalidMultiSendInput();
    error UnableToParseOperation();

    /// @notice uint256(keccak256("guard_manager.guard.address"))
    /// @dev This refers to the storage slot where guard is stored in Safe's layout: https://github.com/safe-global/safe-contracts/blob/ff4c6761fbfae8ab8a94f36fd26bcfb2b5414eb1/contracts/base/GuardManager.sol#L77
    uint256 internal constant _GUARD_STORAGE_SLOT =
        33528237782592280163068556224972516439282563014722366175641814928123294921928;
    /// @notice uint256(keccak256("fallback_manager.handler.address"))
    /// @dev This refers to the storage slot where fallback handler is stored in Safe's layout: https://github.com/safe-global/safe-contracts/blob/ff4c6761fbfae8ab8a94f36fd26bcfb2b5414eb1/contracts/base/FallbackManager.sol#L14
    uint256 internal constant _FALLBACK_HANDLER_STORAGE_SLOT =
        49122629484629529244014240937346711770925847994644146912111677022347558721749;

    /**
     * @notice Contains hash for expected overridable guard removal calldata
     * @dev This is the hash of the calldata for the following function call
     *
     * abi.encodeCall(ISafeWallet.setGuard, (address(0))) = 0xe19a9dd90000000000000000000000000000000000000000000000000000000000000000
     * keccak256(abi.encodeCall(ISafeWallet.setGuard, (address(0)))) = 0xc0e2c16ecb99419a40dd8b9c0b339b27acebd27c481a28cd606927aeb86f5079
     */
    bytes32 internal constant _GUARD_REMOVAL_CALLDATA_HASH =
        0xc0e2c16ecb99419a40dd8b9c0b339b27acebd27c481a28cd606927aeb86f5079;

    /**
     * @notice Contains hash for expected overridable fallback handler removal calldata
     * @dev This is the hash of the calldata for the following function call
     *
     * abi.encodeCall(ISafeWallet.setFallbackHandler, (address(0))) = 0xf08a03230000000000000000000000000000000000000000000000000000000000000000
     * keccak256(abi.encodeCall(ISafeWallet.setFallbackHandler, (address(0)))) = 0x5bdf8c44c012c1347b2b15694dc5cc39b899eb99e32614676b7661001be925b7
     */
    bytes32 internal constant _FALLBACK_REMOVAL_CALLDATA_HASH =
        0x5bdf8c44c012c1347b2b15694dc5cc39b899eb99e32614676b7661001be925b7;

    /**
     * @notice Packs multiple executables into a single bytes array compatible with Safe's MultiSend contract which can be used as argument for multicall method
     * @dev Reference contract at https://github.com/safe-global/safe-contracts/blob/main/contracts/libraries/MultiSend.sol
     * @param _txns Array of executables to pack
     * @return packedTxns bytes array containing packed transactions
     */
    function _packMultisendTxns(Types.Executable[] memory _txns) internal pure returns (bytes memory packedTxns) {
        uint256 len = _txns.length;
        if (len == 0) revert InvalidMultiSendInput();

        uint256 i = 0;
        do {
            uint8 call = uint8(_parseOperationEnum(_txns[i].callType));

            uint256 calldataLength = _txns[i].data.length;

            bytes memory encodedTxn = abi.encodePacked(
                bytes1(call), bytes20(_txns[i].target), bytes32(_txns[i].value), bytes32(calldataLength), _txns[i].data
            );

            if (i != 0) {
                // If not first transaction, append to packedTxns
                packedTxns = abi.encodePacked(packedTxns, encodedTxn);
            } else {
                // If first transaction, set packedTxns to encodedTxn
                packedTxns = encodedTxn;
            }

            unchecked {
                ++i;
            }
        } while (i < len);
    }

    /**
     * @notice Gets the guard for a safe
     * @param safe address of safe
     * @return address of guard, address(0) if no guard exists
     */
    function _getGuard(address safe) internal view returns (address) {
        bytes memory guardAddress = ISafeWallet(safe).getStorageAt(_GUARD_STORAGE_SLOT, 1);
        return address(uint160(uint256(bytes32(guardAddress))));
    }

    /**
     * @notice Gets the fallback handler for a safe
     * @param safe address of safe
     * @return address of fallback handler, address(0) if no fallback handler exists
     */
    function _getFallbackHandler(address safe) internal view returns (address) {
        bytes memory fallbackHandlerAddress = ISafeWallet(safe).getStorageAt(_FALLBACK_HANDLER_STORAGE_SLOT, 1);
        return address(uint160(uint256(bytes32(fallbackHandlerAddress))));
    }

    /**
     * @notice Converts a CallType enum to an Operation enum.
     * @dev Reverts with UnableToParseOperation error if the CallType is not supported.
     * @param callType The CallType enum to be converted.
     * @return operation The converted Operation enum.
     */
    function _parseOperationEnum(Types.CallType callType) internal pure returns (Enum.Operation operation) {
        if (callType == Types.CallType.DELEGATECALL) {
            operation = Enum.Operation.DelegateCall;
        } else if (callType == Types.CallType.CALL) {
            operation = Enum.Operation.Call;
        } else {
            revert UnableToParseOperation();
        }
    }
}

File 11 of 16 : WalletRegistry.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

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

/**
 * @title WalletRegistry
 * @author Brahma.fi
 * @notice Registry for wallet and sub account addresses
 */
contract WalletRegistry is AddressProviderService {
    error AlreadyRegistered();
    error InvalidSender();
    error IsSubAccount();

    event RegisterWallet(address indexed wallet);
    event RegisterSubAccount(address indexed wallet, address indexed subAccount);

    /// @notice subAccount addresses mapped to owner wallet
    mapping(address subAccount => address wallet) public subAccountToWallet;
    /// @notice wallet addresses mapped to list of subAccounts
    mapping(address wallet => address[] subAccountList) public walletToSubAccountList;
    /// @notice address of wallet mapped to boolean indicating if it's a wallet
    mapping(address => bool) public isWallet;

    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /**
     * @notice Registers a wallet
     * @dev Can only be called by wallet to register itself
     */
    function registerWallet() external {
        if (isWallet[msg.sender]) revert AlreadyRegistered();
        if (subAccountToWallet[msg.sender] != address(0)) revert IsSubAccount();
        isWallet[msg.sender] = true;
        emit RegisterWallet(msg.sender);
    }

    /**
     * @notice Registers a sub account for a Safe
     * @param _wallet Console account address, owner of sub account
     * @param _subAccount Sub account address to register
     * @dev Can only be called by safe deployer
     */

    function registerSubAccount(address _wallet, address _subAccount) external {
        if (msg.sender != AddressProviderService._getAuthorizedAddress(_SAFE_DEPLOYER_HASH)) revert InvalidSender();

        if (subAccountToWallet[_subAccount] != address(0) || isWallet[_subAccount]) revert AlreadyRegistered();

        subAccountToWallet[_subAccount] = _wallet;
        walletToSubAccountList[_wallet].push(_subAccount);
        emit RegisterSubAccount(_wallet, _subAccount);
    }

    /**
     * @notice sub account list getter
     * @dev returns sub account list associated with _wallet
     * @param _wallet safe address
     * @return list of subAccounts for wallet
     */
    function getSubAccountsForWallet(address _wallet) external view returns (address[] memory) {
        return walletToSubAccountList[_wallet];
    }
}

File 12 of 16 : TypeHashHelper.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

/**
 * @title TypeHashHelper
 * @author Brahma.fi
 * @notice Helper library containing functions to build EIP712 struct and type hashes
 */
library TypeHashHelper {
    /**
     * @notice Structural representation of execution details
     * @param operation type of operation
     * @param to address to send tx to
     * @param account address of safe
     * @param executor address of executor if executed via executor plugin, address(0) if executed via execTransaction
     * @param gasToken address of token used for gas
     * @param refundReceiver address of receiver of gas payment
     * @param value txn value
     * @param nonce txn nonce
     * @param safeTxGas gas that should be used for safe txn
     * @param baseGas gas cost independent of txn cost
     * @param gasPrice gas price in current block
     * @param data txn callData
     */
    struct ExecutionParams {
        uint8 operation;
        address to;
        address account;
        address executor;
        address gasToken;
        address refundReceiver;
        uint256 value;
        uint256 nonce;
        uint256 safeTxGas;
        uint256 baseGas;
        uint256 gasPrice;
        bytes data;
    }

    /**
     * @notice Type of validation struct to hash
     * @param expiryEpoch max time till validity of the signature
     * @param executionStructHash txn digest generated using TypeHashHelper._buildExecutionParamsHash()
     * @param policyHash policy commit hash of the safe account
     */
    struct ValidationParams {
        uint32 expiryEpoch;
        bytes32 executionStructHash;
        bytes32 policyHash;
    }

    /**
     * @notice EIP712 typehash for execution params data
     * @dev keccak256("ExecutionParams(uint8 operation,address to,address account,address executor,address gasToken,address refundReceiver,uint256 value,uint256 nonce,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,bytes data)");
     */
    bytes32 public constant EXECUTION_PARAMS_TYPEHASH =
        0x483ad580f0a8d7881e792d04b2128f3b214b18aa7336126dc2e77a59752bd6f5;

    /**
     * @notice EIP712 typehash for validation data
     * @dev keccak256("ValidationParams(uint32 expiryEpoch,ExecutionParams executionParams,bytes32 policyHash)ExecutionParams(uint8 operation,address to,address account,address executor,address gasToken,address refundReceiver,uint256 value,uint256 nonce,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,bytes data)");
     */
    bytes32 public constant VALIDATION_PARAMS_TYPEHASH =
        0x37af4ddfcab5e0a0d11676ce89534ca59ffac1c30a5a4cc21f840d2f4704a952;

    /**
     * @notice Builds EIP712 execution struct hash
     * @param txn execution params struct
     * @return executionStructHash
     */
    function _buildExecutionParamsHash(ExecutionParams memory txn) internal pure returns (bytes32) {
        return keccak256(
            abi.encode(
                EXECUTION_PARAMS_TYPEHASH,
                txn.operation,
                txn.to,
                txn.account,
                txn.executor,
                txn.gasToken,
                txn.refundReceiver,
                txn.value,
                txn.nonce,
                txn.safeTxGas,
                txn.baseGas,
                txn.gasPrice,
                keccak256(txn.data)
            )
        );
    }

    /**
     * @notice Builds EIP712 validation struct hash
     * @param validation validation params struct
     * @return validationStructHash
     */
    function _buildValidationParamsHash(ValidationParams memory validation) internal pure returns (bytes32) {
        return keccak256(
            abi.encode(
                VALIDATION_PARAMS_TYPEHASH,
                validation.expiryEpoch,
                validation.executionStructHash,
                validation.policyHash
            )
        );
    }
}

File 13 of 16 : SignatureCheckerLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
///   to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
///
/// WARNING! Do NOT use signatures as unique identifiers.
/// Please use EIP712 with a nonce included in the digest to prevent replay attacks.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               SIGNATURE CHECKING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                if eq(mload(signature), 65) {
                    mstore(0x00, hash)
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    let t :=
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                        isValid := 1
                        mstore(0x60, 0) // Restore the zero slot.
                        mstore(0x40, m) // Restore the free memory pointer.
                        break
                    }
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                // Copy the `signature` over.
                let n := add(0x20, mload(signature))
                pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        add(returndatasize(), 0x44), // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                break
            }
        }
    }

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                if eq(signature.length, 65) {
                    mstore(0x00, hash)
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
                    let t :=
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                        isValid := 1
                        mstore(0x60, 0) // Restore the zero slot.
                        mstore(0x40, m) // Restore the free memory pointer.
                        break
                    }
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), signature.length)
                // Copy the `signature` over.
                calldatacopy(add(m, 0x64), signature.offset, signature.length)
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        add(signature.length, 0x64), // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                break
            }
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r) // `r`.
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                let t :=
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                    isValid := 1
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), mload(0x60)) // `s`.
                mstore8(add(m, 0xa4), mload(0x20)) // `v`.
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        0xa5, // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff)) // `v`.
                mstore(0x40, r) // `r`.
                mstore(0x60, s) // `s`.
                let t :=
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                    isValid := 1
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), s) // `s`.
                mstore8(add(m, 0xa4), v) // `v`.
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        0xa5, // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     ERC1271 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `signature` is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            // Copy the `signature` over.
            let n := add(0x20, mload(signature))
            pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    add(returndatasize(), 0x44), // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /// @dev Returns whether `signature` is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNowCalldata(
        address signer,
        bytes32 hash,
        bytes calldata signature
    ) internal view returns (bool isValid) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), signature.length)
            // Copy the `signature` over.
            calldatacopy(add(m, 0x64), signature.offset, signature.length)
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    add(signature.length, 0x64), // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
            mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    0xa5, // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), s) // `s`.
            mstore8(add(m, 0xa4), v) // `v`.
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    0xa5, // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

File 14 of 16 : EIP712.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CONSTANTS AND IMMUTABLES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 internal constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    address private immutable _cachedThis;
    uint256 private immutable _cachedChainId;
    bytes32 private immutable _cachedNameHash;
    bytes32 private immutable _cachedVersionHash;
    bytes32 private immutable _cachedDomainSeparator;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CONSTRUCTOR                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Cache the hashes for cheaper runtime gas costs.
    /// In the case of upgradeable contracts (i.e. proxies),
    /// or if the chain id changes due to a hard fork,
    /// the domain separator will be seamlessly calculated on-the-fly.
    constructor() {
        _cachedThis = address(this);
        _cachedChainId = block.chainid;

        string memory name;
        string memory version;
        if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
        bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
        bytes32 versionHash =
            _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
        _cachedNameHash = nameHash;
        _cachedVersionHash = versionHash;

        bytes32 separator;
        if (!_domainNameAndVersionMayChange()) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Load the free memory pointer.
                mstore(m, _DOMAIN_TYPEHASH)
                mstore(add(m, 0x20), nameHash)
                mstore(add(m, 0x40), versionHash)
                mstore(add(m, 0x60), chainid())
                mstore(add(m, 0x80), address())
                separator := keccak256(m, 0xa0)
            }
        }
        _cachedDomainSeparator = separator;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   FUNCTIONS TO OVERRIDE                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Please override this function to return the domain name and version.
    /// ```
    ///     function _domainNameAndVersion()
    ///         internal
    ///         pure
    ///         virtual
    ///         returns (string memory name, string memory version)
    ///     {
    ///         name = "Solady";
    ///         version = "1";
    ///     }
    /// ```
    ///
    /// Note: If the returned result may change after the contract has been deployed,
    /// you must override `_domainNameAndVersionMayChange()` to return true.
    function _domainNameAndVersion()
        internal
        view
        virtual
        returns (string memory name, string memory version);

    /// @dev Returns if `_domainNameAndVersion()` may change
    /// after the contract has been deployed (i.e. after the constructor).
    /// Default: false.
    function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the EIP-712 domain separator.
    function _domainSeparator() internal view virtual returns (bytes32 separator) {
        if (_domainNameAndVersionMayChange()) {
            separator = _buildDomainSeparator();
        } else {
            separator = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
        }
    }

    /// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
    /// given `structHash`, as defined in
    /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
    ///
    /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
    /// ```
    ///     bytes32 digest = _hashTypedData(keccak256(abi.encode(
    ///         keccak256("Mail(address to,string contents)"),
    ///         mailTo,
    ///         keccak256(bytes(mailContents))
    ///     )));
    ///     address signer = ECDSA.recover(digest, signature);
    /// ```
    function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
        bytes32 separator;
        if (_domainNameAndVersionMayChange()) {
            separator = _buildDomainSeparator();
        } else {
            separator = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the digest.
            mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
            mstore(0x1a, separator) // Store the domain separator.
            mstore(0x3a, structHash) // Store the struct hash.
            digest := keccak256(0x18, 0x42)
            // Restore the part of the free memory slot that was overwritten.
            mstore(0x3a, 0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    EIP-5267 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        fields = hex"0f"; // `0b01111`.
        (name, version) = _domainNameAndVersion();
        chainId = block.chainid;
        verifyingContract = address(this);
        salt = salt; // `bytes32(0)`.
        extensions = extensions; // `new uint256[](0)`.
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the EIP-712 domain separator.
    function _buildDomainSeparator() private view returns (bytes32 separator) {
        bytes32 nameHash;
        bytes32 versionHash;
        if (_domainNameAndVersionMayChange()) {
            (string memory name, string memory version) = _domainNameAndVersion();
            nameHash = keccak256(bytes(name));
            versionHash = keccak256(bytes(version));
        } else {
            nameHash = _cachedNameHash;
            versionHash = _cachedVersionHash;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            separator := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns if the cached domain separator has been invalidated.
    function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
        uint256 cachedChainId = _cachedChainId;
        address cachedThis = _cachedThis;
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
        }
    }
}

File 15 of 16 : PolicyRegistry.sol
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {AddressProviderService} from "src/core/AddressProviderService.sol";
import {WalletRegistry} from "src/core/registries/WalletRegistry.sol";

/**
 * @title PolicyRegistry
 * @author Brahma.fi
 * @notice Registry for policy commits for wallets and sub accounts
 */
contract PolicyRegistry is AddressProviderService {
    error PolicyCommitInvalid();
    error UnauthorizedPolicyUpdate();

    event UpdatedPolicyCommit(address indexed account, bytes32 policyCommit, bytes32 oldPolicyCommit);

    /// @notice account addresses mapped to their policy commits
    mapping(address account => bytes32 policyCommit) public commitments;

    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /**
     * @notice Enables setting policy commits for accounts
     * @param account address of account to set policy commit for
     * @param policyCommit policy commit hash to set
     * @dev policyCommit for an account can be set by:
     *  1. by safe deployer, if the account is uninitialized
     *  2. by the registered wallet, if the account is a subAccount
     *  3. by the account itself, if account is a registered wallet
     */
    function updatePolicy(address account, bytes32 policyCommit) external {
        if (policyCommit == bytes32(0)) {
            revert PolicyCommitInvalid();
        }

        bytes32 currentCommit = commitments[account];

        // solhint-disable no-empty-blocks
        if (
            currentCommit == bytes32(0)
                && msg.sender == AddressProviderService._getAuthorizedAddress(_SAFE_DEPLOYER_HASH)
        ) {
            // In case invoker is safe  deployer
        } else {
            if (WalletRegistry(walletRegistry).subAccountToWallet(account) == msg.sender) {
                //In case invoker is updating on behalf of sub account
            } else if (msg.sender == account && WalletRegistry(walletRegistry).isWallet(msg.sender)) {
                // In case invoker is a registered wallet
            } else {
                revert UnauthorizedPolicyUpdate();
            }
        }
        // solhint-enable no-empty-blocks
        _updatePolicy(account, policyCommit, currentCommit);
    }

    /**
     * @notice Internal function to update policy commit for an account
     * @param account address of account to set policy commit for
     * @param policyCommit policy commit hash to set
     */
    function _updatePolicy(address account, bytes32 policyCommit, bytes32 oldPolicyCommit) internal {
        emit UpdatedPolicyCommit(account, policyCommit, oldPolicyCommit);
        commitments[account] = policyCommit;
    }
}

File 16 of 16 : Types.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

interface Types {
    enum CallType {
        CALL,
        DELEGATECALL,
        STATICCALL
    }

    struct Executable {
        CallType callType;
        address target;
        uint256 value;
        bytes data;
    }

    struct TokenRequest {
        address token;
        uint256 amount;
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "safe-contracts/=lib/safe-contracts/contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solady/lib/solmate/src/",
    "solidity-bytes-utils/=lib/solidity-bytes-utils/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 20000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_addressProvider","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAddressProvider","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"NotGovernance","type":"error"},{"inputs":[],"name":"addressProvider","outputs":[{"internalType":"contract AddressProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addressProviderTarget","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"txHash","type":"bytes32"},{"internalType":"bool","name":"success","type":"bool"}],"name":"checkAfterExecution","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"},{"internalType":"uint256","name":"safeTxGas","type":"uint256"},{"internalType":"uint256","name":"baseGas","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"address","name":"gasToken","type":"address"},{"internalType":"address payable","name":"refundReceiver","type":"address"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"address","name":"msgSender","type":"address"}],"name":"checkTransaction","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executorRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"policyRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"walletRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

6101006040523480156200001257600080fd5b5060405162000e2f38038062000e2f8339810160408190526200003591620002b9565b806001600160a01b0381166200005e5760405163186b216f60e11b815260040160405180910390fd5b6001600160a01b038116608081905263e51fd7a66200009f60017f70dc150361dabd9d041fabc7ce344e2a2f31a73e202f37c7ba3188518a32c207620002eb565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa158015620000dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001029190620002b9565b6001600160a01b0390811660a0526080511663e51fd7a66200014660017f8547df78c054d57ec09e2772223da2229912d59e65dbd44da39709b40d8c40c3620002eb565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa15801562000183573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001a99190620002b9565b6001600160a01b0390811660c0526080511663e51fd7a6620001ed60017f25c3a50f208295151157a1115daca4d2b545fb99d7e2f46dca323d4edca762f2620002eb565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa1580156200022a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002509190620002b9565b6001600160a01b031660e05260a0516200026a906200028e565b60c05162000278906200028e565b60e05162000286906200028e565b505062000313565b6001600160a01b038116620002b65760405163e6c4247b60e01b815260040160405180910390fd5b50565b600060208284031215620002cc57600080fd5b81516001600160a01b0381168114620002e457600080fd5b9392505050565b818103818111156200030d57634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c05160e051610ad56200035a60003960006101a20152600060ba0152600061017b0152600081816101030152818161012c01526104c70152610ad56000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806375f0bb521161005b57806375f0bb521461014e5780639327136814610163578063ab7aa6ad14610176578063b1cebbe01461019d57600080fd5b806301ffc9a71461008d5780631c4dd7d0146100b557806321b1e480146101015780632954018c14610127575b600080fd5b6100a061009b3660046105a7565b6101c4565b60405190151581526020015b60405180910390f35b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ac565b7f00000000000000000000000000000000000000000000000000000000000000006100dc565b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b61016161015c366004610706565b61025d565b005b6101616101713660046107eb565b6103d5565b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe6d7a83a00000000000000000000000000000000000000000000000000000000148061025757507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b61029061028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b610495565b73ffffffffffffffffffffffffffffffffffffffff166361dbe82e6040518061018001604052808b60018111156102c9576102c961085a565b81526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018d81526020018a81526020018981526020018881526020018c8152602001858152506040518263ffffffff1660e01b81526004016103989190610928565b60006040518083038186803b1580156103b057600080fd5b505afa1580156103c4573d6000803e3d6000fd5b505050505050505050505050505050565b61040361028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b6040517fa3fbfc8700000000000000000000000000000000000000000000000000000000815260048101849052821515602482015233604482015273ffffffffffffffffffffffffffffffffffffffff919091169063a3fbfc879060640160006040518083038186803b15801561047957600080fd5b505afa15801561048d573d6000803e3d6000fd5b505050505050565b6040517f2bf84475000000000000000000000000000000000000000000000000000000008152600481018290526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632bf8447590602401602060405180830381865afa158015610523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105479190610a82565b905061055281610557565b919050565b73ffffffffffffffffffffffffffffffffffffffff81166105a4576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6000602082840312156105b957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146105e957600080fd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146105a457600080fd5b8035610552816105f0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261065d57600080fd5b813567ffffffffffffffff808211156106785761067861061d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156106be576106be61061d565b816040528381528660208588010111156106d757600080fd5b836020870160208301376000602085830101528094505050505092915050565b80356002811061055257600080fd5b60008060008060008060008060008060006101608c8e03121561072857600080fd5b6107318c610612565b9a5060208c0135995067ffffffffffffffff8060408e0135111561075457600080fd5b6107648e60408f01358f0161064c565b995061077260608e016106f7565b985060808d0135975060a08d0135965060c08d0135955061079560e08e01610612565b94506107a46101008e01610612565b9350806101208e013511156107b857600080fd5b506107ca8d6101208e01358e0161064c565b91506107d96101408d01610612565b90509295989b509295989b9093969950565b600080604083850312156107fe57600080fd5b823591506020830135801515811461081557600080fd5b809150509250929050565b81810381811115610257577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600281106108c0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000815180845260005b818110156108ea576020818501810151868301820152016108ce565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815261093a602082018351610889565b60006020830151610963604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015173ffffffffffffffffffffffffffffffffffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015173ffffffffffffffffffffffffffffffffffffffff811660a08401525060a083015173ffffffffffffffffffffffffffffffffffffffff811660c08401525060c083015160e083810191909152830151610100808401919091528301516101208084019190915283015161014080840191909152830151610180610160808501829052610a3e6101a08601846108c4565b908601518582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183870152909250610a7883826108c4565b9695505050505050565b600060208284031215610a9457600080fd5b81516105e9816105f056fea264697066735822122052f6071273e95e97ac5e986303b39323c69c51eb6304e3c623005f15d0d7c38b64736f6c634300081300330000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e229

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100885760003560e01c806375f0bb521161005b57806375f0bb521461014e5780639327136814610163578063ab7aa6ad14610176578063b1cebbe01461019d57600080fd5b806301ffc9a71461008d5780631c4dd7d0146100b557806321b1e480146101015780632954018c14610127575b600080fd5b6100a061009b3660046105a7565b6101c4565b60405190151581526020015b60405180910390f35b6100dc7f000000000000000000000000e2033fd8a642e67f11df2c5567023c1900e440f881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ac565b7f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e2296100dc565b6100dc7f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22981565b61016161015c366004610706565b61025d565b005b6101616101713660046107eb565b6103d5565b6100dc7f00000000000000000000000027fbc3310907c0425ea09115397a40dddc15464181565b6100dc7f0000000000000000000000000145f9674b22be444c9f0e5e2a7761643fe785be81565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe6d7a83a00000000000000000000000000000000000000000000000000000000148061025757507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b61029061028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b610495565b73ffffffffffffffffffffffffffffffffffffffff166361dbe82e6040518061018001604052808b60018111156102c9576102c961085a565b81526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018d81526020018a81526020018981526020018881526020018c8152602001858152506040518263ffffffff1660e01b81526004016103989190610928565b60006040518083038186803b1580156103b057600080fd5b505afa1580156103c4573d6000803e3d6000fd5b505050505050505050505050505050565b61040361028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b6040517fa3fbfc8700000000000000000000000000000000000000000000000000000000815260048101849052821515602482015233604482015273ffffffffffffffffffffffffffffffffffffffff919091169063a3fbfc879060640160006040518083038186803b15801561047957600080fd5b505afa15801561048d573d6000803e3d6000fd5b505050505050565b6040517f2bf84475000000000000000000000000000000000000000000000000000000008152600481018290526000907f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22973ffffffffffffffffffffffffffffffffffffffff1690632bf8447590602401602060405180830381865afa158015610523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105479190610a82565b905061055281610557565b919050565b73ffffffffffffffffffffffffffffffffffffffff81166105a4576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6000602082840312156105b957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146105e957600080fd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146105a457600080fd5b8035610552816105f0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261065d57600080fd5b813567ffffffffffffffff808211156106785761067861061d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156106be576106be61061d565b816040528381528660208588010111156106d757600080fd5b836020870160208301376000602085830101528094505050505092915050565b80356002811061055257600080fd5b60008060008060008060008060008060006101608c8e03121561072857600080fd5b6107318c610612565b9a5060208c0135995067ffffffffffffffff8060408e0135111561075457600080fd5b6107648e60408f01358f0161064c565b995061077260608e016106f7565b985060808d0135975060a08d0135965060c08d0135955061079560e08e01610612565b94506107a46101008e01610612565b9350806101208e013511156107b857600080fd5b506107ca8d6101208e01358e0161064c565b91506107d96101408d01610612565b90509295989b509295989b9093969950565b600080604083850312156107fe57600080fd5b823591506020830135801515811461081557600080fd5b809150509250929050565b81810381811115610257577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600281106108c0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000815180845260005b818110156108ea576020818501810151868301820152016108ce565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815261093a602082018351610889565b60006020830151610963604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015173ffffffffffffffffffffffffffffffffffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015173ffffffffffffffffffffffffffffffffffffffff811660a08401525060a083015173ffffffffffffffffffffffffffffffffffffffff811660c08401525060c083015160e083810191909152830151610100808401919091528301516101208084019190915283015161014080840191909152830151610180610160808501829052610a3e6101a08601846108c4565b908601518582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183870152909250610a7883826108c4565b9695505050505050565b600060208284031215610a9457600080fd5b81516105e9816105f056fea264697066735822122052f6071273e95e97ac5e986303b39323c69c51eb6304e3c623005f15d0d7c38b64736f6c63430008130033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e229

-----Decoded View---------------
Arg [0] : _addressProvider (address): 0x6FCf22e22f736D9ead75de8A1f12cA869287E229

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e229


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.