Address Contract Verified
Address
0x6d70Df6F203204A11B1C8bC67b42b751E7171Add
Balance
0 ETH
Nonce
1
Code Size
6908 bytes
Creator
0x4b017F6D...6e11 at tx 0x17a811c1...d7f4a5
Indexed Transactions
0
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