# Proposal/Ideas to Increase Zero Redemption Friction

**URL:** <https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434>\
**Category:** Bitocracy\
**Created:** [December 9, 2025, 7:01pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434 "2025-12-09T19:01:31Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Brianna](https://avatars.discourse-cdn.com/v4/letter/b/6f9a4e/32.png) [@Brianna](https://forum.sovryn.com/u/Brianna)\
**Post date:** [December 9, 2025, 7:01pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/1 "2025-12-09T19:01:31Z")

</div>

Hi everyone that’s still around! This is my first post on the forum, but felt it better than the discord to actually log ideas and keep the conversation going.

Here’s my ideas that have been thought out on discord and hope to hear others, that we can build into a SIP and take a vote.

Here’s the issue:  
Ultimately I believe we need to come up with an idea to protect borrowers so that people feel safe pledging their Bitcoin as backing for issuing ZUSD/DLLR. We want the supply of DLLR to grow, not contract. Currently people are scared to deposit their bitcoin for fear it will be redeemed the moment there’s a correction in price even at 600% collateral ratios. This creates a significantly inefficient use of capital where you end up with $33M worth of Bitcoin in the system, yet only a 10th of it is used to issue ZUSD. It also prevents people from adding Bitcoin to the system because there is significant risk even if you’re over collateralized by 600%.

My possible solutions:

1. Build a redemption buffer owned by SOV stakers. This idea offloads the risk to stakers rather than the collateral providers. Stakers currently make a fee percentage of every ZUSD issued against BTC. I’m thinking split the fee so stakers receive half of the fee in the newly issued ZUSD and the remaining half in BTC from the borrower that is deposited into the redemption buffer that is used as the first line of redemption. If this redemption buffer ever grows large enough that it no longer makes sense, stakers can vote to distribute some of it back to stakers. Or we could have it automatically happen if it ever reaches some arbitrary number like 30% of all ZUSD. When someone redeems ZUSD against the RBTC redemption buffer that ZUSD will be distributed to stakers.
2. I believe we want to disincentivize redemptions especially from arbitragers using Zero against the RBTC/DLLR AMM. The reason being as Zero currently stands you cannot possibly get a better price for purchasing RBTC on rootstock than through Zero. This is not what you want for a product like this. It essentially means collateral providers are agreeing to sell their BTC at literally mid market prices (no market maker in the world would do this). If we had the staker owned redemption buffer then stakers would be agreeing to that. It’s just a horrible idea all around. You absolutely want collateral providers to have the ability to repay their loans at mid market price, but not other parties. So my best idea to solve this is to model like Tether and have a minimum redemption size of $100K ZUSD and a minimum set fee of $1K per redemption.

I think it’s important that earlier discussions on this be looked at from a larger time frame. US dollar only has high demand in major market corrections - after a over valuation of assets. When the decisions were being made on Zero redemption and origination it was at the end of a major Bitcoin/equity market correction and demand for US Dollars were falling. There were concerns about a depeg of ZUSD if redemption fees were too high. During that same period USDC and USDT also lost their pegs. That is not just a coincidence with the bottom of the market in early 2023 - that’s a very expected move as US dollars are sold to repurchase assets at suppressed prices.

If we can limit redemptions through Zero it also opens an opportunity for actual markets to emerge trading DLLR for other stable coins at low spreads. I’m currently working on a rootstock contract to make markets for DOC/DLLR and RBTC/DLLR so that DLLR holders have more utility at lower spreads and no fees. I’m positive if we can increase the friction for Zero redemption, then we will see a wider spread use of DLLR as the supply grows.

Please share your ideas and any comments on my own (which were helped by others in discord discussions). I really hope we can make some traction on improving Zero. Thank you all!

—– Update: ——  
I think idea #2 or anything surrounding modifying the redemption rates seems to have a lot of contention. I’m all for focusing on adding a Redemption Buffer and then see how it goes and come back to redemption fee modifications at a later time if we still feel it’s necessary.

Here’s the PR code modifications in progress for discussion:

> <https://github.com/DistributedCollective/zero-contracts/pull/9>
>
> Still needs tests for RedemptionBuffer behavior.
> 
> \- When a borrower opens a tr…ove, X% of RBTC goes into a RedemptionBuffer
> \- Redemptions pull from RedemptionBuffer first, at oracle price
> \- ZUSD redeemed via buffer is swapped and sent to FeeDistributor for stakers.
> \- Governance can drain excess from RedemptionBuffer to stakers
> 
> With RedemptionBuffer set:
> 831 passing (9m)
> 23 pending
> 
> No new tests added.

— System-Level Changes —

### A) New “RedemptionBuffer” subsystem (major new feature)

**New in the updated code:**

- A **new RBTC buffer pool** (`RedemptionBuffer`) is introduced.

- Borrowing/minting ZUSD now funds this buffer via a **new RBTC fee** charged in `BorrowerOperations`.

- Redemptions now run **buffer-first** :

This feature does **not exist at all** in your old versions.

* * *

### B) Redemption semantics changed: buffer portion is a swap, not a debt burn

Old redemption:

- Entire redeemed ZUSD is **burned**

- System debt (`ActivePool`) is **decreased** by the same amount

New redemption:

- **Trove-sourced** portion behaves like old (burn + decrease system debt)

- **Buffer-sourced** portion:

This is a meaningful change to the economics/settlement behavior.

* * *

### C) FeeDistributor now accepts RBTC from RedemptionBuffer + can be triggered by it

Old:

- `FeeDistributor.distributeFees()` callable only by BorrowerOps or TroveManager

- `FeeDistributor.receive()` accepts RBTC only from `ActivePool`

New:

- `FeeDistributor`:

* * *

### D) Reentrancy hardening added (TroveManagerBase + shared storage + BorrowerOperations)

Old:

- No shared `nonReentrant`/`requireNotEntered` at TroveManagerBase layer

- No BorrowerOperations reentrancy guard state

New:

- Adds **reentrancy guard framework** in `TroveManagerBase`

- Adds **shared `_reentrancyStatus`** in TroveManager storage (so TroveManager + delegatecall module share the same lock)

- Adds **BorrowerOperations reentrancy state** as well

* * *

### E) Explicit liquidation accounting fix (batch recovery mode)

Old bug (in batch recovery mode liquidation):

- `vars.entireSystemColl` updated by subtracting only `collToSendToSP`

- **did not subtract `collSurplus`** , which can skew TCR/recovery-mode transitions

New fix:

- subtracts both:

* * *

# Contract-by-contract diff

## 1) `RedemptionBuffer` (NEW CONTRACT)

**Added entirely** in the updated code.

Key capabilities (new):

- Receives RBTC (tracked via `totalBufferedColl`)

- `deposit()` (only BorrowerOperations)

- `withdrawForRedemption(to, amount)` (only TroveManager)

- `distributeToStakers(amount)` (owner-only; forwards to FeeDistributor + triggers distribution)

- `syncBalance()` (owner-only accounting reconciliation)

- `getBalance()` view

- `receive()` updates internal accounting

**Nothing in the old code corresponds to this contract.**

* * *

## 2) `BorrowerOperations` (old → new)

### Added: RedemptionBuffer configuration & fee quoting

New functions/events you did **not** have in old:

- `setRedemptionBufferAddress(address)`

- `setRedemptionBufferRate(uint256)`

- `getRedemptionBufferAddress()`

- `getRedemptionBufferRate()`

- `getRedemptionBufferFeeRBTC(uint256 _ZUSDAmount)` (uses `fetchPrice()`)

- `getRedemptionBufferFeeRBTCWithPrice(uint256 _ZUSDAmount, uint256 _price)` (view)

- Events:

### Changed behavior: `openTrove()` now skims RBTC into the buffer

Old `_openTrove`:

- Trove collateral = `msg.value`

- No extra fee to any buffer

New `_openTrove`:

- Computes a **buffer fee in RBTC** based on `_ZUSDAmount` and `price`

- Requires `msg.value > bufferFee`

- Trove collateral used for ICR/NICR + stored on trove = `msg.value - bufferFee`

- `bufferFee` is deposited into `RedemptionBuffer`

**Impact:** opening a trove now costs **extra RBTC** beyond the collateral you want in the trove.

### Changed behavior: debt increases also pay the buffer fee

Old `_adjustSenderTrove`:

- Coll change computed from `msg.value` vs `_collWithdrawal`

- If debt increase, msg.value could be `0` (unless also topping up collateral)

New `_adjustSenderTrove` (when `_isDebtIncrease == true`):

- Computes `bufferFee` from minted `_ZUSDChange` (and price)

- Requires `msg.value >= bufferFee`

- Treats `collTopUp = msg.value - bufferFee` as the _actual_ collateral increase

- Deposits `bufferFee` to `RedemptionBuffer`

**Very important compatibility change:**

- In old code, you could do “withdraw collateral + increase debt” with `msg.value == 0`.

- With a mandatory fee in `msg.value`, the new code must **separate fee vs actual coll top-up** so that “withdraw coll + increase debt” remains possible. (Your new code does this by using “collTopUp” rather than raw msg.value as the collateral-add component.)

### Added: rounding correctness for RBTC fee

New:

- `_ceilDiv(a,b)` and fee conversion uses ceil-division so the buffer fee is **not rounded down** when converting ZUSD-value fee → RBTC.

Old:

- No such fee conversion existed.

### Added: BorrowerOperations reentrancy state

New:

- BorrowerOperations now carries reentrancy state (not present in the old storage) and uses it to guard externally callable flows.

* * *

## 3) `BorrowerOperationsStorage` (old → new)

Old storage fields end at:

- `IMassetManager public massetManager;`

- `IFeeDistributor public feeDistributor;`

New storage adds:

- `IRedemptionBuffer internal redemptionBuffer;`

- `uint256 internal redemptionBufferRate;`

- `uint256 internal _reentrancyStatus;`

**Migration note:** if this is a proxy-based upgrade, this is a storage layout change (safe only if appended and ordering is preserved). If you redeploy, it’s irrelevant.

* * *

## 4) `TroveManagerRedeemOps` (old → new)

### Changed behavior: adds a buffer-first redemption stage

Old redemption flow:

1. Find redeemable troves (lowest ICR above MCR)

2. Redeem against troves only

3. Compute fee on `totalETHDrawn`

4. Pay fee from ActivePool → FeeDistributor

5. Burn ZUSD from redeemer

6. Decrease ActivePool ZUSD debt

7. Send net ETH to redeemer

New redemption flow (high-level):

1. **Swap from RedemptionBuffer first** (at oracle price):

2. Redeem remaining from troves (same concept as old)

3. Compute base rate and redemption fees using **(troves + buffer)** drawn ETH

4. Pay:

5. Send:

### Major semantic change: allowance requirement appears (buffer portion)

Old:

- No ZUSD allowance needed for `redeemCollateral` since TroveManager burns directly from the user via privileged `burn(...)`.

New:

- For the buffer swap portion, the module uses `ZUSD.transferFrom(redeemer, FeeDistributor, amount)`

- That means the redeemer must have **approved the TroveManager** for ZUSD (at least for the swapped portion).

- Your new code also adds an explicit revert message when allowance is insufficient.

### Refactor: parameter packing and helper extraction

Old:

- `_redeemCollateral(...)` took many parameters directly and contained the full trove walk inline.

New:

- Redemption parameters are packed into a struct (e.g. `RedeemParams`)

- Trove-walk extracted into helper (e.g. `_redeemFromTroves`)

- Buffer swap extracted into helper (e.g. `_swapFromBuffer`)

This is mostly a maintainability/stack-depth refactor, but it also supports the buffer insertion cleanly.

* * *

## 5) `TroveManager` (old → new)

### Added: RedemptionBuffer wiring

Old:

- No buffer address stored

- No setter

New:

- Adds `setRedemptionBufferAddress(address)` (owner-only; checkContract)

- Stores `IRedemptionBuffer` in TroveManager storage for use by delegatecall module

### Added/changed: reentrancy protection at TroveManager layer

Old:

- No `nonReentrant` / `requireNotEntered` guard pattern shared with the module.

New:

- TroveManagerBase introduces a shared reentrancy guard

- TroveManager + TroveManagerRedeemOps share the same `_reentrancyStatus` storage slot

- Certain external entrypoints (esp. redemption) are protected from reentrancy.

### Bug fix: Batch liquidation in Recovery Mode (collSurplus accounting)

Old `_getTotalFromBatchLiquidate_RecoveryMode`:

```auto
vars.entireSystemColl = vars.entireSystemColl.sub(singleLiquidation.collToSendToSP);

```

New:

```auto
vars.entireSystemColl = vars.entireSystemColl
    .sub(singleLiquidation.collToSendToSP)
    .sub(singleLiquidation.collSurplus);

```

**Effect:** system collateral tracking is correct when capped liquidations create surplus.

* * *

## 6) `TroveManagerStorage` (old → new)

Old has:

- `address public troveManagerRedeemOps;`

- core addresses/pools

- trove data and reward sums

- no reentrancy state

- no buffer pointer

New adds:

- `IRedemptionBuffer internal redemptionBuffer;`

- `uint256 internal _reentrancyStatus;` (shared lock used across TroveManager + delegatecall module)

* * *

## 7) `TroveManagerBase` (old → new)

### Added: shared reentrancy guard framework

Old:

- No reentrancy guard

New adds:

- `_NOT_ENTERED`, `_ENTERED`

- `nonReentrant` modifier

- `requireNotEntered` modifier

- initialization of `_reentrancyStatus`

* * *

## 8) `FeeDistributor` (old → new)

### Added: RedemptionBuffer address + authorization

Old:

- `distributeFees()` allowed callers:

- `receive()` allowed sender:

New:

- Adds `setRedemptionBufferAddress(address)` (owner-only)

- Adds getter for buffer address

- `distributeFees()` now also allows `msg.sender == redemptionBuffer`

- `receive()` now also allows RBTC from `redemptionBuffer`

**Why it matters:** buffer-funded redemption fees and distributions would revert against the old FeeDistributor.

* * *

## 9) `FeeDistributorStorage` (old → new)

Old:

- no buffer address

New adds:

- `_redemptionBufferAddress` (stored to authorize caller + RBTC sender)

* * *

## Practical integration impacts (things frontends/bots must change)

1. **Opening a trove / increasing debt now requires extra RBTC**

- The transaction must include enough RBTC to cover:

- You can quote this fee via the new `BorrowerOperations.getRedemptionBufferFeeRBTC(...)`.

1. **Redemptions may require ZUSD approval**

- If the redemption uses the buffer portion, the module uses `transferFrom`, so the redeemer needs to approve TroveManager for ZUSD (at least for the buffer-swapped amount).

1. **Deployment/config order matters**  
You must set:

- `BorrowerOperations.setRedemptionBufferAddress(...)`

- `BorrowerOperations.setRedemptionBufferRate(...)`

- `TroveManager.setRedemptionBufferAddress(...)`

- `FeeDistributor.setRedemptionBufferAddress(...)`  
…and wire `RedemptionBuffer.setAddresses(...)` appropriately.

---

<div class="post-metadata">

**Author:** ![Hyde](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.sovryn.com/hyde/32/235_2.png) [@Hyde](https://forum.sovryn.com/u/Hyde)\
**Post date:** [December 11, 2025, 1:05pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/2 "2025-12-11T13:05:01Z")

</div>

Just wanted to say a huge thank You for the detailed post - the rBTC redemption buffer owned by stakers is genuinely one of the most elegant ideas I’ve seen for Zero in a long time (at least since @one_digit post about [Resurecting Zero](https://forum.sovryn.com/t/resurrecting-zero/3242)). Tether-style minimums are also interesting option to consider. Really appreciate You writing it up properly here.

Quick update with today’s numbers so we’re all on the same page:

- ZUSD supply: ~3.22 M

- Collateral: ~348 rBTC → TCR ≈ 1,000 %

- Current redemption fee: already 2.72 % (and it climbs quickly with every new redemption because of the dynamic baseRate)

Those numbers come from this [analytics page](https://redash.sovryn.app/public/dashboards/HKpdoABrGlUNdRbXW4yBZceUU5jBQRwzmLSUDqKf?org_slug=default&p_w248_start=2022-06-15).

As You see redemption fees are already very high. These redemptions are no longer small arbitrage plays, but deliberate moves that are fine paying 2–3 % to force-out rBTC at oracle price.

That makes me think Your two proposals are still very intersting, but we might get the fastest relief by pairing them with a tiny extra layer that removes the fear for borrowers right now - for example a simple 24-hour redemption delay (still lets people exit, but stops instant attacks and panic cascades).

Once borrowing psychology flips and supply starts growing again, Your buffer + minimum-size ideas could become the perfect second line of defence.

Thanks again for sparking this - this is exactly the discussion we needed.

---

<div class="post-metadata">

**Author:** ![Brianna](https://avatars.discourse-cdn.com/v4/letter/b/6f9a4e/32.png) [@Brianna](https://forum.sovryn.com/u/Brianna)\
**Post date:** [December 11, 2025, 6:02pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/3 "2025-12-11T18:02:49Z")

</div>

You’re welcome!!! Thank YOU for the well thought out response!! 🩷 Let’s drop idea #2 or anything related to redemption fees and just focus on building a Redemption Buffer owned and controlled by stakers. I’ve updated the main post to share some possible code changes to the Zero contracts. Please keep the ideas flowing! I don’t want this to fall off like all of @one_digit ‘s efforts!

---

<div class="post-metadata">

**Author:** ![Hyde](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.sovryn.com/hyde/32/235_2.png) [@Hyde](https://forum.sovryn.com/u/Hyde)\
**Post date:** [December 11, 2025, 9:12pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/4 "2025-12-11T21:12:29Z")

</div>

On Discord You have written

> [@Brianna](#):
>
> **@Hyde** By the way I’m thinking we should focus just on the staker owned redemption buffer. That seems to be the biggest way to protect collateral holders. If we want to later change how the redemption fees work etc that’s fine, but ideas around it seem to have the most contention. I can’t imagine many would be against a single change of funding a redemption buffer that reduces the risk from collateral providers.

Some would be against. Now the whole 5% goes to stakers, if Your proposal would be accepted, it would be reduced to half. And rest would be distributed only when there is a redemption happening. On the other hand - more LoC’s - more fees for stakers. So if ZERO becomes attractive again, they will earn more anyway.

---

<div class="post-metadata">

**Author:** ![Brianna](https://avatars.discourse-cdn.com/v4/letter/b/6f9a4e/32.png) [@Brianna](https://forum.sovryn.com/u/Brianna)\
**Post date:** [December 11, 2025, 9:43pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/5 "2025-12-11T21:43:10Z")

</div>

Maybe, but as I have the code now, stakers can vote to distribute the RBTC in the RedemptionBuffer contract. So in effect that RBTC in there is owned by stakers until redeemed, but we’re just going to store it for the long term to protect borrowers and hopefully incentivize more utilization of the collateral. And like you say, if ZUSD grows, that will lead to likely more fees overall even if the rate is cut in half!

---

<div class="post-metadata">

**Author:** ![Hyde](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.sovryn.com/hyde/32/235_2.png) [@Hyde](https://forum.sovryn.com/u/Hyde)\
**Post date:** [December 11, 2025, 11:47pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/6 "2025-12-11T23:47:00Z")

</div>

Since You’ve mentioned that You now have the code ready, please keep in mind that, to ensure full transparency and safety, any modification to the smart-contract logic should:

- be very clearly and extensively commented in the code,

- include comprehensive unit and integration tests covering the new behavior,

- go through a formal SIP so the community can discuss the trade-offs,

- and undergo at least a thorough second review (ideally an external audit) before deployment.

This way, reviewers, testers, and users can quickly understand what changed and why, and we minimize the risk of unintended side effects.

---

<div class="post-metadata">

**Author:** ![Brianna](https://avatars.discourse-cdn.com/v4/letter/b/6f9a4e/32.png) [@Brianna](https://forum.sovryn.com/u/Brianna)\
**Post date:** [December 12, 2025, 12:49am UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/7 "2025-12-12T00:49:31Z")

</div>

Yeah it’s not that ready! lol I’ve been looking at the unit tests. Likely going to need some help with those, they are extensive! And so many of them are built assuming there’s no change in the collateral upon opening a new trove. I uploaded a draft PR so that people could see the proposed changes in actual code and help design it from there.

---

<div class="post-metadata">

**Author:** ![Hyde](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.sovryn.com/hyde/32/235_2.png) [@Hyde](https://forum.sovryn.com/u/Hyde)\
**Post date:** [December 12, 2025, 8:05am UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/8 "2025-12-12T08:05:20Z")

</div>

But still coments would be very helpful, to understand what was changed and why.

---

<div class="post-metadata">

**Author:** ![SetSovFree](https://avatars.discourse-cdn.com/v4/letter/s/e9c0ed/32.png) [@SetSovFree](https://forum.sovryn.com/u/SetSovFree)\
**Post date:** [March 4, 2026, 3:46pm UTC](https://forum.sovryn.com/t/proposal-ideas-to-increase-zero-redemption-friction/3434/9 "2026-03-04T15:46:20Z")

</div>

Late to this thread but want to add explicit support, especially in light of what’s now documented in the no-confidence SIP discussion.

Brianna has done something remarkable here: identified a structural flaw that’s been suppressing Zero utilisation for years, designed a technically sound solution, written draft contract code, and opened a PR — all as an unpaid community member.

To put that in context: the `zero-contracts` repo saw zero merged PRs in all of 2025 from the paid team. Zero is Sovryn’s primary revenue driver. That gap between what the community is producing for free and what the team has delivered on the protocol’s most critical product is no longer something leadership can wave away.

The Redemption Buffer proposal deserves to move to a formal SIP immediately. The core idea — a staker-owned BTC buffer that absorbs redemptions first, funded by a split of the origination fee — is elegant because it aligns everyone’s incentives correctly:

- Borrowers get real protection without needing to over-collateralise to irrational levels

- Stakers gain a growing BTC reserve they control via governance

- ZUSD supply can actually grow, which drives more fees for everyone

The $33M in locked BTC generating only ~$3.2M in ZUSD is the clearest possible signal that the product is broken at a structural level. This fix addresses it directly.

I’d like to see this fast-tracked. If the no-confidence SIP passes and an ISC takes over, formalising and funding completion of this work — including the test coverage and audit Brianna needs — should be one of the ISC’s first mandated actions.
