ETH Price: $2,611.96 (+2.34%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Execute Transact...228772022025-07-08 21:37:3512 hrs ago1752010655IN
Brahma Vaults : Executor Plugin
0 ETH0.000434491.08992689
Execute Transact...228767282025-07-08 20:02:3513 hrs ago1752004955IN
Brahma Vaults : Executor Plugin
0 ETH0.000865691.47321103
Execute Transact...228767242025-07-08 20:01:4713 hrs ago1752004907IN
Brahma Vaults : Executor Plugin
0 ETH0.000159031.56439431
Execute Transact...228767242025-07-08 20:01:4713 hrs ago1752004907IN
Brahma Vaults : Executor Plugin
0 ETH0.00056041.56439431
Execute Transact...228767212025-07-08 20:01:1113 hrs ago1752004871IN
Brahma Vaults : Executor Plugin
0 ETH0.00086991.58977132
Execute Transact...228689822025-07-07 18:03:1139 hrs ago1751911391IN
Brahma Vaults : Executor Plugin
0 ETH0.001116743.12250675
Execute Transact...228689782025-07-07 18:02:2339 hrs ago1751911343IN
Brahma Vaults : Executor Plugin
0 ETH0.000275663.05230272
Execute Transact...228689782025-07-07 18:02:2339 hrs ago1751911343IN
Brahma Vaults : Executor Plugin
0 ETH0.001812983.05230272
Execute Transact...228689712025-07-07 18:00:5939 hrs ago1751911259IN
Brahma Vaults : Executor Plugin
0 ETH0.001620372.99894889
Execute Transact...228612272025-07-06 16:02:592 days ago1751817779IN
Brahma Vaults : Executor Plugin
0 ETH0.000217010.60189762
Execute Transact...228612222025-07-06 16:01:592 days ago1751817719IN
Brahma Vaults : Executor Plugin
0 ETH0.000214110.53122525
Execute Transact...228612192025-07-06 16:01:232 days ago1751817683IN
Brahma Vaults : Executor Plugin
0 ETH0.000047720.52845229
Execute Transact...228612192025-07-06 16:01:232 days ago1751817683IN
Brahma Vaults : Executor Plugin
0 ETH0.00030420.52845229
Execute Transact...228612172025-07-06 16:00:592 days ago1751817659IN
Brahma Vaults : Executor Plugin
0 ETH0.000051660.50814464
Execute Transact...228612172025-07-06 16:00:592 days ago1751817659IN
Brahma Vaults : Executor Plugin
0 ETH0.000275980.50814464
Execute Transact...228580762025-07-06 5:28:113 days ago1751779691IN
Brahma Vaults : Executor Plugin
0 ETH0.000120380.30373849
Execute Transact...228534872025-07-05 14:02:113 days ago1751724131IN
Brahma Vaults : Executor Plugin
0 ETH0.000128660.35917413
Execute Transact...228534842025-07-05 14:01:353 days ago1751724095IN
Brahma Vaults : Executor Plugin
0 ETH0.000034390.38091019
Execute Transact...228534842025-07-05 14:01:353 days ago1751724095IN
Brahma Vaults : Executor Plugin
0 ETH0.000217490.38091019
Execute Transact...228534802025-07-05 14:00:473 days ago1751724047IN
Brahma Vaults : Executor Plugin
0 ETH0.0000390.38367608
Execute Transact...228534802025-07-05 14:00:473 days ago1751724047IN
Brahma Vaults : Executor Plugin
0 ETH0.000208870.38367608
Execute Transact...228457262025-07-04 12:02:354 days ago1751630555IN
Brahma Vaults : Executor Plugin
0 ETH0.000230430.64328268
Execute Transact...228457232025-07-04 12:01:594 days ago1751630519IN
Brahma Vaults : Executor Plugin
0 ETH0.000357030.65922113
Execute Transact...228457182025-07-04 12:00:594 days ago1751630459IN
Brahma Vaults : Executor Plugin
0 ETH0.000363510.63046332
Execute Transact...228450732025-07-04 9:51:234 days ago1751622683IN
Brahma Vaults : Executor Plugin
0 ETH0.000376421.20055677
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x6101a060187265242023-12-06 9:55:11580 days ago1701856511  Contract Creation0 ETH

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ExecutorPlugin

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 20000 runs

Other Settings:
paris EvmVersion
/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {ReentrancyGuard} from "openzeppelin-contracts/security/ReentrancyGuard.sol";
import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
import {EIP712} from "solady/utils/EIP712.sol";
import {ISafeWallet} from "interfaces/external/ISafeWallet.sol";
import {AddressProviderService} from "src/core/AddressProviderService.sol";
import {TransactionValidator} from "src/core/TransactionValidator.sol";
import {ExecutorRegistry} from "src/core/registries/ExecutorRegistry.sol";
import {SafeHelper} from "src/libraries/SafeHelper.sol";
import {TypeHashHelper} from "src/libraries/TypeHashHelper.sol";
import {Types} from "interfaces/Types.sol";

/**
 * @title ExecutorPlugin
 * @author Brahma.fi
 * @notice Responsible for executing transactions on safes with module permissions
 * @dev ExecutorPlugin needs to be enabled as a module on the safe
 */
contract ExecutorPlugin is AddressProviderService, ReentrancyGuard, EIP712 {
    error InvalidExecutor();
    error InvalidSignature();
    error ModuleExecutionFailed();

    /**
     * @notice datatype for execution requests
     * @param exec txn params
     * @param account address of safe to execute txn on
     * @param executor address that initiated execution request
     * @param executorSignature executor's signature for execution
     * @param validatorSignature validator's signature for execution
     * @dev Signature formats:
     *  executorSignature = executor's signatures (arbitrary bytes length)
     *  validatorSignature = abi.encodePacked(policySignature, length, expiryEpoch)
     *  where:
     *      policySignature = validity signature signed by validator (arbitrary bytes length)
     *      length = length of `policySignature` (4 bytes)
     *      expiryEpoch = expiry timestamp (4 bytes)
     */
    struct ExecutionRequest {
        Types.Executable exec;
        address account;
        address executor;
        bytes executorSignature;
        bytes validatorSignature;
    }

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

    /// @notice mapping of account to nonce of executors
    mapping(address account => mapping(address executor => uint256 nonce)) public executorNonce;

    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /**
     * @notice Enables executors to raise execution requests that will be executed via a module transaction
     * @dev The Executors are expected to sign the EIP712 digest generated from following struct: TypeHashHelper.ExecutionParams
     * @param execRequest params for execution request
     * @return returnData of txn
     */
    function executeTransaction(ExecutionRequest calldata execRequest) external nonReentrant returns (bytes memory) {
        address transactionValidator = AddressProviderService._getAuthorizedAddress(_TRANSACTION_VALIDATOR_HASH);
        _validateExecutionRequest(execRequest, transactionValidator);

        bytes memory txnResult = _executeTxnAsModule(execRequest.account, execRequest.exec);

        TransactionValidator(transactionValidator).validatePostExecutorTransaction(msg.sender, execRequest.account);

        return txnResult;
    }

    /**
     * @notice internal helper to execute transaction on a safe as a module
     * @dev executes txn as a module on `_account`
     * @param _account address of safe to execute on
     * @param _executable params of txn to execute
     * @return returnData of txn
     */
    function _executeTxnAsModule(address _account, Types.Executable memory _executable)
        internal
        returns (bytes memory)
    {
        (bool success, bytes memory txnResult) = ISafeWallet(_account).execTransactionFromModuleReturnData(
            _executable.target,
            _executable.value,
            _executable.data,
            SafeHelper._parseOperationEnum(_executable.callType)
        );
        if (!success) revert ModuleExecutionFailed();
        return txnResult;
    }

    /**
     * @notice internal helper to validate the execution request
     * @dev - validates executor's signature and checks if the executor is valid for given account.
     *      - validates policy
     * @param execRequest params for execution request
     */
    function _validateExecutionRequest(ExecutionRequest calldata execRequest, address transactionValidator) internal {
        if (!ExecutorRegistry(executorRegistry).isExecutor(execRequest.account, execRequest.executor)) {
            revert InvalidExecutor();
        }

        // Empty Signature check for EOA executor
        if (execRequest.executor.code.length == 0 && execRequest.executorSignature.length == 0) {
            // Executor is an EOA and no executor signature is provided
            revert InvalidSignature();
        }

        // Build execution struct hash
        bytes32 _executionStructHash = TypeHashHelper._buildExecutionParamsHash(
            TypeHashHelper.ExecutionParams({
                to: execRequest.exec.target,
                value: execRequest.exec.value,
                data: execRequest.exec.data,
                operation: uint8(SafeHelper._parseOperationEnum(execRequest.exec.callType)),
                account: execRequest.account,
                executor: execRequest.executor,
                nonce: executorNonce[execRequest.account][execRequest.executor]++,
                safeTxGas: 0,
                baseGas: 0,
                gasPrice: 0,
                gasToken: address(0),
                refundReceiver: address(0)
            })
        );

        // Build EIP712 digest for execution struct hash
        bytes32 _transactionDigest = _hashTypedData(_executionStructHash);

        // Validate executor signature
        if (
            !SignatureCheckerLib.isValidSignatureNow(
                execRequest.executor, _transactionDigest, execRequest.executorSignature
            )
        ) {
            revert InvalidExecutor();
        }

        // Validate policy signature
        TransactionValidator(transactionValidator).validatePreExecutorTransaction(
            msg.sender, execRequest.account, _executionStructHash, execRequest.validatorSignature
        );
    }

    /**
     * @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);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

// 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
        }
    }
}

// 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)))
        }
    }
}

// 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;
}

/// 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();
    }
}

/// 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();
        }
    }
}

/// SPDX-License-Identifier: BUSL-1.1

/// Copyright (C) 2023 Brahma.fi

pragma solidity 0.8.19;

import {AddressProviderService} from "../AddressProviderService.sol";
import {WalletRegistry} from "./WalletRegistry.sol";
import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";

/**
 * @title ExecutorRegistry
 * @author Brahma.fi
 * @notice Registry for executors for sub accounts
 */
contract ExecutorRegistry is AddressProviderService {
    using EnumerableSet for EnumerableSet.AddressSet;

    error NotAuthorized();
    error AlreadyExists();
    error DoesNotExist();
    error NoPolicyCommit();

    event RegisterExecutor(address indexed _account, address indexed _authorizer, address indexed _executor);
    event DeRegisterExecutor(address indexed _account, address indexed _authorizer, address indexed _executor);

    /// @notice account addresses mapped to executor addresses
    mapping(address account => EnumerableSet.AddressSet) private accountExecutors;

    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /**
     * @notice Registers an executor for account
     * @dev Adds new executor if it doesn't already exists else reverts with AlreadyExists()
     * @dev Can be only called by main console
     * @param _account console/subaccount address to add executor to
     * @param _executor executor to add
     */
    function registerExecutor(address _account, address _executor) external {
        _validateMsgSenderConsoleAccount(_account);
        if (!accountExecutors[_account].add(_executor)) revert AlreadyExists();
        emit RegisterExecutor(_account, msg.sender, _executor);
    }

    /**
     * @notice De-registers an executor for console account/ subaccount
     * @dev removes an executor if it exists else reverts with DoesNotExist()
     * @dev Can be only called by main console
     * @param _account console/subaccount address to remove executor from
     * @param _executor executor to remove
     */
    function deRegisterExecutor(address _account, address _executor) external {
        _validateMsgSenderConsoleAccount(_account);

        if (!accountExecutors[_account].remove(_executor)) revert DoesNotExist();
        emit DeRegisterExecutor(_account, msg.sender, _executor);
    }

    /**
     * @notice checks if _executor is registered for _account
     * @param _account address of account
     * @param _executor address of executor
     * @return isExecutorValid
     */
    function isExecutor(address _account, address _executor) external view returns (bool) {
        return accountExecutors[_account].contains(_executor);
    }

    /**
     * @return all the executors for a console account / subaccount
     * @param _account address of account
     */
    function getExecutorsForAccount(address _account) external view returns (address[] memory) {
        return accountExecutors[_account].values();
    }

    function _validateMsgSenderConsoleAccount(address _account) internal view {
        // msg.sender is console account
        if (msg.sender == _account && WalletRegistry(walletRegistry).isWallet(msg.sender)) return;

        // msg.sender is console account and owns the _account
        if (WalletRegistry(walletRegistry).subAccountToWallet(_account) == msg.sender) return;

        revert NotAuthorized();
    }
}

/// 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();
        }
    }
}

/// 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 11 of 19 : 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;
    }
}

// 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 13 of 19 : 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);
}

/// 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 15 of 19 : 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);
}

/// 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);
    }
}

/// 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];
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

/// 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;
    }
}

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

API
[{"inputs":[{"internalType":"address","name":"_addressProvider","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAddressProvider","type":"error"},{"inputs":[],"name":"InvalidExecutor","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"ModuleExecutionFailed","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"NotGovernance","type":"error"},{"inputs":[],"name":"UnableToParseOperation","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":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"enum Types.CallType","name":"callType","type":"uint8"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Types.Executable","name":"exec","type":"tuple"},{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"bytes","name":"executorSignature","type":"bytes"},{"internalType":"bytes","name":"validatorSignature","type":"bytes"}],"internalType":"struct ExecutorPlugin.ExecutionRequest","name":"execRequest","type":"tuple"}],"name":"executeTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"executor","type":"address"}],"name":"executorNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"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":[],"name":"walletRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

6101a06040523480156200001257600080fd5b5060405162001a1f38038062001a1f833981016040819052620000359162000388565b806001600160a01b0381166200005e5760405163186b216f60e11b815260040160405180910390fd5b6001600160a01b038116608081905263e51fd7a66200009f60017f70dc150361dabd9d041fabc7ce344e2a2f31a73e202f37c7ba3188518a32c207620003ba565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa158015620000dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000102919062000388565b6001600160a01b0390811660a0526080511663e51fd7a66200014660017f8547df78c054d57ec09e2772223da2229912d59e65dbd44da39709b40d8c40c3620003ba565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa15801562000183573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001a9919062000388565b6001600160a01b0390811660c0526080511663e51fd7a6620001ed60017f25c3a50f208295151157a1115daca4d2b545fb99d7e2f46dca323d4edca762f2620003ba565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa1580156200022a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000250919062000388565b6001600160a01b031660e05260a0516200026a906200035d565b60c05162000278906200035d565b60e05162000286906200035d565b50600160005530610100524661012052606080620002a2600090565b620002f357620002ed604080518082018252600e81526d22bc32b1baba37b928363ab3b4b760911b602080830191909152825180840190935260038352620312e360ec1b9083015291565b90925090505b8151602092830120815191830191909120610140829052610160819052604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8152938401929092529082015246606082015230608082015260a090206101805250620003e2565b6001600160a01b038116620003855760405163e6c4247b60e01b815260040160405180910390fd5b50565b6000602082840312156200039b57600080fd5b81516001600160a01b0381168114620003b357600080fd5b9392505050565b81810381811115620003dc57634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c05160e05161010051610120516101405161016051610180516115b9620004666000396000610cca01526000610d8401526000610d5e01526000610d0e01526000610ceb0152600081816101cb015261051e015260006092015260006101a401526000818160e001528181610109015261047c01526115b96000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80634611b4c71161005b5780634611b4c71461014b57806384b0196e14610184578063ab7aa6ad1461019f578063b1cebbe0146101c657600080fd5b80631c4dd7d01461008d57806321b1e480146100de5780632954018c146101045780632bf4762b1461012b575b600080fd5b6100b47f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000000000006100b4565b6100b47f000000000000000000000000000000000000000000000000000000000000000081565b61013e610139366004610ea7565b6101ed565b6040516100d59190610f50565b610176610159366004610f85565b600160209081526000928352604080842090915290825290205481565b6040519081526020016100d5565b61018c61032a565b6040516100d59796959493929190610fbe565b6100b47f000000000000000000000000000000000000000000000000000000000000000081565b6100b47f000000000000000000000000000000000000000000000000000000000000000081565b60606101f76103d3565b600061022c61022760017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd6110ac565b61044a565b90506102388382610507565b600061026561024d60408601602087016110bf565b61025786806110dc565b61026090611216565b6109dd565b905073ffffffffffffffffffffffffffffffffffffffff821663dcf297313361029460408801602089016110bf565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff92831660048201529116602482015260440160006040518083038186803b1580156102ff57600080fd5b505afa158015610313573d6000803e3d6000fd5b50929450505050506103256001600055565b919050565b7f0f0000000000000000000000000000000000000000000000000000000000000060608060008080836103c1604080518082018252600e81527f4578656375746f72506c7567696e0000000000000000000000000000000000006020808301919091528251808401909352600383527f312e3000000000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b600260005403610443576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640160405180910390fd5b6002600055565b6040517f2bf84475000000000000000000000000000000000000000000000000000000008152600481018290526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632bf8447590602401602060405180830381865afa1580156104d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fc91906112c7565b905061032581610ae3565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016639c84cc4861055360408501602086016110bf565b61056360608601604087016110bf565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401602060405180830381865afa1580156105d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f791906112f4565b61062d576040517f710c949700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61063d60608301604084016110bf565b73ffffffffffffffffffffffffffffffffffffffff163b15801561066d5750610669606083018361130f565b1590505b156106a4576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161018081019091526000906108ac90806106d86106c587806110dc565b6106d390602081019061137b565b610b33565b60018111156106e9576106e9611396565b60ff1681526020016106fb86806110dc565b61070c9060408101906020016110bf565b73ffffffffffffffffffffffffffffffffffffffff16815260200185602001602081019061073a91906110bf565b73ffffffffffffffffffffffffffffffffffffffff16815260200161076560608701604088016110bf565b73ffffffffffffffffffffffffffffffffffffffff168152600060208201819052604082015260600161079886806110dc565b604001358152602001600160008760200160208101906107b891906110bf565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600087604001602081019061080791906110bf565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600090812080549161083b836113c5565b90915550815260006020820181905260408201819052606082015260800161086386806110dc565b61087190606081019061130f565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050915250610ba9565b905060006108b982610cc6565b90506109166108ce60608601604087016110bf565b826108dc606088018861130f565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610de192505050565b61094c576040517f710c949700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316635a8793943361097960408801602089016110bf565b8561098760808a018a61130f565b6040518663ffffffff1660e01b81526004016109a79594939291906113fd565b60006040518083038186803b1580156109bf57600080fd5b505afa1580156109d3573d6000803e3d6000fd5b5050505050505050565b60606000808473ffffffffffffffffffffffffffffffffffffffff16635229073f856020015186604001518760600151610a1a8960000151610b33565b6040518563ffffffff1660e01b8152600401610a39949392919061147c565b6000604051808303816000875af1158015610a58573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610a9e91908101906114f9565b9150915081610ad9576040517fcce1466600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9150505b92915050565b73ffffffffffffffffffffffffffffffffffffffff8116610b30576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b60006001826002811115610b4957610b49611396565b03610b5657506001919050565b6000826002811115610b6a57610b6a611396565b03610b7757506000919050565b6040517f664a3ff500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516020808301516040808501516060860151608087015160a088015160c089015160e08a01516101008b01516101208c01516101408d01516101608e01518051908d0120995160009d610ca99d7f483ad580f0a8d7881e792d04b2128f3b214b18aa7336126dc2e77a59752bd6f59d919c919b9a9998979695949392019c8d5260ff9b909b1660208d015273ffffffffffffffffffffffffffffffffffffffff998a1660408d015297891660608c015295881660808b015293871660a08a01529190951660c088015260e08701949094526101008601939093526101208501929092526101408401919091526101608301526101808201526101a00190565b604051602081830303815290604052805190602001209050919050565b60007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000030147f0000000000000000000000000000000000000000000000000000000000000000461416610dbb5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f000000000000000000000000000000000000000000000000000000000000000060208201527f00000000000000000000000000000000000000000000000000000000000000009181019190915246606082015230608082015260a090205b67190100000000000060005280601a5282603a52604260182091506000603a5250919050565b73ffffffffffffffffffffffffffffffffffffffff9092169160008315610ea0576040516041835103610e575783600052606083015160001a60205260208301516040526040830151606052602060016080600060015afa805186183d1517610e5557506000606052604052506001610ea0565b505b600060605280604052631626ba7e60e01b808252846004830152602482016040815284516020018060448501828860045afa505060208160443d01858a5afa9051909114169150505b9392505050565b600060208284031215610eb957600080fd5b813567ffffffffffffffff811115610ed057600080fd5b820160a08185031215610ea057600080fd5b60005b83811015610efd578181015183820152602001610ee5565b50506000910152565b60008151808452610f1e816020860160208601610ee2565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610ea06020830184610f06565b73ffffffffffffffffffffffffffffffffffffffff81168114610b3057600080fd5b60008060408385031215610f9857600080fd5b8235610fa381610f63565b91506020830135610fb381610f63565b809150509250929050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e081840152610ffa60e084018a610f06565b838103604085015261100c818a610f06565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561106b5783518352928401929184019160010161104f565b50909c9b505050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610add57610add61107d565b6000602082840312156110d157600080fd5b8135610ea081610f63565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183360301811261111057600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561116c5761116c61111a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156111b9576111b961111a565b604052919050565b80356003811061032557600080fd5b600067ffffffffffffffff8211156111ea576111ea61111a565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006080823603121561122857600080fd5b611230611149565b611239836111c1565b815260208084013561124a81610f63565b8282015260408481013590830152606084013567ffffffffffffffff81111561127257600080fd5b840136601f82011261128357600080fd5b8035611296611291826111d0565b611172565b81815236848385010111156112aa57600080fd5b818484018583013760009181019093015250606082015292915050565b6000602082840312156112d957600080fd5b8151610ea081610f63565b8051801515811461032557600080fd5b60006020828403121561130657600080fd5b610ea0826112e4565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261134457600080fd5b83018035915067ffffffffffffffff82111561135f57600080fd5b60200191503681900382131561137457600080fd5b9250929050565b60006020828403121561138d57600080fd5b610ea0826111c1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113f6576113f661107d565b5060010190565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011683010190509695505050505050565b73ffffffffffffffffffffffffffffffffffffffff851681528360208201526080604082015260006114b16080830185610f06565b9050600283106114ea577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b82606083015295945050505050565b6000806040838503121561150c57600080fd5b611515836112e4565b9150602083015167ffffffffffffffff81111561153157600080fd5b8301601f8101851361154257600080fd5b8051611550611291826111d0565b81815286602083850101111561156557600080fd5b611576826020830160208601610ee2565b809350505050925092905056fea2646970667358221220c8c45e195a0dad5f5c876eeec47bafe9896aec771f35e88effeaf5ba1d19d83064736f6c634300081300330000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e229

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100885760003560e01c80634611b4c71161005b5780634611b4c71461014b57806384b0196e14610184578063ab7aa6ad1461019f578063b1cebbe0146101c657600080fd5b80631c4dd7d01461008d57806321b1e480146100de5780632954018c146101045780632bf4762b1461012b575b600080fd5b6100b47f000000000000000000000000e2033fd8a642e67f11df2c5567023c1900e440f881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b7f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e2296100b4565b6100b47f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22981565b61013e610139366004610ea7565b6101ed565b6040516100d59190610f50565b610176610159366004610f85565b600160209081526000928352604080842090915290825290205481565b6040519081526020016100d5565b61018c61032a565b6040516100d59796959493929190610fbe565b6100b47f00000000000000000000000027fbc3310907c0425ea09115397a40dddc15464181565b6100b47f0000000000000000000000000145f9674b22be444c9f0e5e2a7761643fe785be81565b60606101f76103d3565b600061022c61022760017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd6110ac565b61044a565b90506102388382610507565b600061026561024d60408601602087016110bf565b61025786806110dc565b61026090611216565b6109dd565b905073ffffffffffffffffffffffffffffffffffffffff821663dcf297313361029460408801602089016110bf565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff92831660048201529116602482015260440160006040518083038186803b1580156102ff57600080fd5b505afa158015610313573d6000803e3d6000fd5b50929450505050506103256001600055565b919050565b7f0f0000000000000000000000000000000000000000000000000000000000000060608060008080836103c1604080518082018252600e81527f4578656375746f72506c7567696e0000000000000000000000000000000000006020808301919091528251808401909352600383527f312e3000000000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b600260005403610443576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640160405180910390fd5b6002600055565b6040517f2bf84475000000000000000000000000000000000000000000000000000000008152600481018290526000907f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22973ffffffffffffffffffffffffffffffffffffffff1690632bf8447590602401602060405180830381865afa1580156104d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fc91906112c7565b905061032581610ae3565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000145f9674b22be444c9f0e5e2a7761643fe785be16639c84cc4861055360408501602086016110bf565b61056360608601604087016110bf565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401602060405180830381865afa1580156105d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f791906112f4565b61062d576040517f710c949700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61063d60608301604084016110bf565b73ffffffffffffffffffffffffffffffffffffffff163b15801561066d5750610669606083018361130f565b1590505b156106a4576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161018081019091526000906108ac90806106d86106c587806110dc565b6106d390602081019061137b565b610b33565b60018111156106e9576106e9611396565b60ff1681526020016106fb86806110dc565b61070c9060408101906020016110bf565b73ffffffffffffffffffffffffffffffffffffffff16815260200185602001602081019061073a91906110bf565b73ffffffffffffffffffffffffffffffffffffffff16815260200161076560608701604088016110bf565b73ffffffffffffffffffffffffffffffffffffffff168152600060208201819052604082015260600161079886806110dc565b604001358152602001600160008760200160208101906107b891906110bf565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600087604001602081019061080791906110bf565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600090812080549161083b836113c5565b90915550815260006020820181905260408201819052606082015260800161086386806110dc565b61087190606081019061130f565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050915250610ba9565b905060006108b982610cc6565b90506109166108ce60608601604087016110bf565b826108dc606088018861130f565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610de192505050565b61094c576040517f710c949700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316635a8793943361097960408801602089016110bf565b8561098760808a018a61130f565b6040518663ffffffff1660e01b81526004016109a79594939291906113fd565b60006040518083038186803b1580156109bf57600080fd5b505afa1580156109d3573d6000803e3d6000fd5b5050505050505050565b60606000808473ffffffffffffffffffffffffffffffffffffffff16635229073f856020015186604001518760600151610a1a8960000151610b33565b6040518563ffffffff1660e01b8152600401610a39949392919061147c565b6000604051808303816000875af1158015610a58573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610a9e91908101906114f9565b9150915081610ad9576040517fcce1466600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9150505b92915050565b73ffffffffffffffffffffffffffffffffffffffff8116610b30576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b60006001826002811115610b4957610b49611396565b03610b5657506001919050565b6000826002811115610b6a57610b6a611396565b03610b7757506000919050565b6040517f664a3ff500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516020808301516040808501516060860151608087015160a088015160c089015160e08a01516101008b01516101208c01516101408d01516101608e01518051908d0120995160009d610ca99d7f483ad580f0a8d7881e792d04b2128f3b214b18aa7336126dc2e77a59752bd6f59d919c919b9a9998979695949392019c8d5260ff9b909b1660208d015273ffffffffffffffffffffffffffffffffffffffff998a1660408d015297891660608c015295881660808b015293871660a08a01529190951660c088015260e08701949094526101008601939093526101208501929092526101408401919091526101608301526101808201526101a00190565b604051602081830303815290604052805190602001209050919050565b60007f067db72730c0a467faa9d9b32324fdbb731f98cdb8b0eafe3a677133b16a3c8c7f000000000000000000000000ec4181ee959e47f72e9cc60274fbe53d68949e4730147f0000000000000000000000000000000000000000000000000000000000000001461416610dbb5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f93ae17d63c11c26435c52b81ff53503650df80d35c62972110e64a0454badbec60208201527fe6bbd6277e1bf288eed5e8d1780f9a50b239e86b153736bceebccf4ea79d90b39181019190915246606082015230608082015260a090205b67190100000000000060005280601a5282603a52604260182091506000603a5250919050565b73ffffffffffffffffffffffffffffffffffffffff9092169160008315610ea0576040516041835103610e575783600052606083015160001a60205260208301516040526040830151606052602060016080600060015afa805186183d1517610e5557506000606052604052506001610ea0565b505b600060605280604052631626ba7e60e01b808252846004830152602482016040815284516020018060448501828860045afa505060208160443d01858a5afa9051909114169150505b9392505050565b600060208284031215610eb957600080fd5b813567ffffffffffffffff811115610ed057600080fd5b820160a08185031215610ea057600080fd5b60005b83811015610efd578181015183820152602001610ee5565b50506000910152565b60008151808452610f1e816020860160208601610ee2565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610ea06020830184610f06565b73ffffffffffffffffffffffffffffffffffffffff81168114610b3057600080fd5b60008060408385031215610f9857600080fd5b8235610fa381610f63565b91506020830135610fb381610f63565b809150509250929050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e081840152610ffa60e084018a610f06565b838103604085015261100c818a610f06565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561106b5783518352928401929184019160010161104f565b50909c9b505050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610add57610add61107d565b6000602082840312156110d157600080fd5b8135610ea081610f63565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183360301811261111057600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561116c5761116c61111a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156111b9576111b961111a565b604052919050565b80356003811061032557600080fd5b600067ffffffffffffffff8211156111ea576111ea61111a565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006080823603121561122857600080fd5b611230611149565b611239836111c1565b815260208084013561124a81610f63565b8282015260408481013590830152606084013567ffffffffffffffff81111561127257600080fd5b840136601f82011261128357600080fd5b8035611296611291826111d0565b611172565b81815236848385010111156112aa57600080fd5b818484018583013760009181019093015250606082015292915050565b6000602082840312156112d957600080fd5b8151610ea081610f63565b8051801515811461032557600080fd5b60006020828403121561130657600080fd5b610ea0826112e4565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261134457600080fd5b83018035915067ffffffffffffffff82111561135f57600080fd5b60200191503681900382131561137457600080fd5b9250929050565b60006020828403121561138d57600080fd5b610ea0826111c1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113f6576113f661107d565b5060010190565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011683010190509695505050505050565b73ffffffffffffffffffffffffffffffffffffffff851681528360208201526080604082015260006114b16080830185610f06565b9050600283106114ea577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b82606083015295945050505050565b6000806040838503121561150c57600080fd5b611515836112e4565b9150602083015167ffffffffffffffff81111561153157600080fd5b8301601f8101851361154257600080fd5b8051611550611291826111d0565b81815286602083850101111561156557600080fd5b611576826020830160208601610ee2565b809350505050925092905056fea2646970667358221220c8c45e195a0dad5f5c876eeec47bafe9896aec771f35e88effeaf5ba1d19d83064736f6c63430008130033

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 Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ 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.