-
Notifications
You must be signed in to change notification settings - Fork 2
Add ADR-0001: MOET Reserve Architecture #278
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
Open
vishalchangrani
wants to merge
10
commits into
main
Choose a base branch
from
vishal/first_adr
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
30d01a5
Add ADR-0001: MOET Reserve Architecture (pure mint/burn model)
vishalchangrani a20658a
Trim ADR-0001 to ~half length
vishalchangrani 6a4bd7a
Removing ADR from the title since its redundant
vishalchangrani b90e5f5
Update docs/adr/ADR-0001-MOET-Reserve-Architecture.md
vishalchangrani 01aadbb
Update docs/adr/ADR-0001-MOET-Reserve-Architecture.md
vishalchangrani fc764ad
Update docs/adr/ADR-0001-MOET-Reserve-Architecture.md
vishalchangrani dd80c54
Update docs/adr/ADR-0001-MOET-Reserve-Architecture.md
vishalchangrani 6456568
Update docs/adr/ADR-0001-MOET-Reserve-Architecture.md
vishalchangrani 5b3400a
Update ADR-0001-MOET-Reserve-Architecture.md
vishalchangrani 3b97ca8
Merge branch 'main' into vishal/first_adr
vishalchangrani File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| # MOET Reserve Architecture — Pure Mint/Burn Model | ||
|
|
||
| **Status**: Accepted | ||
| **Date**: 2026-03-05 | ||
| **Authors**: Jordan Schalm, Dieter Shirley (review), Alexandr Ni (implementation) | ||
| **Component**: ALP / MOET | ||
|
|
||
| --- | ||
| ## Provenance | ||
| This document was generated by Sonet 4.6 from the Slack discussion [here](https://flow-foundation.slack.com/archives/C08QF29F7TK/p1772730444226649) and Q&A session on March 5th, 2026. | ||
|
|
||
| ## Context | ||
|
|
||
| FlowALP treats MOET differently from every other token in the system: it is the endogenous synthetic stablecoin minted against collateral. This raises a structural question about how the protocol should manage MOET internally when it is used as collateral or borrowed as debt. | ||
|
|
||
| Three candidate designs were on the table: | ||
|
|
||
| 1. **Vault-backed reserves** — hold actual MOET in a reserve pool, lending from it as needed. | ||
| 2. **Hybrid** — lend from reserves while reserves exist, mint otherwise; return repayments to reserves if space, burn otherwise. | ||
| 3. **Pure mint/burn** — treat all MOET supply inside the protocol as virtual; mint on every withdrawal (debt issuance or collateral return), burn on every deposit (debt repayment or collateral deposit). | ||
|
|
||
| A secondary question: how should insurance fees denominated in MOET be handled, given that there is no MOET reserve vault in the pure mint/burn model? | ||
|
|
||
| The decision was needed to unblock PR #184 (multi-debt-type positions) and to eliminate scattered `isMOET` guard clauses throughout the protocol's token-agnostic lending logic. | ||
|
|
||
| ### Background: Net MOET Supply Dynamics | ||
|
|
||
| Because interest accrues on borrowed MOET, total repayment obligations will always exceed total MOET minted — borrowers owe more than was issued. The system resolves this through the **redeemer contract** (and DEX liquidity in the interim): anyone can exchange stablecoins for MOET, ensuring supply can always be supplemented when repayment demand exceeds circulating supply. | ||
|
|
||
| --- | ||
|
|
||
| ## Competitive Benchmarks | ||
|
|
||
| **MakerDAO/Sky (DAI)** and **Aave GHO** both follow the pure mint/burn model: synthetic supply is entirely virtual, minted on borrow, burned on repay, with no reserve vault held by the protocol. | ||
|
|
||
| **Key lesson from production:** Hybrid models introduce accounting complexity where reserve balance and outstanding debt can diverge, creating insolvency edge cases. MakerDAO explicitly rejected reserve-backed minting for DAI for this reason. | ||
|
|
||
| --- | ||
|
|
||
| ## Decision | ||
|
|
||
| FlowALP adopts a **pure mint/burn model** for all MOET flows through the token reserve interface. | ||
|
|
||
| The token reserve for MOET is implemented as an object satisfying `FungibleToken.Receiver` and `FungibleToken.Provider`, but backed by a MOET minter rather than a vault: | ||
|
|
||
| - **`withdraw` (any amount)** → mints new MOET and returns it to the caller. | ||
| - **`deposit` (any amount)** → burns the received MOET immediately. | ||
|
|
||
| This applies uniformly to both collateral and debt operations — all four paths (`depositCollateral`, `depositRepayment`, `withdrawDebt`, `withdrawCollateral`) route through the same two-method interface. | ||
|
|
||
| **Insurance fees** are the one explicit exception: collected and held in a **separate, dedicated MOET vault** (obtained through AMM swap at collection time, never routed through the burn path, never used for lending). | ||
|
|
||
| --- | ||
|
|
||
| ## Rationale | ||
|
|
||
| ### Why pure mint/burn over vault-backed reserves? | ||
|
|
||
| Vault-backed reserves create an accounting gap that grows with interest accrual: | ||
|
|
||
| - User A deposits 100 MOET as collateral. Reserve holds 100 MOET. | ||
| - User B borrows 100 MOET. Reserve is empty. | ||
| - User A repays 5 MOET interest. Reserve holds 5 MOET. | ||
| - User A withdraws collateral. Protocol can only provide 5. | ||
|
|
||
| Under pure mint/burn, every deposit is burned and every withdrawal is minted on demand — the protocol never needs to hold inventory and the books always balance. | ||
|
|
||
| ### Why not the four-method interface? | ||
|
|
||
| A four-method interface with asymmetric MOET behaviour (vault for collateral, burn for repayments) was rejected because with the chosen mint-and-burn strategy, we do not treat deposits of collateral/repayment differently with MOET. | ||
|
|
||
| ### Why hold insurance fees rather than burn them? | ||
|
|
||
| MOET is minted only when collateral of greater value is deposited into FlowALP. Conceptually mint-and-burn insurance funds would work similarly (the contract would burn collected insurance funds, track how much it collected, and only allow itself to mint that amount in the future). However, since insurance collection and use occur at substantially different times, it is more difficult to demonstrate and audit this. With insurance funds stored in a vault, we can simply rely on the vault's balance semantics to demonstrate the limitations of insurance. | ||
|
|
||
| --- | ||
|
|
||
| ## Alternatives Considered | ||
|
|
||
| **1. Vault-backed reserves** — Rejected. Interest accrual creates an accounting gap; the reserve cannot satisfy withdrawal obligations over time. | ||
|
|
||
| **2. Four-method interface with asymmetric MOET behaviour** — Rejected. Burning repayments without vault deposits still requires periodic minting; adds complexity without eliminating the gap. | ||
|
|
||
| **3. Hybrid model** — Rejected. Produces accounting invariant violations where reserve balance and outstanding debt diverge. No production DeFi protocol uses this for an endogenous synthetic. | ||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## Implementation Notes | ||
|
|
||
| The token reserve interface satisfies both `FungibleToken.Receiver` and `FungibleToken.Provider`: | ||
|
|
||
| - **Non-MOET tokens**: backed by a `Vault`; `withdraw`/`deposit` pass through directly. | ||
| - **MOET**: backed by a minter resource. `withdraw` calls `minter.mintTokens(amount)`; `deposit` calls `burnCallback()`. | ||
|
|
||
| Insurance fees are minted separately and deposited into a dedicated `insuranceFund` vault. An epsilon adjustment (±1 unit) may be applied during collection to maintain the accounting invariant that net credits + net debits + fees = 0. | ||
|
|
||
| --- | ||
|
|
||
| ## User Impact | ||
|
|
||
| - **Borrowers / liquidators**: No change to mechanics, rates, or health factor calculations. The reserve model is internal. | ||
| - **MOET depositors**: Deposited MOET is burned immediately; on withdrawal, equivalent MOET is minted. Net economic effect is identical to vault-backed storage — users observe no difference. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.