The audited Solidity code for the contract named AskOrdersFacet that allows users to create ask orders in a market. A potential vulnerability was identified related to the order of external calls and state-changing operations.
Reentrancy Concern:
The function createAsk contains an external call to LibOrders.sellMatchAlgo(...). After this external call, there's a state-changing operation (emit Events.CreateAsk(...)).
Even though the nonReentrant modifier is used, having state-changing operations after external calls can be a potential point of vulnerability.
How It Works:
Contract A calls an external function in Contract B.
Before Contract B finishes executing, it calls back into Contract A.
Contract A's state can be changed by Contract B before Contract A finishes its original function.
Demonstration with createAsk:
AskOrdersFacet's createAsk function calls LibOrders.sellMatchAlgo(...).
Suppose LibOrders.sellMatchAlgo has a callback to AskOrdersFacet (either directly or indirectly through another contract).
This callback could potentially change the state of AskOrdersFacet or trigger other unexpected behaviors.
After the callback finishes, the control returns to the createAsk function, which then emits the Events.CreateAsk event.
Potential Risks:
Unexpected State Changes: If LibOrders.sellMatchAlgo (or any function it calls) has the capability to change the state of AskOrdersFacet, it could lead to inconsistent or unexpected states.
Event Emission Issues: The emit Events.CreateAsk could be broadcasting incorrect or misleading information if the state of the contract was altered by the reentrancy.
Financial Implications: In some cases, reentrancy can lead to funds being withdrawn or transferred multiple times, leading to financial losses.
Why nonReentrant Might Not Be Enough:
The nonReentrant modifier is designed to prevent reentrant calls by using a mutex (a locking mechanism). When a function with this modifier is called, it locks the contract, preventing any further calls until the current one completes. However:
If there's a flaw or oversight in the implementation of the nonReentrant modifier, it might not provide the intended protection.
Relying solely on nonReentrant can be seen as a "patch" rather than a structural fix. It's generally better to structure the code in a way that inherently avoids reentrancy risks, such as by placing all state-changing operations before external calls.
If exploited, the identified vulnerability could allow an attacker to perform reentrancy attacks. This could lead to unexpected behaviors in the contract, potentially compromising the integrity of the system or leading to financial losses.
Manual Code Review: The code was manually reviewed line-by-line to identify potential vulnerabilities and understand its structure.
Contextual Knowledge: General knowledge of Solidity and smart contract best practices was used to assess the code.
Reorder Operations: Move the emit Events.CreateAsk(...) line to a position before the LibOrders.sellMatchAlgo(...) call to prevent potential reentrancy attacks.
Library Audit: Ensure that all imported libraries (U256, U80, Errors, Events, STypes, MTypes, O, Modifiers, LibAsset, LibOrders) are trusted and have been audited.
Review Modifiers: Examine the implementations of the isNotFrozen, onlyValidAsset, and nonReentrant modifiers to ensure they are secure.
Deep Dive into External Functions: Review the LibOrders.sellMatchAlgo function and other external functions for potential vulnerabilities.
The fact that the state-changing operation is an emit statement (which logs an event) rather than a more critical operation like transferring funds.
I would classify this issue as Low Risk.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.