# Cross contract reentrancy during ElixirStrategy.withdraw() swaps allows an attacker to double account collateral

ElixirStrategy.withdraw computes swap output via holding balance delta, so a malicious token in the swap path reenters HoldingManager.deposit, double-counting collateral and minting jUSD free.

- Payout: Contest reward
- Severity: high
- Program: Jigsaw
- Credited: ktl
- Disclosed: 2025-08-11
- Write-up quality: 8/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_jigsaw_june2025.pdf#3.1.7
- Library page: https://bugbountylibrary.com/r/cantina%3Ajigsaw_june2025%3A3.1.7
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:cantina sev:high tech:evm tech:solidity technique:chained vuln:accounting-error vuln:business-logic vuln:oracle-manipulation vuln:reentrancy

**Target:** ElixirStrategy.withdraw / Uniswap multihop swap / HoldingManager
**Root cause:** withdraw() derives withdrawnAmount from IERC20(tokenIn).balanceOf(_recipient) delta rather than the router's amountOut, so any USDT deposited into the holding mid-swap is counted as swap yield.
**Steps:**
1. Attacker deploys MaliciousToken whose _update() calls back into attacker contract during transfers.
2. Create Uniswap pools MaliciousToken/USDT and MaliciousToken/deUSD, seed liquidity.
3. Deposit USDT collateral via HoldingManager.createHolding + deposit, invest into ElixirStrategy with swap path USDT->USDC->deUSD.
4. After cooldown, call claimInvestment with path deUSD->MaliciousToken->USDT and amountOutMinimum set.
5. During the malicious token transfer, reenter HoldingManager.deposit(tokenIn, balance) to add USDT to the holding.
6. withdraw() reads balance delta including the reentrant deposit, inflating withdrawnAmount/yield and collateral accounting.
**How found:** Code review of withdraw() noticed withdrawnAmount computed from balanceOf delta instead of router amountOut, then reasoned a controllable token in the swap path could reenter and deposit during the swap.
**Exploitation:** Attacker supplies a swap path through a self-created malicious token pool; its transfer hook reenters HoldingManager.deposit to add USDT collateral mid-withdraw. The strategy's balance-delta accounting counts that deposit as swap yield, doubling recorded collateral (1e8 -> 2.85e8) and letting attacker mint jUSD against phantom collateral.
**Escalation:** Repeated deposits during each swap inflate collateral arbitrarily; EIP-7702 noted to bypass msg.sender != tx.origin holding-creation check.
**Insight:** When a strategy measures swap output by token balance delta, any token in the swap path with a transfer hook becomes a reentrancy vector into the same protocol's deposit functions; check whether balance-delta accounting can be polluted by mid-call deposits.
