Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x6d70Df6F203204A11B1C8bC67b42b751E7171Add
Balance 0 ETH
Nonce 1
Code Size 6908 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

6908 bytes
0x608060405234801561001057600080fd5b50600436106100a35760003560e01c8063ab7aa6ad11610076578063cb1dd4ac1161005b578063cb1dd4ac146101a7578063fbbe6709146101d5578063ffa1ad74146101e857600080fd5b8063ab7aa6ad14610159578063b1cebbe01461018057600080fd5b80630bce3117146100a85780631c4dd7d0146100e557806321b1e4801461010c5780632954018c14610132575b600080fd5b6100bb6100b63660046114ee565b610231565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100bb7f000000000000000000000000e2033fd8a642e67f11df2c5567023c1900e440f881565b7f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e2296100bb565b6100bb7f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22981565b6100bb7f00000000000000000000000027fbc3310907c0425ea09115397a40dddc15464181565b6100bb7f0000000000000000000000000145f9674b22be444c9f0e5e2a7761643fe785be81565b6101c76101b5366004611579565b60016020526000908152604090205481565b6040519081526020016100dc565b6100bb6101e33660046114ee565b610397565b6102246040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525081565b6040516100dc91906115e2565b600061023b6106a6565b60008060001b8414159050610291878761028b8a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c925088915061071d9050565b86610b5a565b91508015610342576040517f97cc56fa00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018690527f000000000000000000000000e2033fd8a642e67f11df2c5567023c1900e440f816906397cc56fa90604401600060405180830381600087803b15801561032957600080fd5b505af115801561033d573d6000803e3d6000fd5b505050505b60405173ffffffffffffffffffffffffffffffffffffffff8316907f1bec2f10b396fa20f81f7c7b268ebe3393f3f48faa1d153e5b4c8eb9fcc2e5af90600090a25061038e6001600055565b95945050505050565b60006103a16106a6565b826103d8576040517fc06789fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fce5570ec0000000000000000000000000000000000000000000000000000000081523360048201527f00000000000000000000000027fbc3310907c0425ea09115397a40dddc15464173ffffffffffffffffffffffffffffffffffffffff169063ce5570ec90602401602060405180830381865afa158015610462573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048691906115fc565b6104bc576040517fa5c9405400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61050586866104ff8989808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508b9250610dcb915050565b85610b5a565b6040517f13784dec00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192507f00000000000000000000000027fbc3310907c0425ea09115397a40dddc154641909116906313784dec90604401600060405180830381600087803b15801561059957600080fd5b505af11580156105ad573d6000803e3d6000fd5b50506040517f97cc56fa00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018790527f000000000000000000000000e2033fd8a642e67f11df2c5567023c1900e440f81692506397cc56fa9150604401600060405180830381600087803b15801561064157600080fd5b505af1158015610655573d6000803e3d6000fd5b505060405133925073ffffffffffffffffffffffffffffffffffffffff841691507ffd980c4a98876ab22fbe247e8d6d62278f117000134937b34f900d45e661a00590600090a361038e6001600055565b600260005403610716576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640160405180910390fd5b6002600055565b60606000606083156108e7576040805160028082526060820190925290816020015b61076960408051608081019091528060008152600060208201819052604082015260609081015290565b81526020019060019003908161073f5790505090506107b16107ac60017f6761b254bc21e1b7ed4f8e3505688d9f084e001a5ebafd2d7977ce30ecef45b161164d565b611161565b604080516080810190915290925080600181526020016107f56107ac60017f8d5a074715f2530fdeeceb4f46746aeb2b27977c78b28e1015ad0285c94cc39661164d565b73ffffffffffffffffffffffffffffffffffffffff168152600060208201526040016108456107ac60017fe6cdc984402f5f64c40a95da3acf8f5f3f59e8fedcff9afe09a4affa04d878c561164d565b60405173ffffffffffffffffffffffffffffffffffffffff909116602482015260440160408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe19a9dd90000000000000000000000000000000000000000000000000000000017905290528151829060019081106108d7576108d761168f565b602002602001018190525061096c565b60408051600180825281830190925290816020015b61092660408051608081019091528060008152600060208201819052604082015260609081015290565b8152602001906001900390816108fc5790505090506109696107ac60017f8d34abf3c5faced7a7d854c72007ef86ce0cb6db74d16e1afd2bc85a8d7e526561164d565b91505b6040805160808101909152806000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000027fbc3310907c0425ea09115397a40dddc15464116602080830191909152600060408084018290528051600481526024810190915291820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f33ff495a000000000000000000000000000000000000000000000000000000001790526060909201528251839190610a3157610a3161168f565b60209081029190910101528585610a6c6107ac60017f7b0e5a49f5fbfeec57d31edf25c372fe3431a77cc72d19d57bbc79f88cfc9db461164d565b610a7584611223565b604051602401610a8591906115e2565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8d80ff0a0000000000000000000000000000000000000000000000000000000017905251610af794939291908790600090819081906024016116be565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb63e800d00000000000000000000000000000000000000000000000000000000179052925050509392505050565b600080610b8b6107ac60017f46ab5d0a004add3e813ce77a6e82cd2f59d3037df5a3191fb55cd6c85f3eb4b861164d565b90506000610bbd6107ac60017f859864a5125cf70da3172b89a6fa741eb8d38f9b90eb66230092d37dd13f39cd61164d565b905060008787604051602001610bd49291906117dd565b6040516020818303038152906040528051906020012090505b6000610bf9828761139f565b6040517f1688f0b900000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff851690631688f0b990610c529086908b908690600401611838565b6020604051808303816000875af1925050508015610c8d575060408051601f3d908101601f19168201909252610c8a91810190611877565b60015b610da057610c99611894565b806308c379a003610d6c5750610cad611904565b80610cb85750610d6e565b805160208201207fd7c71a0bdd2eb2834ad042153c811dd478e4ee2324e3003b9522e03e7b3735dc14610d17576040517f57b11a0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818473ffffffffffffffffffffffffffffffffffffffff167faaec203cc0189434a3dd32aa252b7e6db66d72fbb07333b1d35368410b1d10cd8a604051610d5e91906115e2565b60405180910390a350610da3565b505b6040517f57b11a0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b94505b5073ffffffffffffffffffffffffffffffffffffffff841615610bed57505050949350505050565b60606000610dfd6107ac60017f8d5a074715f2530fdeeceb4f46746aeb2b27977c78b28e1015ad0285c94cc39661164d565b6040805160028082526060820190925291925060009190816020015b610e4360408051608081019091528060008152600060208201819052604082015260609081015290565b815260200190600190039081610e1957905050604080516080810182526001815273ffffffffffffffffffffffffffffffffffffffff85166020820152600081830152905133602482015291925090606082019060440160408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f610b592500000000000000000000000000000000000000000000000000000000179052905281518290600090610f0757610f0761168f565b60209081029190910101526040805160808101909152806001815273ffffffffffffffffffffffffffffffffffffffff8416602082015260006040820152606001610f766107ac60017f9b8c1f1719c06602c36f68ac7f7e0f638ec66e5e753d776a9228d4478104d89261164d565b60405173ffffffffffffffffffffffffffffffffffffffff909116602482015260440160408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe19a9dd90000000000000000000000000000000000000000000000000000000017905290528151829060019081106110085761100861168f565b602090810291909101015284846110436107ac60017f7b0e5a49f5fbfeec57d31edf25c372fe3431a77cc72d19d57bbc79f88cfc9db461164d565b61104c84611223565b60405160240161105c91906115e2565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8d80ff0a000000000000000000000000000000000000000000000000000000001790526110e26107ac60017f6761b254bc21e1b7ed4f8e3505688d9f084e001a5ebafd2d7977ce30ecef45b161164d565b60008060006040516024016110fe9897969594939291906116be565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb63e800d00000000000000000000000000000000000000000000000000000000179052925050505b92915050565b6040517f2bf84475000000000000000000000000000000000000000000000000000000008152600481018290526000907f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22973ffffffffffffffffffffffffffffffffffffffff1690632bf8447590602401602060405180830381865afa1580156111ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112139190611877565b905061121e81611428565b919050565b80516060906000819003611263576040517fb52777dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b600061128e85838151811061127d5761127d61168f565b602002602001015160000151611478565b600181111561129f5761129f611660565b905060008583815181106112b5576112b561168f565b60200260200101516060015151905060008260f81b8785815181106112dc576112dc61168f565b60200260200101516020015160601b8886815181106112fd576112fd61168f565b60200260200101516040015160001b8460001b8a88815181106113225761132261168f565b6020026020010151606001516040516020016113429594939291906119ac565b60405160208183030381529060405290508360001461138457858160405160200161136e929190611a2b565b6040516020818303038152906040529550611388565b8095505b836001019350505050818110611266575050919050565b600082815260016020526040812080548491836113bb83611a5a565b91905055836040518060400160405280600181526020017f31000000000000000000000000000000000000000000000000000000000000008152506040516020016114099493929190611a92565b60408051601f1981840301815291905280516020909101209392505050565b73ffffffffffffffffffffffffffffffffffffffff8116611475576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6000600182600281111561148e5761148e611660565b0361149b57506001919050565b60008260028111156114af576114af611660565b036114bc57506000919050565b6040517f664a3ff500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008060006080868803121561150657600080fd5b853567ffffffffffffffff8082111561151e57600080fd5b818801915088601f83011261153257600080fd5b81358181111561154157600080fd5b8960208260051b850101111561155657600080fd5b60209283019a909950918801359760408101359750606001359550909350505050565b60006020828403121561158b57600080fd5b5035919050565b60005b838110156115ad578181015183820152602001611595565b50506000910152565b600081518084526115ce816020860160208601611592565b601f01601f19169290920160200192915050565b6020815260006115f560208301846115b6565b9392505050565b60006020828403121561160e57600080fd5b815180151581146115f557600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561115b5761115b61161e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6101008082528951908201819052600090610120830190602090818d01845b8281101561170f57815173ffffffffffffffffffffffffffffffffffffffff16855293830193908301906001016116dd565b50505083018a905273ffffffffffffffffffffffffffffffffffffffff89166040840152828103606084015261174581896115b6565b91505061176a608083018773ffffffffffffffffffffffffffffffffffffffff169052565b73ffffffffffffffffffffffffffffffffffffffff851660a08301528360c08301526117ae60e083018473ffffffffffffffffffffffffffffffffffffffff169052565b9998505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461147557600080fd5b60208082528181018390526000908460408401835b8681101561182d578235611805816117bb565b73ffffffffffffffffffffffffffffffffffffffff16825291830191908301906001016117f2565b509695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260606020820152600061186760608301856115b6565b9050826040830152949350505050565b60006020828403121561188957600080fd5b81516115f5816117bb565b600060033d11156118ad5760046000803e5060005160e01c5b90565b601f19601f830116810181811067ffffffffffffffff821117156118fd577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040525050565b600060443d10156119125790565b6040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc803d016004833e81513d67ffffffffffffffff816024840111818411171561196057505050505090565b82850191508151818111156119785750505050505090565b843d87010160208285010111156119925750505050505090565b6119a1602082860101876118b0565b509095945050505050565b7fff00000000000000000000000000000000000000000000000000000000000000861681527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008516600182015283601582015282603582015260008251611a1a816055850160208701611592565b919091016055019695505050505050565b60008351611a3d818460208801611592565b835190830190611a51818360208801611592565b01949350505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611a8b57611a8b61161e565b5060010190565b84815283602082015282604082015260008251611ab6816060850160208701611592565b919091016060019594505050505056fea2646970667358221220edefa778675a56cb83f8ea7b157285892f21fe42a4608a5befbb36c4400d423f64736f6c63430008130033

Verified Source Code Full Match

Compiler: v0.8.19+commit.7dd6d404 EVM: paris Optimization: Yes (20000 runs)
Types.sol 22 lines
// 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;
    }
}
Constants.sol 82 lines
/// 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);
}
SafeDeployer.sol 249 lines
/// 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 {AddressProviderService} from "../core/AddressProviderService.sol";
import {WalletRegistry} from "../core/registries/WalletRegistry.sol";
import {PolicyRegistry} from "../core/registries/PolicyRegistry.sol";
import {ISafeProxyFactory} from "../../interfaces/external/ISafeProxyFactory.sol";
import {ISafeWallet} from "../../interfaces/external/ISafeWallet.sol";
import {Types, SafeHelper} from "../libraries/SafeHelper.sol";
import {ISafeMultiSend} from "../../interfaces/external/ISafeMultiSend.sol";

/**
 * @title SafeDeployer
 * @author Brahma.fi
 * @notice Deploys new brahma console accounts and sub accounts
 */
contract SafeDeployer is AddressProviderService, ReentrancyGuard {
    /// @notice version of safe deployer
    string public constant VERSION = "1";

    /**
     * @notice hash of safe create2 failure reason
     * @dev keccak256("Create2 call failed");
     */
    bytes32 internal constant _SAFE_CREATION_FAILURE_REASON =
        0xd7c71a0bdd2eb2834ad042153c811dd478e4ee2324e3003b9522e03e7b3735dc;

    event SafeProxyCreationFailure(address indexed singleton, uint256 indexed nonce, bytes initializer);
    event ConsoleAccountDeployed(address indexed consoleAddress);
    event SubAccountDeployed(address indexed subAccountAddress, address indexed consoleAddress);
    event PreComputeAccount(address[] indexed owners, uint256 indexed threshold);

    error InvalidCommitment();
    error NotWallet();
    error PreComputedAccount(address addr);
    error SafeProxyCreationFailed();

    constructor(address _addressProvider) AddressProviderService(_addressProvider) {}

    /// @notice owners nonce
    mapping(bytes32 ownersHash => uint256 count) public ownerSafeCount;

    /**
     * @notice Deploys a new console account with or without policy commit and registers it
     * @dev _owners list should contain addresses in the same order to generate same console address on all chains
     * @param _owners list of safe owners
     * @param _threshold safe threshold
     * @param _policyCommit commitment
     * @param _salt salt to be used during creation of safe
     * @return _safe deployed console account address
     */
    function deployConsoleAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
        external
        nonReentrant
        returns (address _safe)
    {
        bool _policyHashValid = _policyCommit != bytes32(0);

        _safe = _createSafe(_owners, _setupConsoleAccount(_owners, _threshold, _policyHashValid), _salt);

        if (_policyHashValid) {
            PolicyRegistry(policyRegistry).updatePolicy(_safe, _policyCommit);
        }
        emit ConsoleAccountDeployed(_safe);
    }

    /**
     * @notice Deploys a new sub-account with policy commit and registers it
     * @dev ConsoleAccount is enabled as module
     * @param _owners list of safe owners
     * @param _threshold safe threshold
     * @param _policyCommit commitment
     * @param _salt salt to be used during creation of safe, to generate nonce
     * @return _subAcc deployed sub-account address
     */
    function deploySubAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
        external
        nonReentrant
        returns (address _subAcc)
    {
        // Policy commit is required for sub account
        if (_policyCommit == bytes32(0)) revert InvalidCommitment();

        // Check if msg.sender is a registered wallet
        if (!WalletRegistry(walletRegistry).isWallet(msg.sender)) revert NotWallet();

        // Deploy sub account
        _subAcc = _createSafe(_owners, _setupSubAccount(_owners, _threshold), _salt);

        // Register sub account to wallet
        WalletRegistry(walletRegistry).registerSubAccount(msg.sender, _subAcc);

        // Update policy commit for sub account
        PolicyRegistry(policyRegistry).updatePolicy(_subAcc, _policyCommit);
        emit SubAccountDeployed(_subAcc, msg.sender);
    }

    /**
     * @notice Private helper function to setup Console account with setUp transactions
     * @param _owners list of owners addresses
     * @param _threshold safe threshold
     */
    function _setupConsoleAccount(address[] memory _owners, uint256 _threshold, bool _policyHashValid)
        private
        view
        returns (bytes memory)
    {
        address fallbackHandler;
        Types.Executable[] memory txns;

        if (_policyHashValid) {
            txns = new Types.Executable[](2);
            fallbackHandler = AddressProviderService._getAuthorizedAddress(_CONSOLE_FALLBACK_HANDLER_HASH);

            // Enable guard on console account
            txns[1] = Types.Executable({
                callType: Types.CallType.DELEGATECALL,
                target: AddressProviderService._getAuthorizedAddress(_SAFE_ENABLER_HASH),
                value: 0,
                data: abi.encodeCall(
                    ISafeWallet.setGuard, (AddressProviderService._getAuthorizedAddress(_SAFE_MODERATOR_OVERRIDABLE_HASH))
                    )
            });
        } else {
            txns = new Types.Executable[](1);
            fallbackHandler = AddressProviderService._getAuthorizedAddress(_SAFE_FALLBACK_HANDLER_HASH);
        }

        // Register Wallet
        /// @dev This function is being packed as a part of multisend transaction as, safe internally performs
        // a delegatecall during initializer to the target contract, so direct call doesnt work. Multisend is
        // supposed to be delegatecall
        txns[0] = Types.Executable({
            callType: Types.CallType.CALL,
            target: walletRegistry,
            value: 0,
            data: abi.encodeCall(WalletRegistry.registerWallet, ())
        });

        return abi.encodeCall(
            ISafeWallet.setup,
            (
                _owners,
                _threshold,
                AddressProviderService._getAuthorizedAddress(_SAFE_MULTI_SEND_HASH),
                abi.encodeCall(ISafeMultiSend.multiSend, (SafeHelper._packMultisendTxns(txns))),
                fallbackHandler,
                address(0),
                0,
                address(0)
            )
        );
    }

    /**
     * @notice Private helper function to setup subAccount safe with setUp transactions
     * @param _owners list of owners addresses
     * @param _threshold safe threshold
     */
    function _setupSubAccount(address[] memory _owners, uint256 _threshold) private view returns (bytes memory) {
        address safeEnabler = AddressProviderService._getAuthorizedAddress(_SAFE_ENABLER_HASH);
        Types.Executable[] memory txns = new Types.Executable[](2);

        // Enable Brhma Console account as module on sub Account
        txns[0] = Types.Executable({
            callType: Types.CallType.DELEGATECALL,
            target: safeEnabler,
            value: 0,
            data: abi.encodeCall(ISafeWallet.enableModule, (msg.sender))
        });

        // Enable guard on subAccount
        txns[1] = Types.Executable({
            callType: Types.CallType.DELEGATECALL,
            target: safeEnabler,
            value: 0,
            data: abi.encodeCall(ISafeWallet.setGuard, (AddressProviderService._getAuthorizedAddress(_SAFE_MODERATOR_HASH)))
        });

        return abi.encodeCall(
            ISafeWallet.setup,
            (
                _owners,
                _threshold,
                AddressProviderService._getAuthorizedAddress(_SAFE_MULTI_SEND_HASH),
                abi.encodeCall(ISafeMultiSend.multiSend, (SafeHelper._packMultisendTxns(txns))),
                AddressProviderService._getAuthorizedAddress(_CONSOLE_FALLBACK_HANDLER_HASH),
                address(0),
                0,
                address(0)
            )
        );
    }

    /**
     * @notice Internal function to create a new Safe Wallet.
     * @dev SafeDeployer calls createProxyWithNonce to deploy a new Safe Wallet. This also contains initializer bytes
     *  which are used during creation to setup the safe with owners and threshold. An actor can precompute the salt
     *  for a given set of owners and deploy the safe. We choose to not consider that safe as a valid safe and deploy a new
     *  safe. In case the actor chooses to deploy multiple precomputed safes with bumped nonces, the transaction will run out
     *  of gas and user can retry with a new random salt
     *  To generate deterministic addresses for a given set of owners, the order of owner addresses and threshold should be same
     * @param _owners list of owners addresses
     * @param _salt salt to be used during creation of safe, to generate nonce
     * @return _safe The address of the created Safe Wallet.
     */
    function _createSafe(address[] calldata _owners, bytes memory _initializer, bytes32 _salt)
        private
        returns (address _safe)
    {
        address safeProxyFactory = AddressProviderService._getAuthorizedAddress(_SAFE_PROXY_FACTORY_HASH);
        address safeSingleton = AddressProviderService._getAuthorizedAddress(_SAFE_SINGLETON_HASH);
        bytes32 ownersHash = keccak256(abi.encode(_owners));

        // Generate nonce based on owners and user provided salt
        do {
            uint256 nonce = _genNonce(ownersHash, _salt);
            try ISafeProxyFactory(safeProxyFactory).createProxyWithNonce(safeSingleton, _initializer, nonce) returns (
                address _deployedSafe
            ) {
                _safe = _deployedSafe;
            } catch Error(string memory reason) {
                // KEK
                if (keccak256(bytes(reason)) != _SAFE_CREATION_FAILURE_REASON) {
                    // Revert if the error is not due to create2 call failure
                    revert SafeProxyCreationFailed();
                }
                // A safe is already deployed with the same salt, retry with bumped nonce
                emit SafeProxyCreationFailure(safeSingleton, nonce, _initializer);
            } catch {
                revert SafeProxyCreationFailed();
            }
        } while (_safe == address(0));
    }

    /**
     * @notice Internal function to get the nonce of a user's safe deployment
     * @param _ownersHash address of owner of the safe.
     * @param _salt salt to be used in nonce generation
     * @return The nonce of the user's safe deployment.
     */
    function _genNonce(bytes32 _ownersHash, bytes32 _salt) private returns (uint256) {
        return uint256(keccak256(abi.encodePacked(_ownersHash, ownerSafeCount[_ownersHash]++, _salt, VERSION)));
    }
}
AddressProvider.sol 159 lines
/// 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();
    }
}
SafeHelper.sol 117 lines
/// 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();
        }
    }
}
ISafeWallet.sol 103 lines
// 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;
}
AddressProviderService.sol 67 lines
/// 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();
    }
}
IAddressProviderService.sol 7 lines
// 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);
}
ISafeMultiSend.sol 6 lines
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

interface ISafeMultiSend {
    function multiSend(bytes memory transactions) external payable;
}
PolicyRegistry.sol 70 lines
/// 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;
    }
}
WalletRegistry.sol 68 lines
/// 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];
    }
}
ISafeProxyFactory.sol 24 lines
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

interface ISafeProxyFactory {
    event ProxyCreation(address proxy, address singleton);

    function calculateCreateProxyWithNonceAddress(address _singleton, bytes memory initializer, uint256 saltNonce)
        external
        returns (address proxy);

    function createProxy(address singleton, bytes memory data) external returns (address proxy);

    function createProxyWithCallback(address _singleton, bytes memory initializer, uint256 saltNonce, address callback)
        external
        returns (address proxy);

    function createProxyWithNonce(address _singleton, bytes memory initializer, uint256 saltNonce)
        external
        returns (address proxy);

    function proxyCreationCode() external pure returns (bytes memory);

    function proxyRuntimeCode() external pure returns (bytes memory);
}
ReentrancyGuard.sol 77 lines
// 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;
    }
}
IERC165.sol 25 lines
// 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);
}
ReentrancyGuard.sol 77 lines
// 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;
    }
}
IERC165.sol 25 lines
// 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);
}

Read Contract

VERSION 0xffa1ad74 → string
addressProvider 0x2954018c → address
addressProviderTarget 0x21b1e480 → address
executorRegistry 0xb1cebbe0 → address
ownerSafeCount 0xcb1dd4ac → uint256
policyRegistry 0x1c4dd7d0 → address
walletRegistry 0xab7aa6ad → address

Write Contract 2 functions

These functions modify contract state and require a wallet transaction to execute.

deployConsoleAccount 0x0bce3117
address[] _owners
uint256 _threshold
bytes32 _policyCommit
bytes32 _salt
returns: address
deploySubAccount 0xfbbe6709
address[] _owners
uint256 _threshold
bytes32 _policyCommit
bytes32 _salt
returns: address

Recent Transactions

No transactions found for this address