Router ignores return values of token transfers and native-coin refunds
contracts/Router.sol:L388–L421 (_payOut, _refund)
- Likelihood
- Medium
- Impact
- High
- Weakness
- CWE-252
- Description
_payOutcallsIERC20(token).transfer(to, amount)and discards the returned boolean. Some BEP-20 tokens signal failure by returningfalseinstead of reverting. When such a transfer fails, the router continues as if the maker or taker had been paid, and the order is marked filled._refundreturns excess BNB withpayable(msg.sender).call{value: excess}("")and ignores thesuccessflag. If the taker is a contract that rejects BNB, the refund silently stays in the router, where the next caller who triggers a refund path can receive it.Tokens that do not return a value at all cause the opposite problem: the interface expects a boolean and the call reverts when decoding, blocking trading in those pairs. A safe-transfer wrapper handles both cases.
- Preconditions
- A listed token that returns false on failure, or a taker contract that rejects native refunds.
- Exploit scenario
- 1.A token with a transfer blacklist returns
falsewhen the recipient is blacklisted. - 2.A maker who is blacklisted by the token has an order filled; the transfer fails silently and the order is marked filled.
- 3.The proceeds remain in the router and are paid to the next taker whose payout uses the same token, while the maker loses their escrow.
- 1.A token with a transfer blacklist returns
- Impact
Makers or takers lose funds; balances stranded in the router can be captured by other users.
- Recommendation
Use
SafeERC20.safeTransferfor all token movements and revert, or credit a withdrawable balance, when a native refund fails.- Fix
- contracts/Router.sol (token path at remediation commit; native path recommended)diff
- IERC20(token).transfer(to, amount);+ IERC20(token).safeTransfer(to, amount); ...- payable(msg.sender).call{value: excess}("");+ (bool ok,) = payable(msg.sender).call{value: excess}("");+ if (!ok) pendingRefunds[msg.sender] += excess; - Resolution
Partially resolved. Token transfers now use SafeERC20. The native refund path still ignores the call result; the team plans a withdrawable refund balance.
