Platform: Code4rena
Start Date: 13/10/2023
Pot Size: $31,250 USDC
Total HM: 4
Participants: 51
Period: 7 days
Judge: 0xsomeone
Id: 295
League: ETH
Rank: 27/51
Findings: 1
Award: $113.54
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: niroh
Also found by: 0xDetermination, 0xSmartContract, 0xbrett8571, 0xdice91, 0xweb3boy, Bauchibred, Bube, DadeKuma, JCK, K42, LinKenji, Myd, SAAJ, ZanyBonzy, albahaca, castle_chain, catellatech, digitizeworx, emerald7017, fouzantanveer, hunter_w3b, invitedtea, m4ttm, rahul, xiao
113.5407 USDC - $113.54
Brahma Console v2, is an orchestration layer designed to enhance the DeFi (Decentralized Finance) experience within smart contract wallets. It is built on top of the "Safe" platform and offers user-configurable automation and strategies for frequent DeFi interactions, all at a low cost facilitated by Brahma.
Brahma Console allows users to automate tasks without requiring them to relinquish custody of their funds. This means that users retain full control of their assets from their own wallet. Brahma also has the availability of "SafeSub-accounts", which reduce risk by isolating user interactions from the main protocol.
TypeHashHelper Library:
SafeHelper Library:
Contract 3 - TransactionValidator
Contract 4 - SafeModeratorOverridable
These contracts collectively contribute to the security and policy compliance of the Brahma.fi system, ensuring that transactions are validated and adhere to the required policies, reducing the risk of unauthorized actions and providing a comprehensive framework for handling various types of transactions.
Contract 5 - SafeEnabler
Contract 6 - SafeModerator
Contract 7 - Constants
Contract 8 - ConsoleFallbackHandler
These contracts play a crucial role in the Brahma ecosystem by enabling the management of executors, wallets, policies, signature validations, and the configuration of modules and guards, contributing to the security and functionality of Safe accounts.
Contract 9 - AddressProvider
RegistryInitialised
: Issued when a registry is initialized in the AddressProvider.AuthorizedAddressInitialised
: Issued when an authorized address is initialized in the AddressProvider.GovernanceTransferRequested
: Issued when a governance change is requested.GovernanceTransferred
: Issued when a governance change is successfully completed.The tenth contract, called "PolicyValidator," is responsible for validating policy signatures for secure transactions and module executions. It ensures that transactions comply with policies before execution:
Contract 10 - PolicyValidator
InvalidSignature
: Issued when a signature is invalid.NoPolicyCommit
: Issued when a policy commitment is not available for the account.TxnExpired
: Issued when a transaction has expired according to the expiration time.InvalidSignatures
: Issued when signatures are invalid.The eleventh contract, "PolicyRegistry," is a registry of policy commitments for wallets and subaccounts, allowing authorized entities to set and update policy commitments:
Contract 11 - PolicyRegistry
UpdatedPolicyCommit
: Issued when a policy commitment for an account is updated.PolicyCommitInvalid
: Issued when an attempt is made to set an invalid policy commitment.UnauthorizedPolicyUpdate
: Issued when an unauthorized entity attempts to update a policy commitment.Finally, the twelfth contract, "ExecutorRegistry," is a registry of executors associated with subaccounts, allowing subaccount owners to register or remove executors:
Contract 12 - ExecutorRegistry
RegisterExecutor
: Issued when an executor is registered for a subaccount.DeRegisterExecutor
: Issued when an executor is removed from a subaccount.NotOwnerWallet
: Issued when someone who is not the owner attempts to take action on a subaccount.AlreadyExists
: Issued when an attempt is made to register an executor that already exists.DoesNotExist
: Issued when an attempt is made to remove an executor that doesn't exist.Contract number 13 is the "WalletRegistry":
Contract 13 - WalletRegistry
their associated subaccounts. It provides functions to register wallets and subaccounts, query the list of subaccounts for a wallet, and verify ownership relationships between wallets and subaccounts.
Key Functions:
registerWallet()
: Allows the registration of a wallet. It can only be called by the safe deployer or the wallet itself. It checks if the wallet is already registered and if the address is a subaccount.
registerSubAccount(address _wallet, address _subAccount)
: Allows the registration of a subaccount for a wallet. It can only be called by the safe deployer. It checks if the subaccount is already registered.
getSubAccountsForWallet(address _wallet)
: Allows getting the list of subaccounts associated with a wallet.
isOwner(address _wallet, address _subAccount)
: Allows verifying if a wallet is the owner of a specific subaccount.
Contract number 14 is the "AddressProviderService":
Contract 14 - AddressProviderService
Key Functions:
addressProviderTarget()
: Allows obtaining the AddressProvider address associated with the inheriting contract.
_getRegistry(bytes32 _key)
: Allows obtaining a registry address from the AddressProvider using a keccak256 key corresponding to the registry.
_getAuthorizedAddress(bytes32 _key)
: Allows obtaining an authorized address from the AddressProvider using a keccak256 key corresponding to the authorized address.
_onlyGov()
: Checks if the message sender is the "governance" according to the AddressProvider.
_notNull(address _addr)
: Checks if an address is not null.
Contract number 15 is the "ExecutorPlugin":
Contract 15 - ExecutorPlugin
ExecutorPlugin
is responsible for executing transactions in Console accounts with module permissions. Executors can send execution requests, which are performed as module transactions in Console accounts. It validates the executor's signature, checks the executor's validity for the account, and verifies the execution policy using the TransactionValidator contract. If all checks are successful, it executes the transaction and handles return data.Key Functions:
executeTransaction(ExecutionRequest calldata execRequest)
: Allows executors to send execution requests for transactions. It validates the execution request, and if valid, executes the transaction as a module in the Console account.
_executeTxnAsModule(address _account, Types.Executable memory _executable)
: Internal function to execute the transaction in a Console account as a module.
_validateExecutionRequest(ExecutionRequest calldata execRequest)
: Internal function to validate the execution request. It verifies the executor's signature, the executor's validity for the account, and the execution policy.
_domainNameAndVersion()
: Internal function to get the EIP712 domain name and version.
These contracts are essential components of the ecosystem and perform critical functions in the Console system. If you have any specific questions about any of these contracts or need more details, feel free to ask.
Contract 16 - SafeDeployer
Key Functions:
deployConsoleAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
: Allows deploying a new Console account with or without a policy commitment and registers it. It enables the configuration of a Console account with a list of owners, a threshold, a policy commitment, and a salt value.
deploySubAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
: Allows deploying a new subaccount with a policy commitment and registers it. Subaccounts are activated as modules in Console accounts. It also verifies if the message sender is a registered wallet before deploying a subaccount.
_setupConsoleAccount(address[] memory _owners, uint256 _threshold, bool _policyHashValid)
: Private function that configures a Console account with setup transactions. Depending on whether a policy commitment is provided, additional transactions are added to enable a guardian in the Console account.
_setupSubAccount(address[] memory _owners, uint256 _threshold, address _consoleAccount)
: Private function that configures a subaccount with setup transactions. It enables the Console account as a module in the subaccount and also enables a guardian in the subaccount.
_createSafe(address[] calldata _owners, bytes memory _initializer, bytes32 _salt)
: Private function to create a new Gnosis Safe. It uses the GnosisProxyFactory contract to create an instance of Gnosis Safe with an initializer and a salt value. It generates a nonce based on the owners and the provided salt and handles nonce collisions if an account with the same nonce already exists.
_genNonce(bytes32 _ownersHash, bytes32 _salt)
: Private function that generates a nonce for deploying a Safe account based on the hash of the owners and the salt value.
This contract is fundamental for creating and configuring Console accounts and subaccounts in the Console ecosystem. It enables the creation of deterministic accounts based on the provided configuration and ensures that Safe accounts are deployed securely and without nonce collisions.
High-level overview : I analyzed the overall codebase in one iteration to get a high-level understanding of the code structure and functionality.
Documentation review : I studied the documentation to understand the purpose of each contract, its functionality, and how it is connected with other contracts.
Literature review : I read old audits and known findings, as well as the bot races findings.
Testing setup : I set up my testing environment and ran the tests to ensure that all tests passed. Used yarn and hardhat to test this protocol or foundry as well.
Detailed analysis : I started with the detailed analysis of the code base, line by line. I took the necessary notes to ask some questions to the sponsors.
graph TD subgraph Brahma Contracts SafeDeployer["SafeDeployer"] ExecutorRegistry["ExecutorRegistry"] PolicyValidator["PolicyValidator"] PolicyRegistry["PolicyRegistry"] ExecutorPlugin["ExecutorPlugin"] end subgraph Registry Contracts WalletRegistry["WalletRegistry"] end Users --> SafeDeployer Users --> ExecutorRegistry Users --> PolicyValidator Users --> WalletRegistry Users --> PolicyRegistry Users --> ExecutorPlugin Governance/Admin --> SafeDeployer Governance/Admin --> ExecutorRegistry Governance/Admin --> PolicyValidator Governance/Admin --> WalletRegistry Governance/Admin --> PolicyRegistry Governance/Admin --> ExecutorPlugin
Here's an analysis of potential systemic and centralization risks in the provided contracts:
ExecutorRegistry:
Systemic Risks:
Centralization Risks:
WalletRegistry:
Systemic Risks:
Centralization Risks:
PolicyRegistry:
Systemic Risks:
Centralization Risks:
DefaultCallbackHandler:
Systemic Risks:
Centralization Risks:
SafeEnabler:
Systemic Risks:
Centralization Risks:
SafeModerator:
Systemic Risks:
Centralization Risks:
To mitigate these risks, it's important to conduct thorough security audits, implement robust access control mechanisms, maintain transparency in the governance of these contracts, and ensure that proper upgradeability processes are in place. Additionally, it's crucial to continuously monitor and address vulnerabilities and emerging threats to maintain the security and integrity of the Brahma ecosystem.
Our insights from the audit of the Brahma ecosystem contracts have been enlightening. We have learned about the modular and scalable architecture, which provides flexibility for extending the system's functionality. Security has been a primary focus, and the contracts incorporate various checks and validations to protect user funds.
The management of executors and wallets through ExecutorRegistry and WalletRegistry is vital for controlling and tracking entities interacting with the system. Proper callback handling, as demonstrated by DefaultCallbackHandler, is essential when interacting with external contracts or modules.
Transaction validation, a critical aspect handled by PolicyValidator and SafeModerator, underscores the importance of ensuring that transactions adhere to predefined policies and security rules before execution.
It's imperative to stay informed about the latest documentation and updates related to these contracts, as the Brahma ecosystem evolves. Overall, the audit has provided valuable insights into the secure management of digital assets and the need for a deep understanding of access control, policy management, and transaction validation in the context of the Brahma contracts.
What the project can add in the understanding of Security;
By distributing the project to testnets, ensuring that the audits are carried out in onchain audit.
Pause Mechanism This is a chaotic situation, which can be thought of as a choice between decentralization and security.
Add On-Chain Monitoring System; If On-Chain Monitoring systems such as Forta are added to the project, its security will increase.
For example ; This bot tracks any DEFI transactions in which wrapping, unwrapping, swapping, depositing, or withdrawals occur over a threshold amount. If transactions occur with unusually high token amounts, the bot sends out an alert. https://app.forta.network/bot/0x7f9afc392329ed5a473bcf304565adf9c2588ba4bc060f7d215519005b8303e3
The coverage test is 100%, but the team should be able to add invariant tests to increase the safety.
In general, the Brahma project exhibits an interesting and well-developed architecture we believe the team has done a good job regarding the code, but the identified risks need to be addressed, and measures should be implemented to protect the protocol from potential malicious use cases. It is also highly recommended that the team continues to invest in security measures such as mitigation reviews, audits, and bug bounty programs to maintain the security and reliability of the project.
25 hours
#0 - c4-pre-sort
2023-10-22T21:27:34Z
raymondfam marked the issue as sufficient quality report
#1 - alex-ppg
2023-10-27T13:14:45Z
While certain portions of the analysis appear generic, the statements made by it are correct and the diagram produced is an interesting variation of traditional analysis reports.
#2 - c4-judge
2023-10-27T13:14:49Z
alex-ppg marked the issue as grade-a