# SynapLogic sale: multi-entry refund logic drains contract funds

SynapLogic sale contract refunded each recipient array entry at 10% rate, letting attacker pass 20+ self-entries to drain 27.6 ETH and 3450 USDC.

- Payout: Real-world hack
- Severity: high
- Program: SynapLogic
- Credited: DeFiHackLabs
- Disclosed: 2026-01-20
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-01/SynapLogic_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260120-synaplogic
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm technique:source-review vuln:business-logic

**Target:** Token sale proxy contract (buy/swap functions) on Base
**Root cause:** The buy/swap functions iterate over caller-supplied recipients/rates/refundFlags arrays and issue a refund per entry at the supplied rate, without capping total refund to the amount actually paid in.
**Steps:**
1. Identify SaleProxy `0x39F36e2E58f36F7E5c17784847fd07Da1fEE1a32` on Base and its `buy` selector `0x670a3267` / `swap` selector `0x7f7f92f5`.
2. Read `minBnb()` to get minimum ETH value.
3. Build arrays of length N where every `recipients[i]=attacker`, `rates[i]=10`, `refundFlags[i]=true`.
4. Call `buy{value: minBnb}(recipients, rates, refundFlags)` — each entry refunds 10% of value, so N=20 yields 200% refund.
5. For USDC: approve SaleProxy, call `swap(tokenAmount, recipients, rates, refundFlags)` with 20 entries at rate 10 to receive 200% of tokenAmount back.
6. Repeat across multiple txs to drain full contract balance.
**How found:** Likely spotted that refund amount is computed per array element rather than against total msg.value, so array length multiplies payout.
**Exploitation:** Attacker supplied a single small deposit but an array of 20+ refund entries all pointing to themselves at 10% rate each, so the contract paid out 200% of the input per call. Repeated calls drained ~27.6 ETH and ~3450 USDC from the sale proxy.
**Escalation:** Looping the call across many transactions and both ETH and USDC paths to exhaust the entire contract balance.
**Insight:** When a function accepts parallel arrays of recipients/rates/flags, check whether per-element payouts are summed against a single deposit — array length becomes a multiplier.
