Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lastInterestRateAdjTime is not reset on debt increases #632

Open
bingen opened this issue Dec 3, 2024 · 1 comment
Open

lastInterestRateAdjTime is not reset on debt increases #632

bingen opened this issue Dec 3, 2024 · 1 comment
Assignees
Labels
wontfix This will not be worked on

Comments

@bingen
Copy link
Collaborator

bingen commented Dec 3, 2024

Brought up by @cvalkan

According to the Readme, lastInterestRateAdjTime is only (re)set to the current blocktime when a new Trove is opened or when its interest rate is changed, but not when its debt is increased.
It seems that this indeed reflects the behavior of our code (see: onAdjustTrove() doesn't update lastInterestRateAdjTime).
We had similar discussions regarding joining/leaving batches (CS-BOLD-021)

  1. Attacker opens a bunch of small (e.g. 2000 BOLD) troves with relatively low interest rates, aiming to keep them at the bottom of the list by adjusting their rates from time to time or reopening them after redemptions if necessary. The overall costs of doing this shouldn't be too high since those troves are relatively small.
  2. Attacker now opens a big trove with 0.5% interest rate
  3. In case of an incoming redemption, the attacker frontruns it by closing his big trove. He then increases the debt and collateral of one of his small troves.
    a. The attacker incurs the upfront fee on the debt added to the small trove
    b. lastInterestRateAdjTime isn't reset since it's only a debt increase
  4. Attacker can now wait for the next redemption and frontrun it by increasing its rate without having to pay an upfront fee even if step 3 happened less than 7 days ago, assuming that the increased trove is older than 7 days.
  5. After 7 days the attacker reduces the increased trove's rate back to 0.5%
  6. In case of an incoming redemption, the attacker can proceed with step 3. Rinse and repeat.

From what I can see, this strategy allows the attacker to make free adjustments within less than 7 days, which is unfair.

@bingen bingen self-assigned this Dec 3, 2024
@ColinPlatt
Copy link
Collaborator

ColinPlatt commented Dec 6, 2024

Looking at the one-premature-adjustment-fee-free change, I had a few thoughts.

First and foremost, recall that premature adjustment fees were originally designed as a solution to a user monitoring redemptions, and optimistically moving further up the redemption queue, and then back down, while incurring minimal increased interest rates (by virtue of maintaining a low interest rate for a majority of the time, and higher rates during relatively short periods.

A fee equal to one week of, roughly, average interest was therefore levied for all changes made within the cooldown period (one week). Which meant that the above strategy would pay the lower rate, plus 2 weeks at the average rate, making this simple strategy unprofitable.

ChainSecurity’s analysis, yielded a finding, where two changes could be made for free, where a user who has adjusted less than 7 days previously, joins a batch that they control and which had not been changed previously for at least 7 days, and which has a different interest rate than the trove currently has (first free change), and then making a 2nd change before the 7 day period of the trove before this exploit (“second free change”).

Recall that we closed this, by requiring that moving into a batch would trigger a premature adjustment fee in the above, effectively closing the first, but not the second free change.

Robert’s scenario using smaller troves and adjusting a larger troves’ debt to one of the smaller troves, is another example of this effect of using the second fee free change.

Our later discussions, pointing out that more sophisticated optimisations of the first variant, a batch which can be controlled to exercise the second free change (“empty batch method”).

Both Robert’s scenario, and the empty batch method, require a user wanting the ability to make the second free change to pay an upfront premature adjustment fee, and give the user up-to 7 days to take advantage of this adjustment, after which, no premature adjustment would have taken place.

Knowing this helps us simply the advantage, and risks of performing this exploit.

I propose that we compare the behaviour of an entity wishing to gain access to the second free change (“Attacker”) with that of an honest user who simply adjusts their rates.

Robert’s scenario was as follows:

Attacker opens a bunch of small (e.g. 2000 BOLD) troves with relatively low interest rates, aiming to keep them at the bottom of the list by adjusting their rates from time to time or reopening them after redemptions if necessary. The overall costs of doing this shouldn't be too high since those troves are relatively small.
Attacker now opens a big trove with 0.5% interest rate
In case of an incoming redemption, the attacker frontruns it by closing his big trove. He then increases the debt and collateral of one of his small troves.
The attacker incurs the upfront fee on the debt added to the small trove
lastInterestRateAdjTime isn't reset since it's only a debt increase
Attacker can now wait for the next redemption and frontrun it by increasing its rate without having to pay an upfront fee even if step 3 happened less than 7 days ago, assuming that the increased trove is older than 7 days.
After 7 days the attacker reduces the increased trove's rate back to 0.5%
In case of an incoming redemption, the attacker can proceed with step 3. Rinse and repeat.

An honest user by comparison would perform the following:

Honest user opens a single trove with the total amount equal to the Attacker’s small troves and big trove with 0.5% interest rate
In case of an incoming redemption, the honest user increases their rate to a new safe rate.
lastInterestRateAdjTime is reset
Honest user can now wait for the change in redemptions and decide to increase its rate, paying an upfront fee [using this interchangeably with premature adjustment fee] if step 3 happened less than 7 days ago, otherwise not paying paying an upfront fee
After 7 days the Honest user reduces the trove's rate back to 0.5%
In case of an incoming redemption, the honest user can proceed with step 2. Rinse and repeat.

The key to understand here is the time change between steps 1 and 3 in the attacker’s scenario, and between 1 and 2 in the honest user scenario.

In the event that 7 days have passed in both, the attacker would have paid today to access the second rate change, whereas the honest user would not have. This means that the attacker would have to save more in interest (paying a sufficiently lower amount in interest than incurred by the upfront fee, while not being forced to exercise their option (step 4) too long before 7 days have passed, but not more than 7 days, ideal exercise time would be seconds before the end of their original cooldown period, but this is not a decision taken by the attacker but by third party redeemers, so we assume cannot be influenced by the attacker or known ex ante). Being forced to proceed to step 4 soon after step 3 means that the interest saved would be lower (for simplicity, let’s assume they are forced to move to the same rate that the honest user sets in their step 2), as they would now move to the higher rate (post 4) and complete the remainder of the 7 days at the new higher rate, whereas they would have paid the sunk cost of the upfront fee (which was not done by the honest user). Their relative PnL (compared with the honest user would be the difference of the interest rate saved over the time in effect, less the upfront fee). We can work out what this difference would be, but unless the difference between the higher than attacker step 2 IR, but lower than attacker step 4 fee set in attacker’s step 3 is sufficiently broad, this attack is likely to not be profitable for the attacker.

If we, conversely, assume that both the attacker and honest user have changed their IRs less than 7 days (between attacker step 1 and 3, and honest user step 1 and 2), then the math is a little different.

Because the primary motivation of changing the interest rate for both the attacker and the honest user is in response to an increased risk of redemption (recall that by taking the risk and not changing, they both have to pay the upfront fee to rebuild their position post a redemption, which can be said to be their max downside). I propose that the behaviour of the attacker and the honest user would vary at steps 3 and 2, respectively.

Recall at step 1, both had a rate of 0.5%. As both know that by setting their rate, they will be either forced to pay a rate which is perceived as safe for 7 days, or face a further upfront fee. An honest user would therefore likely opt for a “safe rate”, which balances their interest rate costs, while minimising the risk that they need to repeat step 2 in less than a week.

The attacker, however, has a second strategy to maximise their PnL. They ideally set an “intermediate rate” which avoids redemptions now, but will likely need to be changed less than 7 days from now (after which the intermediate rate is deemed to have been safe, and the honest user simplified overpaid, but this is purely speculative and no second free change was realized, i.e. the attacker took a zero-sum bet and won versus the honest user). To determine the potential PnL of this scenario, we need to now determine the difference between the intermediate rate and the safe rate, up to a maximum of 7 days.

I have tried to map this out in this spreadsheet

You can also find a visual in this presentation

As we do not know, ex ante, when redemptions will come through, an attacker may be forced to exercise this optionality, moving to the safe rate, almost immediately. This would mean that their savings are minimal. Therefore the worst case from the point of view of honest users, is that the attacker is forced to move, but only moments before the 7 day cooldown has ended.

It’s worth noting, that repeating the attack after 7 days, whether option in step 4 is exercised or not, it likely to return to the scenario where the honest user is better off, as they would have not changed their rate, and the attacker would have to pay to gain access to the second free change option.

I would argue that the further in time the forced exercise of step 4 occurs, the greater the uncertainty. Said differently, setting an intermediate rate which needs to almost immediately be raised to the safe rate results in the least PnL for the attacker (at the expense of honest users), and can be most accurately predicted. Setting an intermediate rate that is closer to the safe rate (higher), reduces the maximum PnL of the attacker, and minimizes the socialized cost.

Finally, I would offer, that if the attacker has a sufficient weight in the system, and is able to materially change the yield paid out across BOLD, the greater the likelihood of a subsequent redemption occurring as third parties sell their BOLD due to below market yield, forcing the attacker to exercise 4 over a shorter period of time.

In closing, I think that there are significant differences between the original finding from ChainSecurity and the issue proposed by Robert, as well as the empty batch method. Given the highly path dependent nature of a profitable payoff for an attacker, as well as the degradation of UX for honest users, my suggestion is that we do not make any further code changes for this issue, and document the issue as a known issue in the readme.

@bingen bingen added the wontfix This will not be worked on label Dec 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

2 participants