# SwingTraderManager.addSwingTrader will push traderId with active = false to activeTraders

SwingTraderManager.addSwingTrader pushes inactive traders into activeTraders, causing duplicate entries and permanently stuck inactive traders that corrupt collateral/ratio accounting.

- Payout: Contest reward
- Severity: high
- Program: Malt Protocol
- Credited: cccz
- Disclosed: 2023-05-01
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-02-malt-findings/issues/12
- Library page: https://bugbountylibrary.com/r/c4%3A2023-02-malt%3AH-04
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** SwingTraderManager Solidity contract (addSwingTrader/toggleTraderActive)
**Root cause:** addSwingTrader unconditionally pushes traderId to activeTraders even when active=false, and toggleTraderActive pushes again on activation; the swap-and-pop removal only removes one duplicate, leaving a stale entry.
**Steps:**
1. Call `addSwingTrader(3, addr, false, "Test")` as admin; traderId 3 is pushed to activeTraders with active=false.
2. Call `toggleTraderActive(3)`; active becomes true and traderId 3 is pushed again, so activeTraders contains 3 twice.
3. Call `toggleTraderActive(3)` again; only one instance is popped, leaving a duplicate/stale entry.
4. Observe `getTokenBalances()` and `calculateSwingTraderMaltRatio()` double-count the trader's balances, corrupting `syncGlobalCollateral()` and `getRealBurnBudget()/getSwingTraderEntryPrice()`.
**How found:** Code review of addSwingTrader/toggleTraderActive revealed the unconditional push and the swap-and-pop removal only handling one duplicate; PoC test asserts activeTraders[2]==activeTraders[3]==3.
**Insight:** When auditing array-based active-set registries, check that every push is paired with a conditional and that swap-and-pop removal can handle duplicate entries; duplicates silently double-count in aggregate balance/ratio loops.
