Skip to content

fix: round protocol calculations in favor of reserves - #62

Merged
DengreSarthak merged 3 commits into
mainfrom
fix/reserve-safe-rounding
Oct 25, 2026
Merged

DengreSarthak merged 3 commits into
mainfrom
fix/reserve-safe-rounding

Conversation

@sach2004

@sach2004 sach2004 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Addressed Issues:

No related issue — addresses the review requests to favour the reserve when rounding and to use shared Gluon Z liabilities and equity calculations.

Screenshots/Recordings:

Not applicable. This PR changes contract arithmetic, tests and documentation.

Additional Notes:

Previously, rounding fusion burns down could allow a small reserve withdrawal without burning enough tokens. Rounded intermediate prices could also overstate transmutation output.

This PR applies rounding according to the direction of value transfer:

  • Fission rounds proportional token outputs down.
  • Fusion rounds both required token burns up.
  • Transmutations value burned tokens down, value minted tokens up, and round the final output down.
  • The user's net amount rounds down after fees. Dev payments round down, with the remaining fee and dust retained in the reserve.
  • The volume-dependent beta fee term rounds up before applying its existing cap.
  • Reserve-ratio calculations preserve intermediate division remainders.

Gluon Z pricing

Following the review feedback, both tranche prices now use shared internal _liabilities and _equity functions.

The calculation follows the Ergo Gluon Z implementation, using its fixed qStar = 0.99.

For target Neutron liabilities T and reserve R:

  • Liabilities: min(T, R * T / (T + 0.01 * R))
  • Equity: R - liabilities

The 99% value is a normalization threshold, not a fixed cap on liabilities. It is separate from the deployer-selected critical reserve ratio.

This replaces the pricing branches that assigned the whole reserve to Neutron liabilities and returned zero Proton equity. Intermediate overflow is handled without treating an unrepresentable target liability as zero equity.

Liabilities round opposite to equity. The continuous equation preserves positive equity for positive reserve and finite target liabilities; integer rounding can still produce a zero price for an unrepresentably small value.

Existing behaviour

The remaining user-operation AmountTooSmall checks are removed, as discussed in review. Small inputs may produce zero output. Transmutations retain ExcessiveSlippage protection when output is below the caller's requested minimum.

Initialization still requires valid, positive seed supplies. Oracle fallback, intentional acceptance of older prices, zero-price no-ops, peg-adjustment steps, beta settings and dev access controls are preserved.

Already-merged PR #63 is integrated into this branch. Its deployer-selected reserve-ratio interface is retained.

Validation

  • git diff --check
  • forge fmt --check
  • forge lint --use 0.8.30 src test --deny warnings
  • Forced Solc 0.8.30 build and deployment-size checks
  • Full suite: 194/194 tests passed, with 512 runs per fuzz test
  • Gluon Z pricing suite: 12/12 tests passed again, with 2,048 runs per fuzz test

Pricing tests compare allocation and price bounds against an independent rational reference. They cover the normalization threshold, liabilities above reserve, extreme arithmetic, empty supply, zero oracle prices, reserve-token decimals from 0 to 18, and transmutation value conservation with and without dev fees.

Factory runtime is 24,143 bytes, leaving 433 bytes below the deployment limit under the tested settings. Reactor runtime is 14,165 bytes.

Slither has not been rerun on this revision. This PR does not deploy contracts or change network configuration. The updated pricing has not yet been exercised through a new Sepolia deployment.

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the [Discord server](https://discord.gg/hjUhu33uAn) and I will share a link to this PR with the project maintainers there
  • I have read the [Contribution Guidelines](../CONTRIBUTING.md)
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • Bug Fixes

    • Improved rounding across prices, reserves, fees, and token conversions to preserve value and prevent over-issuance.
    • Small conversions can complete with zero output when the minimum output is zero; minimum-output protection remains in place.
    • Fee and rounding remainders remain in the reserve, while volume-based fees round up when needed.
  • Documentation

    • Clarified minimum-output behavior, fee calculations, rounding rules, and outcomes for combined fee rates of 100% or more.

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: StabilityNexus/Gluon-EVM/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 7362e037-8ca8-4bb8-bdd0-47551d1b60ac
📥 Commits

Reviewing files that changed from the base of the PR and between a10b56a and 6b13726.

📒 Files selected for processing: 8
  • README.md
  • src/StableCoin.sol
  • test/DevFeeAccounting.t.sol
  • test/GluonIntegration.t.sol
  • test/GluonZPricing.t.sol
  • test/PegFactorRegression.t.sol
  • test/ReserveRounding.t.sol
  • test/UnsignedVolumeLedger.t.sol

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The contract now uses explicit rounding for price, reserve, issuance, redemption, fusion, transmutation, and fee calculations. Tiny operations can complete with zero-rounded outputs when minimum-output checks allow them. Tests and README documentation cover the updated rules.

Changes

Rounding and value accounting

Layer / File(s) Summary
Price and reserve calculations
src/StableCoin.sol, test/GluonZPricing.t.sol, test/ReserveRounding.t.sol, test/PegFactorRegression.t.sol
Price helpers accept rounding modes, reserve ratios retain fractional contributions, and peg-factor pricing handles overflow-aware liability and equity calculations.
Fission and fusion rounding
src/StableCoin.sol, test/DevFeeAccounting.t.sol, test/GluonIntegration.t.sol, test/ReserveRounding.t.sol
Fission uses cached supplies and the pre-deposit reserve. Fusion rounds token burns up. Tests cover zero-rounded outputs, bootstrap values, and balance changes.
Transmutation fees and output rules
src/StableCoin.sol, test/DevFeeAccounting.t.sol, test/GluonIntegration.t.sol, test/ReserveRounding.t.sol, test/UnsignedVolumeLedger.t.sol
Transmutation output prices use ceiling rounding. Zero-rounded outputs no longer trigger AmountTooSmall; minimum-output checks remain. Combined fee and volume-fee rounding rules are tested.
Rounding documentation
README.md
The README documents zero minimum-output behavior, combined fees, fee retention, and rounding directions across calculations.

Priority: ⬇️ Low

Estimated code review effort: 4 (Complex) | ~50 minutes

Change: Bug fix

Suggested labels: Solidity Lang, Documentation

Suggested reviewers: zahnentferner

Merge Risk: ⚪ Minimal · up to 6b137

The change adjusts rounding so protocol calculations favor reserves. Tests cover tiny operations, extreme prices, fee conservation and reserve safety, and no actionable merge-blocking issue was found. Rerunning Slither and exercising the updated pricing on a testnet deployment are advisable follow-ups.

Pre-merge checks: Passed 4
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: updating protocol calculations so rounding favors reserves.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks the rounded tide
Tiny coins pass through the gate
Prices turn with careful sides
Fees leave dust to guard the state
Tests hop over bounds and strain
The reserve holds its measured grain

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread src/StableCoin.sol
if (protonSupplyTokens == 0) return WAD;
if (normalizedReserve == 0) return 0;
if (normalizedReserve == 0 || basePrice == 0) return 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear why this function became so complicated. The corresponding _neutronPriceInBase function is not as complicated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neutron pricing takes the lower of two prices. Proton pricing uses what remains in the reserve after covering Neutrons, so I added rounding and overflow handling for that calculation. Some of the code is repeated; I will simplify it while keeping those protections.

Comment thread src/StableCoin.sol Outdated

liabilities = Math.min(normalizedReserve, liabilities);
uint256 equity = normalizedReserve - liabilities;
// An unrepresentable liability leaves no equity.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There must be a bug somewhere. Gluon's mathematical equations are explicitly designed to ensure that the equity, and hence the proton price never becomes zero.

Comment thread src/StableCoin.sol Outdated
if (high >= denominator) return 0;

uint256 liabilities = Math.mulDiv(neutronSupplyTokens, pegFactor, denominator);
if (liabilities >= normalizedReserve) return 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There must be a bug here too.

Comment thread src/StableCoin.sol

return Math.mulDiv(equity, WAD, protonSupplyTokens, rounding);
return Math.mulDiv(normalizedReserve - liabilities, WAD, protonSupplyTokens, rounding);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion to solve these bugs cleanly:

  • create internal functions that calculate the liabilities and the equity, like the Djed contract does.

And a general advice:

  • whenever your code has if statements with special return statements for corner cases, the probability of bugs is high.

@DengreSarthak
DengreSarthak merged commit d38ad81 into main Oct 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants