Platform: Code4rena
Start Date: 09/01/2024
Pot Size: $100,000 USDC
Total HM: 13
Participants: 28
Period: 28 days
Judge: 0xsomeone
Total Solo HM: 8
Id: 319
League: ETH
Rank: 22/28
Findings: 1
Award: $100.10
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: aariiif
Also found by: 0xepley, LinKenji, Sathish9098, ZanyBonzy, catellatech, fouzantanveer, hassanshakeel13, hunter_w3b, invitedtea, yongskiws
100.0972 USDC - $100.10
The Opus Protocol, designed to operate on the StarkNet blockchain, represents a significant advancement in the decentralized finance (DeFi) space, introducing a modular, scalable, and secure framework for creating and managing synthetic assets and collateralized debt positions (CDPs). This protocol leverages the power of StarkNet's layer 2 scaling solutions to offer improved efficiency, reduced transaction costs, and enhanced computational capabilities.
Opus protocol, by incorporating complex financial mechanisms such as dynamic yield adjustments, collateralization ratios, and multi-asset liquidity provisions, introduces risks related to the accurate execution and understanding of these mechanisms. Complex interactions between these elements could lead to scenarios where the protocol behaves unpredictably under stress conditions or specific market events.
Dynamic Yield Adjustments
Dynamic yield adjustments are mechanisms designed to respond to changing market conditions by altering the rewards for liquidity providers or borrowers. While they aim to balance supply and demand within the protocol and incentivize user participation, these adjustments can lead to unpredictability.
Multi-Asset Liquidity Provisions
Supporting multiple assets for collateral and borrowing increases the protocol's flexibility and attractiveness to users. However, it also introduces complexity in managing the relative value and liquidity of each asset.
Opus protocol, contracts are designed to fulfill specific functions, such as managing assets, handling permissions, or facilitating transactions. Each contract might depend on the state or outputs of other contracts to function correctly.
State Transitions
: Consider a contract that handles asset collateralization. It might transition assets between different states (e.g., active, liquidated). These transitions depend on price feeds from oracles. If an oracle contract provides incorrect data, it might trigger incorrect state transitions, such as unjustified liquidations.
State Transitions Scenario
The handling of state transitions in a contract like CollateralManager would involve interaction with a PriceOracle contract to fetch asset prices for collateral ratio calculations. Cairo's design around StarkNet's account model and asynchronous calls between contracts adds layers to how these interactions are structured.
@external func check_liquidation{ syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr }(user: felt) -> (): let (assetPrice) = PriceOracle.get_current_price(user_asset) let collateralRatio = calculate_collateral_ratio(user, assetPrice) if collateralRatio < liquidation_threshold: liquidate_collateral(user) end return () end
if the PriceOracle.get_current_price function is manipulated to return a lower asset price, the CollateralManager could wrongly trigger liquidations. Given Cairo's asynchronous nature, these interactions are more nuanced, relying on message passing and potentially external calls that complete in subsequent transactions, making tracking and debugging such issues more complex.
Nested Calls
: Contracts often interact with each other through nested calls, where one contract calls another, which in turn might call another contract. This chain of interactions can lead to situations where unexpected conditions in one contract propagate through the system, potentially causing cascading failures or unintended state changes.
Nested Calls Scenario
Considering a nested call scenario in Cairo, where a StakingContract calls a RewardsContract based on prices from a PriceOracle, the code could resemble:
@external func distribute_rewards{ syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr }(user: felt) -> (): let (assetPrice) = PriceOracle.get_current_price(staked_asset) let rewardAmount = calculate_reward(user, assetPrice) RewardsContract.payout(user, rewardAmount) return () end
Here, a fault in PriceOracle.get_current_price affecting the reward calculation would propagate through StakingContract.distribute_rewards into RewardsContract.payout, potentially leading to improper reward distribution. The asynchronous contract calls in StarkNet, where state changes are finalized in subsequent transactions, could complicate the identification of the initial fault and its resolution.
Oracle manipulation presents a critical risk in Opus, where financial decisions—ranging from asset valuation to smart contract executions—are heavily dependent on external data inputs. The integrity and accuracy of this data are paramount, as they directly impact the protocol's security and the users' assets.
Single Point of Failure
: Relying on a single oracle or a single data source introduces a critical point of failure. If this oracle is compromised, either maliciously or due to a technical glitch, it could feed incorrect data to the protocol, leading to erroneous contract executions.
Manipulation Attacks
: Even without direct tampering, oracles are susceptible to manipulation. For instance, an attacker might temporarily influence the market price of an asset on exchanges that an oracle uses as data sources, tricking the protocol into executing actions based on this skewed data.
@view func is_loan_safe{ syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr }(loan_id: felt) -> (is_safe: felt): alloc_locals let (collateral_value) = PriceOracle.get_collateral_value(loan_id) let (loan_amount) = LoanStorage.get_loan_amount(loan_id) # Assume collateral_value and loan_amount are in the same units let is_safe = collateral_value > loan_amount * COLLATERALIZATION_RATIO return (is_safe) end
If PriceOracle is compromised, whether through a hack or a bug, it might return artificially inflated collateral values. Consequently, LoanManager.is_loan_safe would incorrectly assess risky loans as safe, potentially leading to under-collateralized loans not being liquidated in a timely manner, exposing the protocol to losses if the market moves unfavorably.
An attacker aims to take out a loan and then manipulate the market price of the collateral asset on the exchanges that PriceOracle uses for data. By temporarily driving up the price, the attacker increases the loan's perceived safety, allowing for a larger loan amount. After securing the loan, the attacker sells off the collateral on the open market, crashing its price back to normal levels, effectively walking away with more funds than the collateral's true value.
Addressing economic and financial risks within a Opus requires a thorough understanding of its economic model's nuances and the potential for unintended consequences arising from its incentive structures, penalty mechanisms, and collateralization requirements.
Incentive Misalignment
: The protocol's incentives must align with its long-term goals, such as securing the network, ensuring liquidity, and fostering adoption. Misaligned incentives might encourage behaviors that are detrimental to the protocol's health, such as participants gaming the system for short-term gains at the expense of long-term stability.
Liquidity Risks
: Protocols rely on sufficient liquidity to function effectively, particularly those involving lending, borrowing, or asset exchange. Economic models that fail to incentivize adequate liquidity provision can lead to market inefficiencies, increased volatility, and reduced protocol utility.
Collateralization Models
: Over-collateralization is common in DeFi protocols to manage credit risk, but excessive requirements can limit participation and utility. Under-collateralization, on the other hand, increases the risk of insolvency under market stress.
Suppose Opus includes a LendingPool contract that allows users to deposit assets as collateral and borrow against them. The contract determines borrowing limits based on the collateral's value and the current market conditions, managed through a CollateralManager.
@external func borrow{ syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr }(user: felt, asset_id: felt, amount: felt): alloc_locals # Verify if the user's collateral ratio is within acceptable limits let (collateral_value) = CollateralManager.get_collateral_value(user, asset_id) let borrow_limit = collateral_value * MAX_BORROW_RATIO / 100 assert amount <= borrow_limit, 'Borrow amount exceeds limit' # Logic to update the user's debt and collateral position # ... end
Let's say the Opus protocol provides significantly high yield farming rewards for lenders but minimal incentives for borrowers, aiming to quickly boost the total value locked (TVL) in the protocol. While this might increase the TVL in the short term, it could lead to a liquidity imbalance where there's too much capital chasing too few borrowing opportunities, resulting in inefficient capital allocation and potentially destabilizing the lending market.
Imagine during a market downturn, many borrowers find their positions close to being liquidated and try to repay their loans to avoid liquidation. However, due to insufficient liquidity in the LendingPool (perhaps because the incentives were not aligned to encourage liquidity provision in times of volatility), borrowers are unable to access the assets needed for repayment, leading to unnecessary liquidations and a loss of trust in the protocol.
If the Opus protocol sets the MAX_BORROW_RATIO too high, allowing users to borrow nearly the full value of their collateral, a minor market correction could push many accounts into under-collateralization, triggering widespread liquidations. This could not only harm the users but also put stress on the protocol's liquidity and potentially its solvency, if the liquidated assets cannot be sold off quickly enough to cover the loans.
Given Opus's complex smart contract ecosystem, the risk of bugs or vulnerabilities in the contract code is significant. These could be exploited to drain funds, manipulate contract states, or disrupt protocol operations.
Developing on StarkNet with Cairo introduces specific risks associated with the relatively new programming language and its compiler. Subtle bugs in the Cairo language itself or the compilation process could lead to vulnerabilities in the Opus protocol that are hard to predict or detect with current auditing practices.
Mitigation Strategy
The degree of decentralization and the specific consensus mechanism employed by StarkNet can pose systemic risks to Opus protocol, especially if StarkNet's security assumptions do not align with those of Opus. The protocol's operation and security are contingent upon the underlying layer's ability to resist centralization pressures and secure consensus.
Mitigation Strategy
Opus protocol's performance and security are inherently tied to the underlying infrastructure provided by StarkNet. Any systemic issues within StarkNet, such as scalability bottlenecks, network downtime, or vulnerabilities within the StarkNet layer, could directly impact the functionality and security of Opus.
Mitigation Strategy
:
The Opus protocol utilizes role-based access control to manage permissions across different aspects of the system. Misconfiguration of these roles or improper assignment of privileges could lead to unauthorized actions, potentially compromising the protocol's integrity.
Example
: If the roles for updating oracle addresses or adjusting economic parameters (such as debt ceilings or interest rates) are not properly restricted or audited, an actor with elevated permissions could manipulate these critical settings for personal gain.
Mitigation
: Implement strict governance procedures for role assignment and changes, along with multi-sig requirements for sensitive operations. Regularly audit the access control lists and role assignments for discrepancies.
The protocol's reliance on intricate state management across multiple contracts (such as managing troves, yangs, and debt positions) increases the risk of bugs that could lead to inconsistent state or loss of funds.
Example
: The mechanism for redistributing debt and managing trove states involves complex calculations and state transitions. A bug in the redistribution logic or an oversight in trove state updating could result in inaccurate debt allocations or trove states not reflecting the true financial positions of users.
Mitigation
: Extensive unit and integration testing of state transitions, leveraging formal verification where possible to mathematically prove correctness under various conditions, and implementing fail-safes or circuit breakers that can pause operations in case of detected anomalies.
Upgradeability and Proxy Contracts
If the protocol employs upgradeable contracts via proxy patterns, there are inherent risks associated with the upgrade process itself, including the potential introduction of bugs or vulnerabilities in new contract versions.
Example
: An upgrade to a new contract version containing a critical vulnerability could be exploited before detection, leading to system-wide impacts. Additionally, the centralization risk in the upgrade governance process could be a point of failure if not decentralized properly.
Mitigation
: Use transparent and community-driven governance processes for approving upgrades, enforce thorough testing and auditing of new contract versions before deployment, and implement timelocks for upgrades to allow for community review.
Opus may rely on custom oracles or a specific set of oracles for asset prices, interest rates, or other external data. These integrations could pose risks if:
Opus-Specific Mitigation
: Implement robust oracle failover mechanisms and validate oracle data through cross-referencing with multiple sources. Design contracts to handle oracle downtime gracefully, possibly pausing sensitive operations.
If Opus integrates with specific Automated Market Makers (AMMs) or liquidity pools for its operations (e.g., for swapping collateral types or managing liquidity), it inherits risks from these platforms, including:
Opus-Specific Mitigation
: Regularly audit and monitor the health and integrity of integrated AMMs or liquidity pools. Establish thresholds for transactions sizes and velocities to mitigate impact from manipulated pools.
Opus may have a built-in upgrade mechanism for its contracts to introduce new features or fix issues. This introduces risks related to:
Opus-Specific Mitigation
: Use a transparent and community-reviewed process for upgrades, involving multi-signature transactions or governance votes. Implement extensive testing and simulation for upgrades, particularly focusing on interactions with integrated protocols.
If Opus introduces custom tokens or utilizes less common token standards for its operations, there are risks related to:
Opus-Specific Mitigation
: Ensure broad compatibility and adhere to best practices in token design. Provide detailed documentation and developer resources for interacting with custom tokens. Conduct extensive testing in diverse environments.
Opus's functionality might be closely tied to specific features of the underlying blockchain platform (e.g., Ethereum EVM optimizations, gas fee mechanisms). Changes or updates in the blockchain platform could:
Opus-Specific Mitigation
: Stay informed about planned updates or changes in the underlying blockchain platform. Participate in testnet phases of new blockchain versions to assess impact early. Design contracts with flexibility to adapt to changes in platform features or costs.
Analyzing the role-based access control (RBAC) in the provided Opus Protocol helps us understand how specific implementations can potentially create admin abuse risks. The protocol defines various roles with distinct capabilities, such as managing system parameters
, updating rates
, adding or suspending assets
, and even shutting down the system
. Here's how these roles, if not properly managed or secured, could lead to admin abuse risks.
The protocol assigns significant powers to specific roles. For example, the shrine_roles
module grants roles like ADD_YANG
, UPDATE_RATES
, and KILL
substantial control over the protocol's operational parameters and even the ability to halt the protocol entirely. If these roles are assigned to a single entity or a small, centralized group, it creates a risk of abuse where the protocol could be manipulated to favor certain outcomes or to disadvantage others.
The code does not explicitly detail mechanisms for checks and balances on admin actions. For instance, the default_admin_role
in various modules (absorber_roles
, shrine_roles
, etc.) holds considerable authority without apparent restrictions or oversight mechanisms. This could lead to unilateral decisions without community consent or against the broader interests of the protocol's users.
The ability to set critical system parameters, such as SET_THRESHOLD
in shrine_roles
or SET_PENALTY_SCALAR
in purger_roles
, can be particularly risky. Malicious or compromised admins could adjust these parameters to create exploitative conditions, manipulate market dynamics, or extract undue profits.
The roles defined across modules (absorber_roles
, caretaker_roles
, sentinel_roles
, etc.) include capabilities that, if abused, could lead to significant disruptions or financial losses. For example, the caretaker_roles
with the SHUT
permission can shut down parts of the system, potentially triggering panic or financial loss among users.
To mitigate these risks, the implementation of roles and permissions in smart contracts should include:
Key Management Policies
: Establishing robust key management policies and practices, including regular rotation and secure storage of private keys.
Regular Security Audits
: Conducting thorough security audits, including the review of RBAC implementation to identify potential vulnerabilities or excessive privileges.
Timelocks and Guardrails
: Implementing timelocks for sensitive operations and setting guardrails or limits on parameter changes to prevent extreme modifications.
In the context of the Opus protocol, non-standard token risks could indeed be a concern if the protocol interacts with, issues, or relies on tokens that do not fully adhere to established standards like ERC-20 . These risks become relevant in several scenarios, particularly if Opus aims to support a wide array of assets or introduces its own unique token mechanics for DeFi operations. Here's how non-standard token risks might manifest within Opus.
Custom Tokens for Protocol Mechanics
: If Opus issues its own tokens for governance, staking, rewards, or other functionalities that diverge from standard implementations, it could lead to integration issues with wallets, exchanges, and other DeFi protocols that expect standard token behavior.
Supporting Non-standard Assets
: Should Opus allow the use of various collateral types, including tokens with non-standard features (like tokens with rebasing mechanics, deflationary models, or admin keys for pausing), the protocol could face challenges in accurately assessing collateral value, managing liquidations, or ensuring security.
Innovative DeFi Strategies
: Opus might employ innovative DeFi strategies involving complex interactions with multiple token types, including non-standard ones. This could introduce risks related to token compatibility, unexpected contract interactions, and the handling of edge cases.
Achieving a 90% test coverage in a complex DeFi protocol like Opus is commendable and indicates a strong commitment to quality and security. However, in the context of blockchain and DeFi applications, where security is paramount and vulnerabilities can lead to significant financial losses, striving for as close to 100% coverage as possible is ideal. Here's an analysis of how 90% coverage affects the protocol and why aiming for 100% coverage is important
High Confidence in Tested Code
: A 90% test coverage means that the majority of the codebase is tested, suggesting that the protocol's core functionalities are likely well-validated against common issues and edge cases. This high level of coverage provides confidence in the stability and reliability of the tested parts of the protocol.
Potential Uncovered Areas
: The remaining 10% of untested code could contain critical paths or functionalities that have not been examined. These areas might harbor vulnerabilities or bugs that could affect the protocol's security, functionality, or performance.
Risk of Financial Exploits
: In DeFi protocols, even a single overlooked flaw can lead to significant financial losses. Uncovered code represents a risk vector for potential exploits, especially if these sections involve financial calculations, asset transfers, or permission checks.
Identify and Mitigate All Risks
: Achieving 100% test coverage ensures that every line of code is evaluated, including those that might be deemed less critical but could still pose risks. This comprehensive testing is crucial for identifying and mitigating all possible vulnerabilities, especially in a DeFi context where the impact can be financially significant.
Improve Code Quality
: Striving for 100% coverage encourages developers to write more testable, modular, and maintainable code. It often leads to the discovery of code smells, unnecessary complexities, or inefficiencies that can be refactored or optimized.
Enhanced Developer and User Confidence
: High test coverage is a signal of a project's quality and robustness, increasing confidence among developers, auditors, and users. In the competitive DeFi landscape, this confidence can be a key differentiator and attract more users and investors.
Regulatory and Compliance Benefits
: As the regulatory landscape for DeFi evolves, having comprehensive test coverage could facilitate compliance with future standards and requirements, making the protocol more resilient to regulatory changes.
Improving the Opus protocol through software engineering practices involves addressing several key areas to enhance security, scalability, and usability.
Component-based Architecture
: Adopt a modular design where different functionalities (e.g., token swaps, liquidity provision, staking) are encapsulated in separate, reusable components. This approach facilitates easier updates, testing, and integration of new features.
Interface-driven Development
: Define clear interfaces for interactions between different components. This can help in isolating changes to specific modules without affecting others, reducing the risk of introducing bugs when extending the protocol.
Comprehensive Test Coverage
: Strive for near-complete test coverage that includes unit tests for individual functions, integration tests for component interactions, and end-to-end tests for user scenarios. Consider property-based and fuzz testing to uncover edge cases.
Automated Regression Testing
: Implement automated testing pipelines to run a comprehensive suite of tests for every code change. This helps in identifying and fixing regressions early in the development cycle.
Formal Verification
: Apply formal verification methods to critical smart contracts to mathematically prove the correctness of contract behaviors under specified conditions, enhancing security and trustworthiness.
Layer 2 Integration
: Explore integration with Layer 2 scaling solutions or sidechains to improve transaction throughput, reduce costs, and enhance user experience without sacrificing security.
Gas Optimization
: Regularly review and optimize smart contract code to reduce gas consumption, making the protocol more cost-effective for users. This includes optimizing data storage, minimizing transaction complexity, and leveraging gas-efficient patterns.
Open Source and Documentation
: Maintain an open-source codebase with comprehensive documentation, including detailed API references, architecture overviews, and developer guides. This encourages community contributions and fosters a vibrant developer ecosystem around the protocol.
Transparent Governance
: Implement transparent and inclusive governance mechanisms that allow the community to participate in decision-making processes. This could involve DAO structures, governance tokens, or community forums for proposing and voting on protocol improvements.
Simplified User Interfaces
: Develop intuitive and user-friendly interfaces for interacting with the protocol, abstracting away the complexity of blockchain interactions. This can help in attracting a broader user base.
Cross-chain Capabilities
: Work towards interoperability with other blockchains to tap into larger ecosystems, expand the user base, and facilitate seamless asset transfers across chains.
The Opus protocol, let's perform an in-depth architecture assessment focusing on its business logic as interpreted from the provided details. The Opus protocol appears to be a complex DeFi platform with multiple components interacting through defined roles and permissions.
The protocol seems to include components for optimizing yields, possibly through automated strategies that interact with various DeFi protocols. This involves complex logic for asset allocation, risk assessment, and return maximization.
Opus likely features liquidity pools and staking mechanisms, integral for providing liquidity and securing the protocol. The business logic must handle variable APRs, rewards distribution, and potentially auto-compounding features.
With roles like default_admin_role, caretaker, and sentinel, the protocol incorporates a governance system that allows certain actions to be performed by specific roles. This could include protocol upgrades, parameter adjustments, and emergency actions to safeguard assets.
The detailed roles (e.g., shrine_roles, sentinel_roles) indicate a sophisticated access control system managing permissions across different protocol actions. This system's logic must ensure security while allowing flexibility for governance and operational efficiency.
Indicated by components like IERC20 and ISRC5 interfaces, there's an element of cross-protocol interaction, requiring careful management of external calls, data integrity, and transaction atomicity to prevent exploits.
Weak Spot
: Hardcoding role values and permissions within the contract can make the system inflexible and potentially risky if there's a need to update roles or permissions dynamically. Additionally, if the logic for checking roles is not centralized or if it's inconsistently applied, it could lead to unauthorized actions.
const KILL: u128 = 1; const SET_REWARD: u128 = 2;
Mitigation
: Consider using a more dynamic approach for role management, such as the OpenZeppelin AccessControl library, which allows for flexible and transparent role management. Ensure that role checks are consistently applied across all sensitive functions.
The use of constants to combine roles for specific actions or defaults, while clear, can lead to mistakes in role management, especially as the complexity of the protocol grows.
fn default_admin_role() -> u128 { KILL + SET_REWARD }
Weak Spot
: Misconfiguration of these combinations can inadvertently grant more permissions than intended. Moreover, the approach requires careful management to avoid conflicts or gaps in permissions.
Mitigation
: Utilize a more granular and explicit role management system. Implement unit tests and integration tests that specifically validate permissions and access control logic to ensure that only the intended roles can perform specific actions.
The provided snippets indicate a complex system with many components interacting through state changes, external calls, and event emissions.
Weak Spot
: Complex interactions and dependencies can introduce bugs or vulnerabilities, particularly in scenarios involving external calls or when managing nuanced state changes.
fn deposit(ref self: ContractState, yang: ContractAddress, trove_id: u64, amount: Wad) { ... }
Mitigation
: Simplify interactions where possible, and isolate external calls to reduce the attack surface. Implement comprehensive testing, including fuzzing and formal verification, to ensure that state management is robust against edge cases.
The handling of errors or unexpected states in smart contracts is crucial to maintain integrity and prevent loss of funds.
Weak Spot
: Insufficient error handling or lack of clear reversion logic in the face of invalid inputs or failed external calls can lead to unintended consequences.
Mitigation
: Ensure comprehensive error handling and validate all inputs thoroughly. For external calls, consider using patterns like "checks-effects-interactions" and ensure that failures in external calls are handled gracefully.
30 hours
#0 - c4-pre-sort
2024-02-07T17:14:10Z
bytes032 marked the issue as sufficient quality report
#1 - c4-judge
2024-02-26T17:57:27Z
alex-ppg marked the issue as grade-b