Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
18726524 | 369 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
SafeModerator
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 20000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; import {IERC165, Guard, Enum} from "interfaces/external/ISafeWallet.sol"; import {AddressProviderService} from "src/core/AddressProviderService.sol"; import {TransactionValidator} from "src/core/TransactionValidator.sol"; /** * @title SafeModerator * @author Brahma.fi * @notice A guard that validates transactions and allows only policy abiding txns, on Brahma console account */ contract SafeModerator is AddressProviderService, Guard { constructor(address _addressProvider) AddressProviderService(_addressProvider) {} /** * @notice Inherited from Guard, function is called before executing a Safe transaction during execTransaction * @param to target address * @param value txn value * @param data txn callData * @param operation type of operation * @param safeTxGas gas that should be used for safe txn * @param baseGas gas cost independent of txn cost * @param gasPrice gas price in current block * @param gasToken address of token used for gas * @param refundReceiver address of receiver of gas payment * @param signatures user signatures appended with validation signature * @param msgSender address of msg.sender of original txn */ function checkTransaction( address to, uint256 value, bytes memory data, Enum.Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address payable refundReceiver, bytes memory signatures, address msgSender ) external view override { TransactionValidator(AddressProviderService._getAuthorizedAddress(_TRANSACTION_VALIDATOR_HASH)) .validatePreTransaction( TransactionValidator.SafeTransactionParams({ from: msg.sender, to: to, value: value, data: data, operation: operation, safeTxGas: safeTxGas, baseGas: baseGas, gasPrice: gasPrice, gasToken: gasToken, refundReceiver: refundReceiver, signatures: signatures, msgSender: msgSender }) ); } /** * @notice Inherited from Guard, function is called after executing a Safe transaction during execTransaction * @param txHash tx hash, computed from transaction * @param success boolean indicating success */ function checkAfterExecution(bytes32 txHash, bool success) external view override { TransactionValidator(AddressProviderService._getAuthorizedAddress(_TRANSACTION_VALIDATOR_HASH)) .validatePostTransaction(txHash, success, msg.sender); } function supportsInterface(bytes4 interfaceId) external pure returns (bool) { return interfaceId == type(Guard).interfaceId || interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.19; import {IERC165} from "openzeppelin-contracts/utils/introspection/IERC165.sol"; /// @title Enum - Collection of enums /// @author Richard Meissner - <[email protected]> contract Enum { enum Operation { Call, DelegateCall } } interface ISafeWallet { /// @dev Allows a Module to execute a Safe transaction without any further confirmations. /// @param to Destination address of module transaction. /// @param value Ether value of module transaction. /// @param data Data payload of module transaction. /// @param operation Operation type of module transaction. function execTransactionFromModule(address to, uint256 value, bytes calldata data, Enum.Operation operation) external returns (bool success); /// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data /// @param to Destination address of module transaction. /// @param value Ether value of module transaction. /// @param data Data payload of module transaction. /// @param operation Operation type of module transaction. function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) external returns (bool success, bytes memory returnData); function getStorageAt(uint256 offset, uint256 length) external view returns (bytes memory); function isOwner(address owner) external view returns (bool); function nonce() external view returns (uint256); function getThreshold() external view returns (uint256); function isModuleEnabled(address module) external view returns (bool); function enableModule(address module) external; function disableModule(address prevModule, address module) external; function removeOwner(address prevOwner, address owner, uint256 _threshold) external; function swapOwner(address prevOwner, address oldOwner, address newOwner) external; function getOwners() external view returns (address[] memory); function approveHash(bytes32 hashToApprove) external; function signedMessages(bytes32 _dataHash) external returns (uint256 _signatures); function execTransaction( address to, uint256 value, bytes calldata data, Enum.Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address payable refundReceiver, bytes memory signatures ) external payable returns (bool); function setup( address[] memory _owners, uint256 _threshold, address to, bytes memory data, address fallbackHandler, address paymentToken, uint256 payment, address paymentReceiver ) external; function addOwnerWithThreshold(address owner, uint256 _threshold) external; function domainSeparator() external view returns (bytes32); function setFallbackHandler(address _fallbackHandler) external; function setGuard(address guard) external; function encodeTransactionData( address to, uint256 value, bytes calldata data, Enum.Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, uint256 _nonce ) external view returns (bytes memory); } interface Guard is IERC165 { function checkTransaction( address to, uint256 value, bytes memory data, Enum.Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address payable refundReceiver, bytes memory signatures, address msgSender ) external; function checkAfterExecution(bytes32 txHash, bool success) external; }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; import {IAddressProviderService} from "../../interfaces/IAddressProviderService.sol"; import {AddressProvider} from "../core/AddressProvider.sol"; import {Constants} from "./Constants.sol"; /** * @title AddressProviderService * @author Brahma.fi * @notice Provides a base contract for services to resolve other services through AddressProvider * @dev This contract is designed to be inheritable by other contracts * Provides quick and easy access to all contracts in Console Ecosystem */ abstract contract AddressProviderService is IAddressProviderService, Constants { error InvalidAddressProvider(); error NotGovernance(address); error InvalidAddress(); /// @notice address of addressProvider // solhint-disable-next-line immutable-vars-naming AddressProvider public immutable addressProvider; address public immutable walletRegistry; address public immutable policyRegistry; address public immutable executorRegistry; constructor(address _addressProvider) { if (_addressProvider == address(0)) revert InvalidAddressProvider(); addressProvider = AddressProvider(_addressProvider); walletRegistry = addressProvider.getRegistry(_WALLET_REGISTRY_HASH); policyRegistry = addressProvider.getRegistry(_POLICY_REGISTRY_HASH); executorRegistry = addressProvider.getRegistry(_EXECUTOR_REGISTRY_HASH); _notNull(walletRegistry); _notNull(policyRegistry); _notNull(executorRegistry); } /** * @inheritdoc IAddressProviderService */ function addressProviderTarget() external view override returns (address) { return address(addressProvider); } /** * @notice Helper to get authorized address from address provider * @param _key keccak256 key corresponding to authorized address * @return authorizedAddress */ function _getAuthorizedAddress(bytes32 _key) internal view returns (address authorizedAddress) { authorizedAddress = addressProvider.getAuthorizedAddress(_key); _notNull(authorizedAddress); } /** * @notice Helper to revert if address is null * @param _addr address to check */ function _notNull(address _addr) internal pure { if (_addr == address(0)) revert InvalidAddress(); } }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; import {ISafeWallet, Enum} from "interfaces/external/ISafeWallet.sol"; import {PolicyValidator} from "src/core/PolicyValidator.sol"; import {SafeHelper} from "src/libraries/SafeHelper.sol"; import {AddressProviderService} from "src/core/AddressProviderService.sol"; import {WalletRegistry} from "src/core/registries/WalletRegistry.sol"; import {TypeHashHelper} from "src/libraries/TypeHashHelper.sol"; /** * @title TransactionValidator * @author Brahma.fi * @notice Allows validation of transactions pre and post execution */ contract TransactionValidator is AddressProviderService { error AccountNotFound(address); error InvalidGuard(); error InvalidFallbackHandler(); error InvalidModule(); error InvalidExecutorPlugin(); error TxnUnAuthorized(); /** * @notice datatype for safe transaction params * @param from address of safe * @param to target address * @param value txn value * @param data txn callData * @param operation type of operation * @param safeTxGas gas that should be used for safe txn * @param baseGas gas cost independent of txn cost * @param gasPrice gas price in current block * @param gasToken address of token used for gas * @param refundReceiver address of receiver of gas payment * @param signatures user signatures appended with validation signature * @param msgSender address of msg.sender of original txn */ struct SafeTransactionParams { Enum.Operation operation; address from; address to; address payable refundReceiver; address gasToken; address msgSender; uint256 value; uint256 safeTxGas; uint256 baseGas; uint256 gasPrice; bytes data; bytes signatures; } constructor(address _addressProvider) AddressProviderService(_addressProvider) {} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSOLE GUARD HOOKS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Validates a txn on guard before execution, for Brahma console accounts * @dev checks for possible console overrides and then performs policy validation * @param txParams params of transaction */ function validatePreTransactionOverridable(SafeTransactionParams memory txParams) external view { // Validate policy _validatePolicySignature( TypeHashHelper.ExecutionParams({ to: txParams.to, value: txParams.value, data: txParams.data, operation: uint8(txParams.operation), account: txParams.from, executor: address(0), nonce: ISafeWallet(txParams.from).nonce(), safeTxGas: txParams.safeTxGas, baseGas: txParams.baseGas, gasPrice: txParams.gasPrice, gasToken: txParams.gasToken, refundReceiver: txParams.refundReceiver }), txParams.signatures ); } /* solhint-disable no-empty-blocks */ /** * @notice Provides on-chain guarantees on security critical expected states of a Brhma console account * @dev Empty hook available for future use */ function validatePostTransactionOverridable(bytes32, /*txHash */ bool, /*success */ address /*console */ ) external view {} /* solhint-enable no-empty-blocks */ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SUBACCOUNT GUARD HOOKS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Validates a txn on guard before execution, for subAccounts * @dev calls policy validator to check if policy signature is valid * @param txParams params of transaction */ function validatePreTransaction(SafeTransactionParams memory txParams) external view { _validatePolicySignature( TypeHashHelper.ExecutionParams({ to: txParams.to, value: txParams.value, data: txParams.data, operation: uint8(txParams.operation), account: txParams.from, executor: address(0), nonce: ISafeWallet(txParams.from).nonce(), safeTxGas: txParams.safeTxGas, baseGas: txParams.baseGas, gasPrice: txParams.gasPrice, gasToken: txParams.gasToken, refundReceiver: txParams.refundReceiver }), txParams.signatures ); } /** * @notice Provides on-chain guarantees on security critical expected states of subAccount for guard * @param subAccount address of subAccount to validate */ function validatePostTransaction(bytes32, /*txHash */ bool, /*success */ address subAccount) external view { _checkSubAccountSecurityConfig(subAccount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EXECUTOR PLUGIN GUARD HOOKS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Validates a module txn before execution * @dev calls policy validator to check if policy signature is valid * @param from address of safe * @param executionStructHash execution struct hash * @param signatures user signatures appended with validation signature */ function validatePreExecutorTransaction( address, /*msgSender */ address from, bytes32 executionStructHash, bytes calldata signatures ) external view { _validatePolicySignature(from, executionStructHash, signatures); } /** * @notice Provides on-chain guarantees on security critical expected states of account for executor plugin * @param account address of account to validate */ function validatePostExecutorTransaction(address, /*msgSender */ address account) external view { // Check if account has executor plugin still enabled as a module on it if (!ISafeWallet(account).isModuleEnabled(AddressProviderService._getAuthorizedAddress(_EXECUTOR_PLUGIN_HASH))) { revert InvalidExecutorPlugin(); } if (WalletRegistry(walletRegistry).isWallet(account)) { _checkConsoleAccountSecurityConfig(account); } else if (WalletRegistry(walletRegistry).subAccountToWallet(account) != address(0)) { _checkSubAccountSecurityConfig(account); } else { revert AccountNotFound(account); } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL METHODS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Internal helper to validate the module, guard and fallback handler for a subaccount * @dev Ensures that guard has not been disabled/updated and the owner console as a module has not been disabled * @param _subAccount address of subAccount */ function _checkSubAccountSecurityConfig(address _subAccount) internal view { address guard = SafeHelper._getGuard(_subAccount); address fallbackHandler = SafeHelper._getFallbackHandler(_subAccount); // Ensure guard has not been disabled if (guard != AddressProviderService._getAuthorizedAddress(_SAFE_MODERATOR_HASH)) revert InvalidGuard(); // Ensure fallback handler has not been altered if (fallbackHandler != AddressProviderService._getAuthorizedAddress(_CONSOLE_FALLBACK_HANDLER_HASH)) { revert InvalidFallbackHandler(); } address ownerConsole = WalletRegistry(walletRegistry).subAccountToWallet(_subAccount); // Ensure owner console as a module has not been disabled if (!ISafeWallet(_subAccount).isModuleEnabled(ownerConsole)) revert InvalidModule(); } /** * @notice Internal helper to validate the module, guard and fallback handler for a console account * @dev Ensures that guard has not been disabled/updated * @param _consoleAccount address of consoleAccount */ function _checkConsoleAccountSecurityConfig(address _consoleAccount) internal view { address guard = SafeHelper._getGuard(_consoleAccount); address fallbackHandler = SafeHelper._getFallbackHandler(_consoleAccount); // Ensure guard has not been disabled if (guard != AddressProviderService._getAuthorizedAddress(_SAFE_MODERATOR_OVERRIDABLE_HASH)) { revert InvalidGuard(); } // Ensure fallback handler has not been altered if (fallbackHandler != AddressProviderService._getAuthorizedAddress(_CONSOLE_FALLBACK_HANDLER_HASH)) { revert InvalidFallbackHandler(); } } /** * @notice Internal helper to validate policy signature for a safe txn * @dev Calls policy validator to check if policy signature is valid * @param _executionParams execution params struct * @param _signatures user signatures appended with validation signature */ function _validatePolicySignature(TypeHashHelper.ExecutionParams memory _executionParams, bytes memory _signatures) internal view { if ( !PolicyValidator(AddressProviderService._getAuthorizedAddress(_POLICY_VALIDATOR_HASH)).isPolicySignatureValid( _executionParams, _signatures ) ) { revert TxnUnAuthorized(); } } /** * @notice Internal helper to validate policy signature for a module txn * @dev Calls policy validator to check if policy signature is valid * @param _from address of safe * @param _executionStructHash execution struct hash * @param _signatures user signatures appended with validation signature */ function _validatePolicySignature(address _from, bytes32 _executionStructHash, bytes memory _signatures) internal view { if ( !PolicyValidator(AddressProviderService._getAuthorizedAddress(_POLICY_VALIDATOR_HASH)).isPolicySignatureValid( _from, _executionStructHash, _signatures ) ) { revert TxnUnAuthorized(); } } }
// SPDX-License-Identifier: 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); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; interface IAddressProviderService { /// @notice Returns the address of the AddressProvider function addressProviderTarget() external view returns (address); }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; import {IAddressProviderService} from "interfaces/IAddressProviderService.sol"; import {Constants} from "src/core/Constants.sol"; /** * @title AddressProvider * @author Brahma.fi * @notice Single source of truth for resolving addresses of core components and external contracts */ contract AddressProvider is Constants { error RegistryAlreadyExists(); error AddressProviderUnsupported(); error NotGovernance(address); error NotPendingGovernance(address); error NullAddress(); event RegistryInitialised(address indexed registry, bytes32 indexed key); event AuthorizedAddressInitialised(address indexed authorizedAddress, bytes32 indexed key); event GovernanceTransferRequested(address indexed previousGovernance, address indexed newGovernance); event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance); /// @notice address of governance address public governance; /// @notice address of pending governance before accepting address public pendingGovernance; /** * @notice keccak256 hash of authorizedAddress keys mapped to their addresses * @dev Core & Roles are used as keys for this mapping. These addresses are mutable * @dev authorizedAddresses are updatable by governance */ mapping(bytes32 => address) internal authorizedAddresses; /** * @notice keccak256 hash of registry keys mapped to their addresses * @dev registries are only set once by governance and immutable */ mapping(bytes32 => address) internal registries; constructor(address _governance, address walletRegistry, address policyRegistry, address executorRegistry) { _notNull(_governance); governance = _governance; _notNull(walletRegistry); _notNull(policyRegistry); _notNull(executorRegistry); registries[_WALLET_REGISTRY_HASH] = walletRegistry; registries[_POLICY_REGISTRY_HASH] = policyRegistry; registries[_EXECUTOR_REGISTRY_HASH] = executorRegistry; } /** * @notice Governance setter * @param _newGovernance address of new governance */ function setGovernance(address _newGovernance) external { _notNull(_newGovernance); _onlyGov(); emit GovernanceTransferRequested(governance, _newGovernance); pendingGovernance = _newGovernance; } /** * @notice Governance accepter */ function acceptGovernance() external { if (msg.sender != pendingGovernance) { revert NotPendingGovernance(msg.sender); } emit GovernanceTransferred(governance, msg.sender); governance = msg.sender; delete pendingGovernance; } /** * @notice Authorized address setter * @param _key key of authorizedAddress * @param _authorizedAddress address to set * @param _overrideCheck overrides check for supported address provider */ function setAuthorizedAddress(bytes32 _key, address _authorizedAddress, bool _overrideCheck) external { _onlyGov(); _notNull(_authorizedAddress); /// @dev skips checks for supported `addressProvider()` if `_overrideCheck` is true if (!_overrideCheck) { /// @dev skips checks for supported `addressProvider()` if `_authorizedAddress` is an EOA if (_authorizedAddress.code.length != 0) _ensureAddressProvider(_authorizedAddress); } authorizedAddresses[_key] = _authorizedAddress; emit AuthorizedAddressInitialised(_authorizedAddress, _key); } /** * @notice Registry address setter * @param _key key of registry address * @param _registry address to set */ function setRegistry(bytes32 _key, address _registry) external { _onlyGov(); _ensureAddressProvider(_registry); if (registries[_key] != address(0)) revert RegistryAlreadyExists(); registries[_key] = _registry; emit RegistryInitialised(_registry, _key); } /** * @notice Authorized address getter * @param _key key of authorized address * @return address of authorized address */ function getAuthorizedAddress(bytes32 _key) external view returns (address) { return authorizedAddresses[_key]; } /** * @notice Registry address getter * @param _key key of registry address * @return address of registry address */ function getRegistry(bytes32 _key) external view returns (address) { return registries[_key]; } /** * @notice Ensures that the new address supports the AddressProviderService interface * and is pointing to this AddressProvider * @param _newAddress address to check */ function _ensureAddressProvider(address _newAddress) internal view { if (IAddressProviderService(_newAddress).addressProviderTarget() != address(this)) { revert AddressProviderUnsupported(); } } /** * @notice Checks if msg.sender is governance */ function _onlyGov() internal view { if (msg.sender != governance) revert NotGovernance(msg.sender); } /** * @notice Checks and reverts if address is null * @param addr address to check if null */ function _notNull(address addr) internal pure { if (addr == address(0)) revert NullAddress(); } }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; /** * @title Constants * @author Brahma.fi * @notice Contains constants used by multiple contracts * @dev Inflates bytecode size by approximately 560 bytes on deployment, but saves gas on runtime */ abstract contract Constants { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* REGISTRIES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @notice Key to map address of ExecutorRegistry bytes32 internal constant _EXECUTOR_REGISTRY_HASH = bytes32(uint256(keccak256("console.core.ExecutorRegistry")) - 1); /// @notice Key to map address of WalletRegistry bytes32 internal constant _WALLET_REGISTRY_HASH = bytes32(uint256(keccak256("console.core.WalletRegistry")) - 1); /// @notice Key to map address of PolicyRegistry bytes32 internal constant _POLICY_REGISTRY_HASH = bytes32(uint256(keccak256("console.core.PolicyRegistry")) - 1); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CORE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @notice Key to map address of ExecutorPlugin bytes32 internal constant _EXECUTOR_PLUGIN_HASH = bytes32(uint256(keccak256("console.core.ExecutorPlugin")) - 1); /// @notice Key to map address of ConsoleFallbackHandler bytes32 internal constant _CONSOLE_FALLBACK_HANDLER_HASH = bytes32(uint256(keccak256("console.core.FallbackHandler")) - 1); /// @notice Key to map address of Safe FallbackHandler bytes32 internal constant _SAFE_FALLBACK_HANDLER_HASH = bytes32(uint256(keccak256("safe.FallbackHandler")) - 1); /// @notice Key to map address of Safe MultiSend bytes32 internal constant _SAFE_MULTI_SEND_HASH = bytes32(uint256(keccak256("safe.MultiSend")) - 1); /// @notice Key to map address of SafeProxyFactory bytes32 internal constant _SAFE_PROXY_FACTORY_HASH = bytes32(uint256(keccak256("safe.ProxyFactory")) - 1); /// @notice Key to map address of SafeSingleton bytes32 internal constant _SAFE_SINGLETON_HASH = bytes32(uint256(keccak256("safe.Singleton")) - 1); /// @notice Key to map address of PolicyValidator bytes32 internal constant _POLICY_VALIDATOR_HASH = bytes32(uint256(keccak256("console.core.PolicyValidator")) - 1); /// @notice Key to map address of SafeDeployer bytes32 internal constant _SAFE_DEPLOYER_HASH = bytes32(uint256(keccak256("console.core.SafeDeployer")) - 1); /// @notice Key to map address of SafeEnabler bytes32 internal constant _SAFE_ENABLER_HASH = bytes32(uint256(keccak256("console.core.SafeEnabler")) - 1); /// @notice Key to map address of SafeModerator bytes32 internal constant _SAFE_MODERATOR_HASH = bytes32(uint256(keccak256("console.core.SafeModerator")) - 1); /// @notice Key to map address of SafeModeratorOverridable bytes32 internal constant _SAFE_MODERATOR_OVERRIDABLE_HASH = bytes32(uint256(keccak256("console.core.SafeModeratorOverridable")) - 1); /// @notice Key to map address of TransactionValidator bytes32 internal constant _TRANSACTION_VALIDATOR_HASH = bytes32(uint256(keccak256("console.core.TransactionValidator")) - 1); /// @notice Key to map address of ConsoleOpBuilder bytes32 internal constant _CONSOLE_OP_BUILDER_HASH = bytes32(uint256(keccak256("console.core.ConsoleOpBuilder")) - 1); /// @notice Key to map address of ExecutionBlocker bytes32 internal constant _EXECUTION_BLOCKER_HASH = bytes32(uint256(keccak256("console.core.ExecutionBlocker")) - 1); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ROLES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @notice Key to map address of Role PolicyAuthenticator bytes32 internal constant _POLICY_AUTHENTICATOR_HASH = bytes32(uint256(keccak256("console.roles.PolicyAuthenticator")) - 1); }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol"; import {EIP712} from "solady/utils/EIP712.sol"; import {AddressProviderService} from "src/core/AddressProviderService.sol"; import {PolicyRegistry} from "src/core/registries/PolicyRegistry.sol"; import {TypeHashHelper} from "src/libraries/TypeHashHelper.sol"; import {ISafeWallet, Enum} from "interfaces/external/ISafeWallet.sol"; /** * @title PolicyValidator * @author Brahma.fi * @notice Responsible for validating policy signatures for safe transactions */ contract PolicyValidator is AddressProviderService, EIP712 { error InvalidSignature(); error NoPolicyCommit(); error TxnExpired(uint32 expiryEpoch); error InvalidSignatures(); /// @notice EIP712 domain name string private constant _NAME = "PolicyValidator"; /// @notice EIP712 domain version string private constant _VERSION = "1.0"; constructor(address _addressProvider) AddressProviderService(_addressProvider) {} /** * @notice generates digest and validates signature against policies for safe transaction * @dev The POLICY_AUTHENTICATOR is expected to sign EIP712 digest generated from the following struct: * TypeHashHelper.ValidationParams, where - * txnDigest = EIP712 digest generated from struct: TypeHashHelper.ExecutionParams * policyHash = policy commit hash of the safe account * expiryEpoch = expiry timestamp * * @dev signatures = abi.encodePacked(safeSignature, validatorSignature, validatorSignatureLength, expiryEpoch) * safeSignature = safe owners signatures (arbitrary bytes length) * validatorSignature = EIP 712 digest signature (arbitrary bytes length) * validatorSignatureLength = length of `validatorSignature` (4 bytes) * expiryEpoch = expiry timestamp (4 bytes) * * @param executionParams execution params struct * @param signatures user signatures appended with validation signature * @return isSignatureValid boolean */ function isPolicySignatureValid(TypeHashHelper.ExecutionParams memory executionParams, bytes calldata signatures) external view returns (bool) { // Build transaction struct hash bytes32 executionStructHash = TypeHashHelper._buildExecutionParamsHash(executionParams); // Validate signature return isPolicySignatureValid(executionParams.account, executionStructHash, signatures); } /** * @notice generates digest and validates signature against policies for module execution * @dev signatures = abi.encodePacked(safeSignature, validatorSignature, validatorSignatureLength, expiryEpoch) * safeSignature = safe owners signatures (arbitrary bytes length) * validatorSignature = EIP 712 digest signed by `POLICY_AUTHENTICATOR`(arbitrary bytes length) * validatorSignatureLength = length of `validatorSignature` (4 bytes) * expiryEpoch = expiry timestamp (4 bytes) * Here, * The `POLICY_AUTHENTICATOR` is expected to sign the EIP 712 digest generated from following struct: * TypeHashHelper.ValidationParams - * txnDigest = EIP712 digest generated from struct: TypeHashHelper.ExecutionParams, with valid executor * policyHash = policy commit hash of the safe account * expiryEpoch = expiry timestamp * * @param account address of account to validate txn for * @param executionStructHash execution digest from ExecutorPlugin * @param signatures user signatures appended with validation signature * @return isSignatureValid boolean */ function isPolicySignatureValid(address account, bytes32 executionStructHash, bytes calldata signatures) public view returns (bool) { // Get policy hash from registry bytes32 policyHash = PolicyRegistry(policyRegistry).commitments(account); if (policyHash == bytes32(0)) { revert NoPolicyCommit(); } // Get expiry epoch and validator signature from signatures (uint32 expiryEpoch, bytes memory validatorSignature) = _decompileSignatures(signatures); // Ensure transaction has not expired if (expiryEpoch < uint32(block.timestamp)) { revert TxnExpired(expiryEpoch); } // Build validation struct hash bytes32 validationStructHash = TypeHashHelper._buildValidationParamsHash( TypeHashHelper.ValidationParams({ executionStructHash: executionStructHash, policyHash: policyHash, expiryEpoch: expiryEpoch }) ); // Build EIP712 digest with validation struct hash bytes32 txnValidityDigest = _hashTypedData(validationStructHash); address policyAuthenticator = AddressProviderService._getAuthorizedAddress(_POLICY_AUTHENTICATOR_HASH); // Empty Signature check for EOA signer if (validatorSignature.length == 0) { uint256 _codesize; assembly { _codesize := extcodesize(policyAuthenticator) } if (_codesize == 0) { // PolicyAuthenticator is an EOA and no policyAuthenticator signature is provided revert InvalidSignature(); } } // Validate signature return SignatureCheckerLib.isValidSignatureNow(policyAuthenticator, txnValidityDigest, validatorSignature); } /** * @notice Internal helper to extract validity signature from overall safe transaction signature * @dev _signatures = abi.encodePacked(safeSignature, validatorSignature, validatorSignatureLength, expiryEpoch) * safeSignature = safe owners signatures (arbitrary bytes length) * validatorSignature = EIP 712 digest signed (arbitrary bytes length) * validatorSignatureLength = length of `validatorSignature` (4 bytes) * expiryEpoch = expiry timestamp (4 bytes) * * @param _signatures packed transaction signature * @return expiryEpoch extracted expiry epoch signed by brahma backend * @return validatorSignature extracted validity signature */ function _decompileSignatures(bytes calldata _signatures) internal pure returns (uint32 expiryEpoch, bytes memory validatorSignature) { if (_signatures.length < 8) revert InvalidSignatures(); uint32 sigLength = uint32(bytes4(_signatures[_signatures.length - 8:_signatures.length - 4])); if (_signatures.length - 8 < sigLength) revert InvalidSignatures(); expiryEpoch = uint32(bytes4(_signatures[_signatures.length - 4:_signatures.length])); validatorSignature = _signatures[_signatures.length - 8 - sigLength:_signatures.length - 8]; } /** * @notice Internal helper to get EIP712 domain name and version * @return name domainName * @return version domainVersion */ function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { return (_NAME, _VERSION); } }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; import {Enum, ISafeWallet} from "interfaces/external/ISafeWallet.sol"; import {Types} from "interfaces/Types.sol"; /** * @title SafeHelper * @author Brahma.fi * @notice Helper library containing functions to interact with safe wallet */ library SafeHelper { error InvalidMultiSendInput(); error UnableToParseOperation(); /// @notice uint256(keccak256("guard_manager.guard.address")) /// @dev This refers to the storage slot where guard is stored in Safe's layout: https://github.com/safe-global/safe-contracts/blob/ff4c6761fbfae8ab8a94f36fd26bcfb2b5414eb1/contracts/base/GuardManager.sol#L77 uint256 internal constant _GUARD_STORAGE_SLOT = 33528237782592280163068556224972516439282563014722366175641814928123294921928; /// @notice uint256(keccak256("fallback_manager.handler.address")) /// @dev This refers to the storage slot where fallback handler is stored in Safe's layout: https://github.com/safe-global/safe-contracts/blob/ff4c6761fbfae8ab8a94f36fd26bcfb2b5414eb1/contracts/base/FallbackManager.sol#L14 uint256 internal constant _FALLBACK_HANDLER_STORAGE_SLOT = 49122629484629529244014240937346711770925847994644146912111677022347558721749; /** * @notice Contains hash for expected overridable guard removal calldata * @dev This is the hash of the calldata for the following function call * * abi.encodeCall(ISafeWallet.setGuard, (address(0))) = 0xe19a9dd90000000000000000000000000000000000000000000000000000000000000000 * keccak256(abi.encodeCall(ISafeWallet.setGuard, (address(0)))) = 0xc0e2c16ecb99419a40dd8b9c0b339b27acebd27c481a28cd606927aeb86f5079 */ bytes32 internal constant _GUARD_REMOVAL_CALLDATA_HASH = 0xc0e2c16ecb99419a40dd8b9c0b339b27acebd27c481a28cd606927aeb86f5079; /** * @notice Contains hash for expected overridable fallback handler removal calldata * @dev This is the hash of the calldata for the following function call * * abi.encodeCall(ISafeWallet.setFallbackHandler, (address(0))) = 0xf08a03230000000000000000000000000000000000000000000000000000000000000000 * keccak256(abi.encodeCall(ISafeWallet.setFallbackHandler, (address(0)))) = 0x5bdf8c44c012c1347b2b15694dc5cc39b899eb99e32614676b7661001be925b7 */ bytes32 internal constant _FALLBACK_REMOVAL_CALLDATA_HASH = 0x5bdf8c44c012c1347b2b15694dc5cc39b899eb99e32614676b7661001be925b7; /** * @notice Packs multiple executables into a single bytes array compatible with Safe's MultiSend contract which can be used as argument for multicall method * @dev Reference contract at https://github.com/safe-global/safe-contracts/blob/main/contracts/libraries/MultiSend.sol * @param _txns Array of executables to pack * @return packedTxns bytes array containing packed transactions */ function _packMultisendTxns(Types.Executable[] memory _txns) internal pure returns (bytes memory packedTxns) { uint256 len = _txns.length; if (len == 0) revert InvalidMultiSendInput(); uint256 i = 0; do { uint8 call = uint8(_parseOperationEnum(_txns[i].callType)); uint256 calldataLength = _txns[i].data.length; bytes memory encodedTxn = abi.encodePacked( bytes1(call), bytes20(_txns[i].target), bytes32(_txns[i].value), bytes32(calldataLength), _txns[i].data ); if (i != 0) { // If not first transaction, append to packedTxns packedTxns = abi.encodePacked(packedTxns, encodedTxn); } else { // If first transaction, set packedTxns to encodedTxn packedTxns = encodedTxn; } unchecked { ++i; } } while (i < len); } /** * @notice Gets the guard for a safe * @param safe address of safe * @return address of guard, address(0) if no guard exists */ function _getGuard(address safe) internal view returns (address) { bytes memory guardAddress = ISafeWallet(safe).getStorageAt(_GUARD_STORAGE_SLOT, 1); return address(uint160(uint256(bytes32(guardAddress)))); } /** * @notice Gets the fallback handler for a safe * @param safe address of safe * @return address of fallback handler, address(0) if no fallback handler exists */ function _getFallbackHandler(address safe) internal view returns (address) { bytes memory fallbackHandlerAddress = ISafeWallet(safe).getStorageAt(_FALLBACK_HANDLER_STORAGE_SLOT, 1); return address(uint160(uint256(bytes32(fallbackHandlerAddress)))); } /** * @notice Converts a CallType enum to an Operation enum. * @dev Reverts with UnableToParseOperation error if the CallType is not supported. * @param callType The CallType enum to be converted. * @return operation The converted Operation enum. */ function _parseOperationEnum(Types.CallType callType) internal pure returns (Enum.Operation operation) { if (callType == Types.CallType.DELEGATECALL) { operation = Enum.Operation.DelegateCall; } else if (callType == Types.CallType.CALL) { operation = Enum.Operation.Call; } else { revert UnableToParseOperation(); } } }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; import {AddressProviderService} from "../AddressProviderService.sol"; /** * @title WalletRegistry * @author Brahma.fi * @notice Registry for wallet and sub account addresses */ contract WalletRegistry is AddressProviderService { error AlreadyRegistered(); error InvalidSender(); error IsSubAccount(); event RegisterWallet(address indexed wallet); event RegisterSubAccount(address indexed wallet, address indexed subAccount); /// @notice subAccount addresses mapped to owner wallet mapping(address subAccount => address wallet) public subAccountToWallet; /// @notice wallet addresses mapped to list of subAccounts mapping(address wallet => address[] subAccountList) public walletToSubAccountList; /// @notice address of wallet mapped to boolean indicating if it's a wallet mapping(address => bool) public isWallet; constructor(address _addressProvider) AddressProviderService(_addressProvider) {} /** * @notice Registers a wallet * @dev Can only be called by wallet to register itself */ function registerWallet() external { if (isWallet[msg.sender]) revert AlreadyRegistered(); if (subAccountToWallet[msg.sender] != address(0)) revert IsSubAccount(); isWallet[msg.sender] = true; emit RegisterWallet(msg.sender); } /** * @notice Registers a sub account for a Safe * @param _wallet Console account address, owner of sub account * @param _subAccount Sub account address to register * @dev Can only be called by safe deployer */ function registerSubAccount(address _wallet, address _subAccount) external { if (msg.sender != AddressProviderService._getAuthorizedAddress(_SAFE_DEPLOYER_HASH)) revert InvalidSender(); if (subAccountToWallet[_subAccount] != address(0) || isWallet[_subAccount]) revert AlreadyRegistered(); subAccountToWallet[_subAccount] = _wallet; walletToSubAccountList[_wallet].push(_subAccount); emit RegisterSubAccount(_wallet, _subAccount); } /** * @notice sub account list getter * @dev returns sub account list associated with _wallet * @param _wallet safe address * @return list of subAccounts for wallet */ function getSubAccountsForWallet(address _wallet) external view returns (address[] memory) { return walletToSubAccountList[_wallet]; } }
/// SPDX-License-Identifier: BUSL-1.1 /// Copyright (C) 2023 Brahma.fi pragma solidity 0.8.19; /** * @title TypeHashHelper * @author Brahma.fi * @notice Helper library containing functions to build EIP712 struct and type hashes */ library TypeHashHelper { /** * @notice Structural representation of execution details * @param operation type of operation * @param to address to send tx to * @param account address of safe * @param executor address of executor if executed via executor plugin, address(0) if executed via execTransaction * @param gasToken address of token used for gas * @param refundReceiver address of receiver of gas payment * @param value txn value * @param nonce txn nonce * @param safeTxGas gas that should be used for safe txn * @param baseGas gas cost independent of txn cost * @param gasPrice gas price in current block * @param data txn callData */ struct ExecutionParams { uint8 operation; address to; address account; address executor; address gasToken; address refundReceiver; uint256 value; uint256 nonce; uint256 safeTxGas; uint256 baseGas; uint256 gasPrice; bytes data; } /** * @notice Type of validation struct to hash * @param expiryEpoch max time till validity of the signature * @param executionStructHash txn digest generated using TypeHashHelper._buildExecutionParamsHash() * @param policyHash policy commit hash of the safe account */ struct ValidationParams { uint32 expiryEpoch; bytes32 executionStructHash; bytes32 policyHash; } /** * @notice EIP712 typehash for execution params data * @dev keccak256("ExecutionParams(uint8 operation,address to,address account,address executor,address gasToken,address refundReceiver,uint256 value,uint256 nonce,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,bytes data)"); */ bytes32 public constant EXECUTION_PARAMS_TYPEHASH = 0x483ad580f0a8d7881e792d04b2128f3b214b18aa7336126dc2e77a59752bd6f5; /** * @notice EIP712 typehash for validation data * @dev keccak256("ValidationParams(uint32 expiryEpoch,ExecutionParams executionParams,bytes32 policyHash)ExecutionParams(uint8 operation,address to,address account,address executor,address gasToken,address refundReceiver,uint256 value,uint256 nonce,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,bytes data)"); */ bytes32 public constant VALIDATION_PARAMS_TYPEHASH = 0x37af4ddfcab5e0a0d11676ce89534ca59ffac1c30a5a4cc21f840d2f4704a952; /** * @notice Builds EIP712 execution struct hash * @param txn execution params struct * @return executionStructHash */ function _buildExecutionParamsHash(ExecutionParams memory txn) internal pure returns (bytes32) { return keccak256( abi.encode( EXECUTION_PARAMS_TYPEHASH, txn.operation, txn.to, txn.account, txn.executor, txn.gasToken, txn.refundReceiver, txn.value, txn.nonce, txn.safeTxGas, txn.baseGas, txn.gasPrice, keccak256(txn.data) ) ); } /** * @notice Builds EIP712 validation struct hash * @param validation validation params struct * @return validationStructHash */ function _buildValidationParamsHash(ValidationParams memory validation) internal pure returns (bytes32) { return keccak256( abi.encode( VALIDATION_PARAMS_TYPEHASH, validation.expiryEpoch, validation.executionStructHash, validation.policyHash ) ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Signature verification helper that supports both ECDSA signatures from EOAs /// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol) /// /// @dev Note: /// - The signature checking functions use the ecrecover precompile (0x1). /// - The `bytes memory signature` variants use the identity precompile (0x4) /// to copy memory internally. /// - Unlike ECDSA signatures, contract signatures are revocable. /// /// WARNING! Do NOT use signatures as unique identifiers. /// Please use EIP712 with a nonce included in the digest to prevent replay attacks. /// This implementation does NOT check if a signature is non-malleable. library SignatureCheckerLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIGNATURE CHECKING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `signature` is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) if eq(mload(signature), 65) { mstore(0x00, hash) mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x40, mload(add(signature, 0x20))) // `r`. mstore(0x60, mload(add(signature, 0x40))) // `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. // Copy the `signature` over. let n := add(0x20, mload(signature)) pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(returndatasize(), 0x44), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) break } } } /// @dev Returns whether `signature` is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) if eq(signature.length, 65) { mstore(0x00, hash) mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), signature.length) // Copy the `signature` over. calldatacopy(add(m, 0x64), signature.offset, signature.length) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(signature.length, 0x64), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) break } } } /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), mload(0x60)) // `s`. mstore8(add(m, 0xa4), mload(0x20)) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } } /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) mstore(0x00, hash) mstore(0x20, and(v, 0xff)) // `v`. mstore(0x40, r) // `r`. mstore(0x60, s) // `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), s) // `s`. mstore8(add(m, 0xa4), v) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1271 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `signature` is valid for `hash` /// for an ERC1271 `signer` contract. function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. // Copy the `signature` over. let n := add(0x20, mload(signature)) pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(returndatasize(), 0x44), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /// @dev Returns whether `signature` is valid for `hash` /// for an ERC1271 `signer` contract. function isValidERC1271SignatureNowCalldata( address signer, bytes32 hash, bytes calldata signature ) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), signature.length) // Copy the `signature` over. calldatacopy(add(m, 0x64), signature.offset, signature.length) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(signature.length, 0x64), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash` /// for an ERC1271 `signer` contract. function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`. mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash` /// for an ERC1271 `signer` contract. function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), s) // `s`. mstore8(add(m, 0xa4), v) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HASHING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an Ethereum Signed Message, created from a `hash`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) /// JSON-RPC method as part of EIP-191. function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, hash) // Store into scratch space for keccak256. mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. } } /// @dev Returns an Ethereum Signed Message, created from `s`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) /// JSON-RPC method as part of EIP-191. /// Note: Supports lengths of `s` up to 999999 bytes. function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let sLength := mload(s) let o := 0x20 mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. mstore(0x00, 0x00) // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. for { let temp := sLength } 1 {} { o := sub(o, 1) mstore8(o, add(48, mod(temp, 10))) temp := div(temp, 10) if iszero(temp) { break } } let n := sub(0x3a, o) // Header length: `26 + 32 - o`. // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) mstore(s, sLength) // Restore the length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes. function emptySignature() internal pure returns (bytes calldata signature) { /// @solidity memory-safe-assembly assembly { signature.length := 0 } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Contract for EIP-712 typed structured data hashing and signing. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol) /// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol) /// /// @dev Note, this implementation: /// - Uses `address(this)` for the `verifyingContract` field. /// - Does NOT use the optional EIP-712 salt. /// - Does NOT use any EIP-712 extensions. /// This is for simplicity and to save gas. /// If you need to customize, please fork / modify accordingly. abstract contract EIP712 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS AND IMMUTABLES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. bytes32 internal constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; address private immutable _cachedThis; uint256 private immutable _cachedChainId; bytes32 private immutable _cachedNameHash; bytes32 private immutable _cachedVersionHash; bytes32 private immutable _cachedDomainSeparator; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTRUCTOR */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Cache the hashes for cheaper runtime gas costs. /// In the case of upgradeable contracts (i.e. proxies), /// or if the chain id changes due to a hard fork, /// the domain separator will be seamlessly calculated on-the-fly. constructor() { _cachedThis = address(this); _cachedChainId = block.chainid; string memory name; string memory version; if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion(); bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name)); bytes32 versionHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version)); _cachedNameHash = nameHash; _cachedVersionHash = versionHash; bytes32 separator; if (!_domainNameAndVersionMayChange()) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Load the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), versionHash) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) separator := keccak256(m, 0xa0) } } _cachedDomainSeparator = separator; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* FUNCTIONS TO OVERRIDE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Please override this function to return the domain name and version. /// ``` /// function _domainNameAndVersion() /// internal /// pure /// virtual /// returns (string memory name, string memory version) /// { /// name = "Solady"; /// version = "1"; /// } /// ``` /// /// Note: If the returned result may change after the contract has been deployed, /// you must override `_domainNameAndVersionMayChange()` to return true. function _domainNameAndVersion() internal view virtual returns (string memory name, string memory version); /// @dev Returns if `_domainNameAndVersion()` may change /// after the contract has been deployed (i.e. after the constructor). /// Default: false. function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HASHING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the EIP-712 domain separator. function _domainSeparator() internal view virtual returns (bytes32 separator) { if (_domainNameAndVersionMayChange()) { separator = _buildDomainSeparator(); } else { separator = _cachedDomainSeparator; if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator(); } } /// @dev Returns the hash of the fully encoded EIP-712 message for this domain, /// given `structHash`, as defined in /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct. /// /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message: /// ``` /// bytes32 digest = _hashTypedData(keccak256(abi.encode( /// keccak256("Mail(address to,string contents)"), /// mailTo, /// keccak256(bytes(mailContents)) /// ))); /// address signer = ECDSA.recover(digest, signature); /// ``` function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) { bytes32 separator; if (_domainNameAndVersionMayChange()) { separator = _buildDomainSeparator(); } else { separator = _cachedDomainSeparator; if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator(); } /// @solidity memory-safe-assembly assembly { // Compute the digest. mstore(0x00, 0x1901000000000000) // Store "\x19\x01". mstore(0x1a, separator) // Store the domain separator. mstore(0x3a, structHash) // Store the struct hash. digest := keccak256(0x18, 0x42) // Restore the part of the free memory slot that was overwritten. mstore(0x3a, 0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EIP-5267 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev See: https://eips.ethereum.org/EIPS/eip-5267 function eip712Domain() public view virtual returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { fields = hex"0f"; // `0b01111`. (name, version) = _domainNameAndVersion(); chainId = block.chainid; verifyingContract = address(this); salt = salt; // `bytes32(0)`. extensions = extensions; // `new uint256[](0)`. } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the EIP-712 domain separator. function _buildDomainSeparator() private view returns (bytes32 separator) { bytes32 nameHash; bytes32 versionHash; if (_domainNameAndVersionMayChange()) { (string memory name, string memory version) = _domainNameAndVersion(); nameHash = keccak256(bytes(name)); versionHash = keccak256(bytes(version)); } else { nameHash = _cachedNameHash; versionHash = _cachedVersionHash; } /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Load the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), versionHash) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) separator := keccak256(m, 0xa0) } } /// @dev Returns if the cached domain separator has been invalidated. function _cachedDomainSeparatorInvalidated() private view returns (bool result) { uint256 cachedChainId = _cachedChainId; address cachedThis = _cachedThis; /// @solidity memory-safe-assembly assembly { result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis))) } } }
/// SPDX-License-Identifier: 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; } }
// 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; } }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "safe-contracts/=lib/safe-contracts/contracts/", "solady/=lib/solady/src/", "solmate/=lib/solady/lib/solmate/src/", "solidity-bytes-utils/=lib/solidity-bytes-utils/contracts/" ], "optimizer": { "enabled": true, "runs": 20000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_addressProvider","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAddressProvider","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"NotGovernance","type":"error"},{"inputs":[],"name":"addressProvider","outputs":[{"internalType":"contract AddressProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addressProviderTarget","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"txHash","type":"bytes32"},{"internalType":"bool","name":"success","type":"bool"}],"name":"checkAfterExecution","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"},{"internalType":"uint256","name":"safeTxGas","type":"uint256"},{"internalType":"uint256","name":"baseGas","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"address","name":"gasToken","type":"address"},{"internalType":"address payable","name":"refundReceiver","type":"address"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"address","name":"msgSender","type":"address"}],"name":"checkTransaction","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executorRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"policyRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"walletRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101006040523480156200001257600080fd5b5060405162000e2f38038062000e2f8339810160408190526200003591620002b9565b806001600160a01b0381166200005e5760405163186b216f60e11b815260040160405180910390fd5b6001600160a01b038116608081905263e51fd7a66200009f60017f70dc150361dabd9d041fabc7ce344e2a2f31a73e202f37c7ba3188518a32c207620002eb565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa158015620000dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001029190620002b9565b6001600160a01b0390811660a0526080511663e51fd7a66200014660017f8547df78c054d57ec09e2772223da2229912d59e65dbd44da39709b40d8c40c3620002eb565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa15801562000183573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001a99190620002b9565b6001600160a01b0390811660c0526080511663e51fd7a6620001ed60017f25c3a50f208295151157a1115daca4d2b545fb99d7e2f46dca323d4edca762f2620002eb565b60405160e083901b6001600160e01b03191681526004810191909152602401602060405180830381865afa1580156200022a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002509190620002b9565b6001600160a01b031660e05260a0516200026a906200028e565b60c05162000278906200028e565b60e05162000286906200028e565b505062000313565b6001600160a01b038116620002b65760405163e6c4247b60e01b815260040160405180910390fd5b50565b600060208284031215620002cc57600080fd5b81516001600160a01b0381168114620002e457600080fd5b9392505050565b818103818111156200030d57634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c05160e051610ad56200035a60003960006101a20152600060ba0152600061017b0152600081816101030152818161012c01526104c70152610ad56000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806375f0bb521161005b57806375f0bb521461014e5780639327136814610163578063ab7aa6ad14610176578063b1cebbe01461019d57600080fd5b806301ffc9a71461008d5780631c4dd7d0146100b557806321b1e480146101015780632954018c14610127575b600080fd5b6100a061009b3660046105a7565b6101c4565b60405190151581526020015b60405180910390f35b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ac565b7f00000000000000000000000000000000000000000000000000000000000000006100dc565b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b61016161015c366004610706565b61025d565b005b6101616101713660046107eb565b6103d5565b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b6100dc7f000000000000000000000000000000000000000000000000000000000000000081565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe6d7a83a00000000000000000000000000000000000000000000000000000000148061025757507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b61029061028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b610495565b73ffffffffffffffffffffffffffffffffffffffff166361dbe82e6040518061018001604052808b60018111156102c9576102c961085a565b81526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018d81526020018a81526020018981526020018881526020018c8152602001858152506040518263ffffffff1660e01b81526004016103989190610928565b60006040518083038186803b1580156103b057600080fd5b505afa1580156103c4573d6000803e3d6000fd5b505050505050505050505050505050565b61040361028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b6040517fa3fbfc8700000000000000000000000000000000000000000000000000000000815260048101849052821515602482015233604482015273ffffffffffffffffffffffffffffffffffffffff919091169063a3fbfc879060640160006040518083038186803b15801561047957600080fd5b505afa15801561048d573d6000803e3d6000fd5b505050505050565b6040517f2bf84475000000000000000000000000000000000000000000000000000000008152600481018290526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632bf8447590602401602060405180830381865afa158015610523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105479190610a82565b905061055281610557565b919050565b73ffffffffffffffffffffffffffffffffffffffff81166105a4576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6000602082840312156105b957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146105e957600080fd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146105a457600080fd5b8035610552816105f0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261065d57600080fd5b813567ffffffffffffffff808211156106785761067861061d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156106be576106be61061d565b816040528381528660208588010111156106d757600080fd5b836020870160208301376000602085830101528094505050505092915050565b80356002811061055257600080fd5b60008060008060008060008060008060006101608c8e03121561072857600080fd5b6107318c610612565b9a5060208c0135995067ffffffffffffffff8060408e0135111561075457600080fd5b6107648e60408f01358f0161064c565b995061077260608e016106f7565b985060808d0135975060a08d0135965060c08d0135955061079560e08e01610612565b94506107a46101008e01610612565b9350806101208e013511156107b857600080fd5b506107ca8d6101208e01358e0161064c565b91506107d96101408d01610612565b90509295989b509295989b9093969950565b600080604083850312156107fe57600080fd5b823591506020830135801515811461081557600080fd5b809150509250929050565b81810381811115610257577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600281106108c0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000815180845260005b818110156108ea576020818501810151868301820152016108ce565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815261093a602082018351610889565b60006020830151610963604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015173ffffffffffffffffffffffffffffffffffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015173ffffffffffffffffffffffffffffffffffffffff811660a08401525060a083015173ffffffffffffffffffffffffffffffffffffffff811660c08401525060c083015160e083810191909152830151610100808401919091528301516101208084019190915283015161014080840191909152830151610180610160808501829052610a3e6101a08601846108c4565b908601518582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183870152909250610a7883826108c4565b9695505050505050565b600060208284031215610a9457600080fd5b81516105e9816105f056fea264697066735822122052f6071273e95e97ac5e986303b39323c69c51eb6304e3c623005f15d0d7c38b64736f6c634300081300330000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e229
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100885760003560e01c806375f0bb521161005b57806375f0bb521461014e5780639327136814610163578063ab7aa6ad14610176578063b1cebbe01461019d57600080fd5b806301ffc9a71461008d5780631c4dd7d0146100b557806321b1e480146101015780632954018c14610127575b600080fd5b6100a061009b3660046105a7565b6101c4565b60405190151581526020015b60405180910390f35b6100dc7f000000000000000000000000e2033fd8a642e67f11df2c5567023c1900e440f881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ac565b7f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e2296100dc565b6100dc7f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22981565b61016161015c366004610706565b61025d565b005b6101616101713660046107eb565b6103d5565b6100dc7f00000000000000000000000027fbc3310907c0425ea09115397a40dddc15464181565b6100dc7f0000000000000000000000000145f9674b22be444c9f0e5e2a7761643fe785be81565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe6d7a83a00000000000000000000000000000000000000000000000000000000148061025757507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b61029061028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b610495565b73ffffffffffffffffffffffffffffffffffffffff166361dbe82e6040518061018001604052808b60018111156102c9576102c961085a565b81526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018d81526020018a81526020018981526020018881526020018c8152602001858152506040518263ffffffff1660e01b81526004016103989190610928565b60006040518083038186803b1580156103b057600080fd5b505afa1580156103c4573d6000803e3d6000fd5b505050505050505050505050505050565b61040361028b60017f28c61107d4cd3a85ca89b185e44bf7402301acc30bf1bcf06b0e5c9d61063bdd610820565b6040517fa3fbfc8700000000000000000000000000000000000000000000000000000000815260048101849052821515602482015233604482015273ffffffffffffffffffffffffffffffffffffffff919091169063a3fbfc879060640160006040518083038186803b15801561047957600080fd5b505afa15801561048d573d6000803e3d6000fd5b505050505050565b6040517f2bf84475000000000000000000000000000000000000000000000000000000008152600481018290526000907f0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e22973ffffffffffffffffffffffffffffffffffffffff1690632bf8447590602401602060405180830381865afa158015610523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105479190610a82565b905061055281610557565b919050565b73ffffffffffffffffffffffffffffffffffffffff81166105a4576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6000602082840312156105b957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146105e957600080fd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146105a457600080fd5b8035610552816105f0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261065d57600080fd5b813567ffffffffffffffff808211156106785761067861061d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156106be576106be61061d565b816040528381528660208588010111156106d757600080fd5b836020870160208301376000602085830101528094505050505092915050565b80356002811061055257600080fd5b60008060008060008060008060008060006101608c8e03121561072857600080fd5b6107318c610612565b9a5060208c0135995067ffffffffffffffff8060408e0135111561075457600080fd5b6107648e60408f01358f0161064c565b995061077260608e016106f7565b985060808d0135975060a08d0135965060c08d0135955061079560e08e01610612565b94506107a46101008e01610612565b9350806101208e013511156107b857600080fd5b506107ca8d6101208e01358e0161064c565b91506107d96101408d01610612565b90509295989b509295989b9093969950565b600080604083850312156107fe57600080fd5b823591506020830135801515811461081557600080fd5b809150509250929050565b81810381811115610257577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600281106108c0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000815180845260005b818110156108ea576020818501810151868301820152016108ce565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815261093a602082018351610889565b60006020830151610963604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015173ffffffffffffffffffffffffffffffffffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015173ffffffffffffffffffffffffffffffffffffffff811660a08401525060a083015173ffffffffffffffffffffffffffffffffffffffff811660c08401525060c083015160e083810191909152830151610100808401919091528301516101208084019190915283015161014080840191909152830151610180610160808501829052610a3e6101a08601846108c4565b908601518582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183870152909250610a7883826108c4565b9695505050505050565b600060208284031215610a9457600080fd5b81516105e9816105f056fea264697066735822122052f6071273e95e97ac5e986303b39323c69c51eb6304e3c623005f15d0d7c38b64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e229
-----Decoded View---------------
Arg [0] : _addressProvider (address): 0x6FCf22e22f736D9ead75de8A1f12cA869287E229
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000006fcf22e22f736d9ead75de8a1f12ca869287e229
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.