Deflex Audit by Ulam Labs
Deflex underwent a second security audit to make sure that users can trade safely. This time, Deflex was audited by Ulam Labs, the report can be found here (for the first audit done by Vantage Point, see here). We’re happy to announce that none of Ulam Labs’ three findings put user funds at risk.
Deflex is a suite of protocols for optimized trading in Algorand. It currently consists of two composable protocols that can be used independently or together:
Order-Router Protocol: The order-router protocol finds the best route to swap from one asset to another. To that end, it breaks down a swap (say 1000 USDC ➜ PEPE) into smaller swaps and applies a series of combo swaps (swaps between the same trading pair but on different AMM pools to reduce price impact) and multi-hop swaps (e.g., swap from USDC ➜ ALGO ➜ PEPE).
Limit-Order Protocol: This is a protocol for on-chain and decentralized limit orders that leverage Algorand’s existing DEXs. Users can place orders that get executed at a later, predetermined market rate (or canceled if that rate is not reached until the order expires).
Ulam’s audit has resulted in three findings that we describe next: one affects the order-router and two the limit-order protocol.
Denial-Of-Service (DOS) Attack
Ulam Labs found that the order-router smart contract (order-router for short) could be locked by an attacker using a malicious transaction such that users cannot swap anymore. However, at no time were funds at risk. To understand how this DOS attack works, we begin with a quick review of how the order-router functions.
The order-router executes a complex combo- and multi-hop swap group atomically. It takes the swap input (say 1000 USDC) from the user along with a plan on how the swap group is to be executed, then converts it into the desired output asset (say PEPE) by executing the swap plan, and finally sends the output back to the user.
The order-router executes most swaps as part of a swap group with inner transactions. Yet some exceptions exist that cannot be executed like that. One example are Algofi lending swaps, which are complex swaps that go through Algofi’s lending protocol and AMM. They are a great source of stable-coin liquidity and hence it’s important to support them. Since they cannot easily be executed as an inner transaction, we employ a trick. We rekey the order-router to the account that executes the transactions and this account does the lending swap as a top-level transaction on behalf of the order-router. When the swap is finalized, we guarantee that the order-router is rekeyed back to itself and that the user receives at least the output that the user asked for (adjusted for slippage).
The problem is that as long as the order-router is rekeyed, an attacker can increase the order-router’s minimum balance, e.g, by opting into apps etc. Code in the order-router was assuming that the minimum balance is always 0.1 ALGO before a swap begins (0.1 ALGO is the minimum account balance). This code ultimately would lock the contract if an attacker indeed executed a malicious transaction. The details are a bit more involved and can be found in Ulam’s report.
Fixing this problem was simple: when a swap is finalized we assert that the minimum balance is 0.1 ALGO. We deployed a new, updated order-router app that’s now used in production.
Manipulated State
The second and third findings in Ulam Labs’ report focus on the limit-order protocol. Neither of these findings put users’ funds at risk, but they are interesting to look at and understand nevertheless. Paradoxically, the root cause of these findings are upgradeable smart contracts even though our contracts are not upgradeable. Before we can explain how these findings work, we need to explain the architecture of the limit-order protocol.
Each user that wants to place a limit order first needs to deploy a limit-order app. Users place their orders in their personal app. A limit order is represented by an escrow account that opts into a limit-order app and its local state stores all the relevant details of the order (today, we’d use box storage to keep track of this information, but the limit-order protocol was designed before box storage was available). To make the limit orders globally searchable, there exists one global registry app to which every limit-order escrow opts in. Note that, neither the limit-order app, nor the registry app are upgradeable. To query all open orders, one can query the indexer for all accounts that have opted into the registry app. When an escrow account opts into the registry app, the app checks that the escrow account is opted into a valid limit-order app. It does so by comparing the limit-order app’s code against a hard-coded value. Only if that’s the case, the registry app accepts the limit order.
Ulam Labs found a way to accept invalid escrow accounts in the registry. An attacker can deploy a malicious, upgradeable limit-order app that puts incorrect data into an escrow account and later upgrade the limit-order app’s malicious code to the correct code. Hence, the registry will accept this invalid escrow account that was previously created with a malicious limit-order app since by the time the escrow opts into the registry, the code of the limit-order app is valid. The result is that the information in an escrow account cannot be fully trusted.
As confirmed by Ulam Labs, an attacker cannot exploit this to steal user funds.
Since escrow accounts belong to a user’s personal limit-order app, any faked escrow account can only harm the attacker. Hence, this attack vector, while interesting, does not lead to any consequential issues.