Withdrawal-fee griefing
Description
By design, the NativeTokenFlow contract lets any user call into wrapAllNativeToken(), which takes the entire native balance of the contract and deposits it into a wrapped token counterpart. The caller pays the gas fees for this one-time deposit, but during a call to cancelOrdersIgnoreInvalidCancellation(Orders[]) with multiple orders, the caller will pay the gas for one withdrawal per cancellation refund.
This opens up for a very minor griefing attack where one user can keep wrapping native tokens quite cheaply, but this makes the price for mass cancellations proportionally higher. It is possible to circumvent this by creating a new order just to fund the contract, cancel the required orders, and then cancel the new order. But this creates awkward use cases for users that want to be frugal with their gas.
Impact
Canceling multiple orders in a single call can be as costly as canceling orders one by one. For particularly large order cancellations, the required gas could reach transaction gas limits and fail. If the ERC-20 takes a fee for wrapping tokens, this attack can be quite devastating, but during the audit, we noticed no plans to use such a token.
If a fee exists, a malicious user could create hundreds of very small orders, call wrapAllNativeToken(), and then refund all their orders. In a normal situation, the user would pay the gas for this, but great care must be taken to avoid any token with a fee.
Recommendations
Since this is by design, it is not easy to fix without changing the design. One solution could be to not allow any user to deposit but require a more permissioned role like owner or a new depositor role that has this access.
Remediation
This issue has been acknowledged by Kite Labs, and a fix was implemented in commit 18a45298↗.