# \[High\] Treasury: bond payments never raise totalReserves → deterministic bond-market halt

**URL:** https://ghostforum.org/t/high-treasury-bond-payments-never-raise-totalreserves-deterministic-bond-market-halt/110
**Category:** Submitted
**Tags:** ghost-dao-contracts
**Created:** [October 2, 2026, 11:42am UTC](https://ghostforum.org/t/high-treasury-bond-payments-never-raise-totalreserves-deterministic-bond-market-halt/110 "2026-10-02T11:42:44Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![atla](https://ghostforum.org/letter_avatar_proxy/v4/letter/a/a5b964/32.png) [@atla](https://ghostforum.org/u/atla)
#### Post date: [October 2, 2026, 11:42am UTC](https://ghostforum.org/t/high-treasury-bond-payments-never-raise-totalreserves-deterministic-bond-market-halt/110/1 "2026-10-02T11:42:45Z")

</div>

## Summary

Bond payments in `BondDepository.deposit` go **directly** to the `TREASURY`  
address via `safeTransfer`/`safeTransferFrom` never through  
`Treasury.deposit()`, the only function that raises `totalReserves` (besides  
governor-only `auditReserves()`). But every bond’s `addNote` calls  
`Treasury.mint(payout)` (`src/types/NoteKeeper.sol:49`), which requires  
`amount <= excessReserves()` where  
`excessReserves() = totalReserves - (FTSO.totalSupply() - totalDebt)`  
(`src/Treasury.sol:260`; `totalDebt` is never written, always 0).

So each bond **lowers** `excessReserves` by exactly its payout while  
`totalReserves` stagnates. Once cumulative payouts exceed the initial buffer,  
**every** new bond reverts with `InsufficientReserves` a deterministic,  
permanent halt of the core bond market until governor/guardian calls  
`auditReserves()`. The treasury demonstrably holds the bond payments the whole  
time: pure accounting drift.

Severity

**High.** Complete, deterministic denial of the protocol’s core bond-issuance  
function; recovery is gated on a trusted role. No fund loss, but the market  
bricks itself through normal use.

Steps to Reproduce

1. Seed reserves properly: `treasury.deposit(reserve, X, X_value/2)` →  
`excessReserves = E0 > 0`.
2. Execute bonds. After each bond assert:  
`treasury.totalReserves()` unchanged, `ftso.totalSupply()` up by `payout`,  
`treasury.excessReserves()` down by exactly `payout`.
3. Keep bonding; eventually `depository.deposit(...)` reverts  
`InsufficientReserves()` from `Treasury.mint` inside `addNote`.
4. Confirm `weth.balanceOf(treasury) > 0` (funds present — accounting only).
5. `vm.prank(governor); treasury.auditReserves();` → bonds work again.

## Proof of Concept

Foundry test against the actual contracts (read-only reference). Full PoC  
source available on request.

```auto
cd poc-foundry && forge test --match-test 'test_GH08' -vv
# PASS test_GH08_bondDoesNotRaiseTotalReserves (mechanism: -payout per bond)
# PASS test_GH08_bondDoSUntilAudit (drain loop -> revert -> audit recovery)

```

Key assertions from the passing tests:

```auto
// after one real bond:
assertEq(treasury.totalReserves(), reservesBefore); // flat
assertEq(treasury.excessReserves(), excessBefore - payout); // drained exactly
// after the drain loop:
vm.expectRevert();
depository.deposit{value: amount}(drainId, amount, type(uint256).max, ALICE); // InsufficientReserves
assertGt(weth.balanceOf(address(treasury)), 0); // funds are there

```

## Impact

The bond market has a deterministic expiry measured in cumulative payout  
volume. In production this reads as “bonds randomly stop working”; the only  
fix is a trusted-role `auditReserves()`, i.e. recurring manual intervention  
for a core function. It also makes `buyBack()` underpay while drifted.

## Remediation

Route bond payments through `Treasury.deposit()` (or otherwise credit  
`totalReserves`) instead of transferring directly to the `TREASURY` address.  
Short-term: document the required `auditReserves()` cadence and consider  
making it permissionless-but-rate-limited, since the values it reads are  
on-chain anyway.

---

<div class="post-metadata">

### Author: ![VoidDragon](https://ghostforum.org/user_avatar/ghostforum.org/voiddragon/32/16_2.png) [@VoidDragon](https://ghostforum.org/u/VoidDragon)
#### Post date: [October 3, 2026, 8:35pm UTC](https://ghostforum.org/t/high-treasury-bond-payments-never-raise-totalreserves-deterministic-bond-market-halt/110/2 "2026-10-03T20:35:59Z")

</div>

Thank you for your report!

It looks like you’ve spotted an issue inherited from Olympus DAO V2. This is more a matter of monetary policy and game theory than of technology. As I understand it, the intent is to ensure that the backing price - that is, the risk-free value (RFV) - is not affected by bond purchases, so that user actions don’t mix into it.

For that reason, there is an `auditReserves` function that acts as a governance trigger to recalculate the backing price. If you see a more in-depth or complex abuse of this mechanism, please continue the thread.

---

<div class="post-metadata">

### Author: ![atla](https://ghostforum.org/letter_avatar_proxy/v4/letter/a/a5b964/32.png) [@atla](https://ghostforum.org/u/atla)
#### Post date: [October 4, 2026, 1:53am UTC](https://ghostforum.org/t/high-treasury-bond-payments-never-raise-totalreserves-deterministic-bond-market-halt/110/3 "2026-10-04T01:53:07Z")

</div>

Thanks for the thoughtful response! Two data points, respectfully:

**1. On the Olympus V2 inheritance:** I checked the `Version-2` branch of OlympusDAO/olympus-contracts directly. There, `Treasury.mint` is indeed gated on `excessReserves()` (and restricted to REWARDMANAGER) but `BondDepository.sol` contains zero `mint()` calls, so V2’s bond flow never touches that gate. In GHOST, `NoteKeeper.addNote` calls `_TREASURY.mint` on _every_ bond, which is what routes the bond flow through the gate. So the toxic combination intake bypassing `Treasury.deposit()` plus payout gated on `excessReserves()` is GHOST’s own composition, not a faithful inheritance. (If you mean a different V2 lineage, happy to be pointed at the exact contract.)

**2. On intent vs. effect:** if the goal is “RFV unaffected by bond purchases,” the accounting should credit reserves by the intake amount (that’s what `Treasury.deposit()` is for) then RFV moves only by the protocol’s discount profit per bond, which is the actual Olympus model. The current code does the opposite: reserves flat, supply up, so RFV strictly _decays_ with every bond until `InsufficientReserves` bricks the market. The mechanism achieves the inverse of the stated intent.

**3. The deeper abuse you asked for:** the drain is attacker-accelerable and profitable. Each bond earns the bonder the discount while reducing `excessReserves` by the full payout, so a griefer can deterministically halt the bond market for all users at net profit, with recovery gated on a trusted `auditReserves()` call. Core-market liveness depending on manual governance intervention is an availability issue with a concrete abuse path.

## **Suggested fix stays minimal: route bond intake through** `Treasury.deposit()` **(or otherwise credit** `totalReserves`). **Happy to demonstrate end-to-end on testnet if useful.**

---

<div class="post-metadata">

### Author: ![VoidDragon](https://ghostforum.org/user_avatar/ghostforum.org/voiddragon/32/16_2.png) [@VoidDragon](https://ghostforum.org/u/VoidDragon)
#### Post date: [October 12, 2026, 10:20am UTC](https://ghostforum.org/t/high-treasury-bond-payments-never-raise-totalreserves-deterministic-bond-market-halt/110/4 "2026-10-12T10:20:04Z")

</div>

After an internal discussion, we’ve identified what amounts to a trap here:

1. We can’t allow `deposit` to be called during a bond purchase, because that’s a direct way to manipulate `totalReserves` via _flash loans_.
2. Meanwhile, `auditReserves` is a governance-only function called via an external Schnorr signature, which minimizes the risk of misuse.

From what we can see right now, staying with the current version is better than the alternatives, though it isn’t the best possible solution. Any thoughts on this?

---

<div class="post-metadata">

### Author: ![atla](https://ghostforum.org/letter_avatar_proxy/v4/letter/a/a5b964/32.png) [@atla](https://ghostforum.org/u/atla)
#### Post date: [October 13, 2026, 2:26am UTC](https://ghostforum.org/t/high-treasury-bond-payments-never-raise-totalreserves-deterministic-bond-market-halt/110/5 "2026-10-13T02:26:47Z")

</div>

Appreciate the straight answer and fair pushback on the naive version of my suggestion. A few thoughts:

**1. You’re right to reject the naive `deposit()` call but the blocking reason is the mint side-effect, not flash loans.** I re-read `Treasury.deposit()`: it’s permissioned (RESERVEDEPOSITOR), pulls real tokens via `safeTransferFrom`, and credits `tokenValue(token, amount)` a deterministic function of the amount actually moved, with no balance snapshot or spot oracle in the reserve-token branch. For the bond flow, that amount would be the genuine payment, so a flash loan can’t inflate the credit beyond real intake and an external attacker can’t call `deposit()` at all. The real problems with the naive call are (a) it would require granting the depository RESERVEDEPOSITOR (broad), and (b) it mints `value − profit` FTSO to the caller on top of the bond payout. So: trap correctly identified, slightly different mechanism than stated.

**2. There’s a third option that escapes the trap: a scoped, mint-free intake credit.** E.g. a depository-only `creditBondIntake(token, amount)` on the Treasury that just does `totalReserves += tokenValue(token, amount)` no transfers, no minting, callable only by the depository, invoked in `BondDepository.deposit` _before_ `addNote` (note the mint gate currently runs before the payment even arrives). Flash loans can’t touch it since `amount` is the actual payment in the same call, permissions stay minimal, and `excessReserves` then decays by the true per-bond discount cost instead of the full payout.

**3. On hardened `auditReserves`:** genuinely good Schnorr-gated governance recovery minimizes misuse risk, credit where due. But note the liveness implication: a heavier recovery path means _longer_ halt windows when the drift trips the gate, and the griefing profit (the bond discount) doesn’t depend on `auditReserves` at all. The hardening makes misuse safer, not the halt shorter.

#### So: status quo vs. naive `deposit()` I agree status quo wins. Status quo vs. the scoped credit the scoped credit keeps your flash-loan and permission concerns at zero while removing the deterministic halt. If you still prefer to accept the risk, that’s a legitimate call for a High I’d just suggest documenting the `auditReserves` cadence the market needs, since the halt is deterministic (cumulative payout vs. buffer), not probabilistic.
