# A user calling controller::dctstake can bypass tax by first transferring the tokens to be staked in a separate transaction

dctStake computes stake value from controller's DCT balance, so pre-transferring tokens and staking amount_=0 avoids the 1% burn tax.

- Payout: Contest reward
- Severity: high
- Program: Goat
- Credited: mctoady
- Disclosed: 2024-06-12
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_goat_mar2024.pdf#3.1.2
- Library page: https://bugbountylibrary.com/r/cantina%3Agoat_mar2024%3A3.1.2
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:high tech:evm tech:solidity vuln:business-logic vuln:fee-bypass

**Target:** Controller.sol dctStake staking function
**Root cause:** _stake derives the staked value from _dct.balanceOf(address(this)) instead of the amount_ parameter, so tokens sent directly to the contract are counted as stake without tax.
**Steps:**
1. Alice calls `controller.ethStake{value: 1 ether}(...)` to set up her pool
2. Alice calls `dct.transfer(address(controller), 100 ether)` directly
3. Alice calls `controller.dctStake(0, payable(alice), 60 days)` with amount_=0
4. _stake reads full controller DCT balance (100 ether) and stakes it; no tax transferred to 0xdead
5. Assert dct.balanceOf(dLocker) == 100 ether confirms 1% tax avoided
**How found:** Code reading: noticed dctStake applies a 1% tax on amount_ but _stake reads the contract's token balance rather than amount_, creating a mismatch exploitable by pre-funding the contract.
**Exploitation:** User bundles a direct DCT transfer to the controller with a dctStake(0,...) call (ideally in one contract call to avoid frontrun/sandwich), so the full balance is staked and the 1% tax to 0xdead is skipped.
**Insight:** When a function taxes a user-supplied amount but downstream logic reads contract balance, the two diverge—pre-fund the contract and pass zero to skip the tax.
