clober-dex / coupon-finance

Coupon Finance Solidity Contracts
Other
1 stars 0 forks source link

Certain legitimate coupon trades will revert in controllers that use the base Controller trading function #89

Closed detectivekim closed 11 months ago

detectivekim commented 1 year ago

Description

The Controller has the _executeCouponTrade() function to trade coupons through the Clober DEX with certain constraints. It is structured as a recursive implementation, where the last coupon is peeled off the array, traded and then through the Clober callback the rest of the array is traded using _executeCouponTrade() again.

https://github.com/clober-dex/coupon-finance/blob/216e0d9463b340ffcc810ecc81957ebe309048ec/contracts/libraries/Controller.sol#L66-L81

The function receives couponsToBuy and couponsToSell arrays. When one of them is not empty, it assumes the other one is (an empty Coupon[] array is passed instead of the actual array). This assumption holds when users interact with the BorrowController or DepositController. However as Controller was written with inheritance in mind, it needs to handle the generic case when coupons need to be both bought and sold. The buy and sell lists come from the Position calculateCouponRequirement() function. This is the critical code component which determines the coupon array lengths.

https://github.com/clober-dex/coupon-finance/blob/216e0d9463b340ffcc810ecc81957ebe309048ec/contracts/libraries/BondPosition.sol#L42-L54

Consider the possibility that a user wishes to extend the debt duration and lower the debt amount. In this case, for the epochs up to the previous expiry epoch, a user is eligible for coupons (mintCoupons), after which they will need to burn coupons until the new expiry epoch. Both arrays will not be empty. The impact will be that the described position changes cannot be implemented in controllers (will revert), although they use the Controller parent code correctly.

Recommended mitigation

Pass the original arrays to the recursive implementation instead of zero-sized arrays in _executeCouponTrade().