[Contracts] Redemption helper#1164
Conversation
90506d2 to
c7cc3d8
Compare
bingen
left a comment
There was a problem hiding this comment.
It looks good in general. I still need to finish the review of truncateRedemption, but the important part in my opinion is that the wrapper redeemCollateral is fine. The frontend may show the amounts passed as collateral and their values in $, so even if one of the other helpers made a mistake the user would realize that is getting a bad deal.
| RedemptionContext[] memory branch = new RedemptionContext[](numBranches); | ||
|
|
||
| for (uint256 i = 0; i < numBranches; ++i) { | ||
| branch[i].collToken = collateralRegistry.getToken(i); |
There was a problem hiding this comment.
Not sure if it’s worth, but we may store the tokens, even immutably like we do it collateral registry, to save some gas.
There was a problem hiding this comment.
I considered this too. The 3 calls to collateralRegistry.getToken() total about 1.35K gas, so that's the upper limit on how much we could save by switching to immutables. It makes the code a bit nasty though.
Or we could use storage, but unless we pass an access list with the TX, it would actually end up costing more, since cold slots take 2.1K to SLOAD.
In the end, I didn't think the 1.35K potential savings warranted adding the extra immutable optimization.
| for (uint256 i = 0; i < numBranches; ++i) { | ||
| if (branch[i].redeemable && branch[i].proportion > 0) { | ||
| uint256 extrapolatedBold = branch[i].redeemedBold * totalProportions / branch[i].proportion; | ||
| if (extrapolatedBold < truncatedBold) truncatedBold = extrapolatedBold; |
There was a problem hiding this comment.
I’m having hard time understanding this part. It seems that truncatedBold can be overridden at any branch iteration. Why do we know the last one is the good one? I’ll get back to it later, but maybe a comment may help.
There was a problem hiding this comment.
For each branch, we determine how much the total BOLD redeemed across all branches would have been if the redemption was proportional (we call this extrapolatedBold). Then we take the smallest of those amounts.
Normally, this is no different from the input _bold amount. However, if any of the branches terminated because of the iteration limit, the redeemed amount on that branch will be less than proportional. Since such redemptions end up redeeming less BOLD than intended, they pay a higher fee than necessary (since the fee is fixed at the beginning of the TX, before the actually redeemed amounts are known).
The point of truncateRedemption() is to find the maximal amount of BOLD that can be redeemed within a given iteration limit without overpaying on the redemption fee.
There was a problem hiding this comment.
Added a comment that hopefully explains this.
7ccd3b8 to
34dda5d
Compare
Adds a helper contract that will be used by the frontend to implement #1065.
Features added
Slippage protection
Normally, a redemption is performed by a bot in a closed arbitrage transaction, usually through a private mempool and with the help of a flashloan. Thus, if there's slippage of the redemption fee, redemption proportions or the execution price of any of the trades involved in the transaction making the redemption unprofitable, the TX will simply fail or not be included at all.
This is not the case when redeeming directly through
CollateralRegistry: the backend will only ensure that the redemption fee (as a percentage) doesn't exceed the maximum acceptable value passed by the redeemer, it doesn't care if the collateral prices (as reported by their oracles) or the proportions of unbacked debt between branches has changed compared to what the redeemer simulated before signing their transaction.This helper contract implements a wrapper around
redeemCollateral()that adds the new parameter_minCollRedeemed, ensuring that the redeemer gets at least as much collateral from each branch as was presented to them on the UI (minus a small slippage tolerance margin, to be calculated by the frontend).Redemption optimization
The job of
CollateralRegistryis to break a redemption down into multiple smaller redemptions (one per each branch) adding up to the total amount that is to be redeemed. Additionally, it is responsible for calculating the redemption fee (as a percentage). It does so at the beginning of the redemption sequence, assuming that each of the per-branch redemptions will be complete, i.e. that there will be no unredeemed BOLD left.However, this is not always the case. For example, the redemption could terminate early on one or more branches due to hitting the user-configured iteration limit (
_maxIterationsPerCollateral). When this happens, the user pays a higher fee than warranted by the actual amount of BOLD successfully redeemed, because of the aforementioned assumption made when calculating the fee.The
truncateRedemption()function lets the user avoid such (unlikely) scenarios by simulating the redemption, and calculating the maximum amount of BOLD that can be redeemed completely within the given iteration limit.