Full Report
Code4rena is a crowd sourced security audit platform. Recently, Trader Joe V2 received a security audit. Joe V2 is a decentralized exchange based on Liquidity Book, an AMM protocol. The first major vulnerability was bad book keeping because of temporary variables. The code snippet is very short: uint256 _fromBalance = _balances[_id][_from]; uint256 _toBalance = _balances[_id][_to]; _balances[_id][_from] = _fromBalance - _amount; _balances[_id][_to] = _toBalance + _amount; What's the big deal? There's no verification on to being the same as from. Walking through the values if they are the same with the starting balance being 10 and the amount being 10: _fromBalance = 10 _toBalance = 10 _balances[_id][_from] = 10 - 10 (_fromBalance) _balances[_id][_to] = 10 + 10(_toBalance) The fourth line of code does not take into consideration the third line of code. Hence, the amount of funds in the account cane be infinitely doubled. The second high finding was an incorrect calculation between V1 and V2 pools. This incorrect calculation could have led to funds being lost for the user - but not stolen; both 3 and 4 were similar to this as well. Weird to me this is considered a security finding. Trader Joes is similar to most LP pools; a user can call mint() to provide liquidity and get an LP token in return. Additionally, it can call burn() to return the LP token to get their underlying assets as well as call collectFees to get the fees they are owed. The fees are calculated using the debt model. In other words, it is keeping track of the last time the fees were obtained by the user and subtracting this from amount. This is to ensure that the user doesn't collect fees for the entire growth of the token; they should only get the fees for the time they have been active. When calling a multitude of functions for token swaps (mint, burn, transfer), the hook _beforeTokenTransfer is called to update the user debts. The hook only updates the cache fees via _cachefees() if the address is not 0 and the address does not equal the address of the pool. This was done since the contract is never expected to own the LP tokens. So, what's the bug? The code for _cacheFees is never triggered! This means that the time we got in (debt) does NOT get added to the calculations. When calling mint, the recipient of the token can be chosen. Because of further bad bookkeeping, the funds can be taken out as well. Steps for the exploit: Transfer funds to the address to mint in the next step. mint with the recipient being the contract itself. Call collectFees() with pair address as the account. It will send itself the fees. Now, the token believes that the user sent it money, even though the contract paid itself! This is because of it keeps an internal balance and comparing that to the actual balance of it. This difference convinces the protocol that we indeed set money to it! Call swap to collect all of the fees. There is an integer overflow within an unchecked block that allows for the fees calculation to go rouge. This allows the attacker to steal the entire reserves, instead of the total fees accrued only. I absolutely love this bug! Super crazy attack which starts by sending your money away.
Analysis Summary
# Vulnerability: Trader Joe V2 Critical Inaccuracies Leading to Fund Duplication and Reserve Theft
## CVE Details
- CVE ID: Not Assigned (Audit Findings)
- CVSS Score: Not Assigned
- CWE: CWE-284 (Improper Access Control), CWE-787 (Out-of-bounds Write - implied by logic flaw leading to gain)
## Affected Systems
- Products: Trader Joe V2 (Decentralized Exchange based on Liquidity Book AMM)
- Versions: Unspecified (The version under the Code4rena audit)
- Configurations: Standard deployment configuration of the Liquidity Book implementation.
## Vulnerability Description
This audit identified several high-risk issues impacting Trader Joe V2:
**1. Infinite Fund Doubling via Lack of Balance Verification (H-01):**
A core bookkeeping error allows an arbitrary account balance to be infinitely doubled during a transfer operation if the `_from` and `_to` addresses are the same. The logic fails to verify that `_from` is different from `_to` before executing updates based on independent temporary variables:
solidity
uint256 _fromBalance = _balances[_id][_from];
uint256 _toBalance = _balances[_id][_to];
_balances[_id][_from] = _fromBalance - _amount; // Line 3: Subtracts _amount
_balances[_id][_to] = _toBalance + _amount; // Line 4: Adds back _amount using the *original* _toBalance
If `_from == _to`, Line 4 uses the original balance before Line 3 modified it, resulting in a net increase of `_amount` on the account. This can be repeated infinitely.
**2. Fee Calculation Bypass and Reserve Theft via Integer Overflow (H-05):**
A complex exploit path allows an attacker to steal the entire protocol reserves through a sequence leveraging incorrect fee debt bookkeeping and an integer overflow.
* **Debt Caching Failure:** The `_beforeTokenTransfer` hook, which is supposed to update fee accrual debt via `_cacheFees()`, fails to execute if an address is the pool address or the zero address. This means the user's entry "debt" for fee calculations is never correctly recorded.
* **Exploit Chain:** An attacker exploits this by:
1. Transferring funds to an intended recipient address.
2. Calling `mint()` with the contract itself as the recipient to artificially inflate the internal balance bookkeeping without external asset transfer.
3. Calling `collectFees()` using the pair address as the account, tricking the protocol into crediting fees to the contract based on the false internal balance setup.
4. Calling `swap` to manipulate the fee calculation further.
* **Final Attack:** An **integer overflow within an unchecked block** during the fee calculation allows the attacker to significantly overshoot the intended fee collection, resulting in the theft of the **entire reserves** instead of just accrued fees.
**3. Incorrect Calculation Affecting V1 Pools (H-02, H-04):**
Calculation errors between V1 and V2 pools could lead to users losing tokens during operations (e.g., V1 pool output calculations could gift tokens to the pool).
## Exploitation
- Status: PoC available (Implied by the detailed steps for H-05 and the confirmation of the logic flaw in H-01). The complexity suggests these are high-severity findings.
- Complexity: Medium to High (Especially H-05, which requires precise sequencing of mint, self-crediting fees, and leveraging an overflow).
- Attack Vector: Network (Remote, via contract interaction).
## Impact
- Confidentiality: None
- Integrity: **Critical** (Total asset loss/theft of reserves via H-05; Unauthorized balance doubling via H-01).
- Availability: Potential (If operations break due to logic errors like H-03).
## Remediation
### Patches
- Specific patch details (commit hashes) are not provided in the summary, but the findings were addressed by the Trader Joe team following the audit.
- **Mitigation for H-01:** Must implement checks ensuring `_from != _to` or adjust the transfer logic so that updates are sequential and dependent (i.e., Line 4 must use the newly updated balance from Line 3 if addresses are the same).
- **Mitigation for H-05:** Correct implementation of the fee debt caching mechanism (`_cacheFees`), ensuring it triggers for all relevant token movements, and auditing all unchecked arithmetic blocks, particularly in fee and ratio calculations.
### Workarounds
- No formal vendor workarounds were provided, but generally for unchecked arithmetic/fee issues, limiting swap sizes or pausing complex functions until patched would be necessary.
## Detection
- **Indicators of compromise:** Unexplained doubling of user balances following a transfer/swap operation. Unexpected draining of total pool reserves disproportionate to fee collection.
- **Detection methods and tools:** Continuous monitoring of internal account balances vs. external token ledger balances. Specific monitoring for transactions that involve `mint()` calls immediately followed by `collectFees()` or `swap()` calls that utilize abnormal inputs/outputs defined by the flawed fee logic.
## References
- Audit Report Findings: [code4rena.com/reports/2022-10-traderjoe](defanged_url)
- H-01 Finding Link: [code4rena.com/reports/2022-10-traderjoe#h-01-transfering-funds-to-yourself-increases-your-balance](defanged_url)
- H-05 Finding Link: [code4rena.com/reports/2022-10-traderjoe#h-05-attacker-can-steal-entire-reserves-by-abusing-fee-calculation](defanged_url)