Skip to content

contracts: add deterministic rounding policy for share conversions#661

Open
Amas-01 wants to merge 1 commit into
Junirezz:mainfrom
Amas-01:contracts-add-deterministic-rounding-policy
Open

contracts: add deterministic rounding policy for share conversions#661
Amas-01 wants to merge 1 commit into
Junirezz:mainfrom
Amas-01:contracts-add-deterministic-rounding-policy

Conversation

@Amas-01
Copy link
Copy Markdown

@Amas-01 Amas-01 commented May 30, 2026

Closes #563

PR:Deterministic Rounding Policy for Mint/Burn Share Conversions

Overview

This document summarizes the implementation of Issue #563, which adds a deterministic rounding policy for all mint and burn share conversions in the YieldVault-RWA contract.

Changes Made

1. New Math Module (src/math.rs)

Created a centralized math module that implements the deterministic rounding policy:

Functions:

  • `assets_to_shares(assets, total_shares, total_afeat(frontend): persist latest wasm analysis result in local storage

ssets) -> i128`

  • shares_to_assets(shares, total_shares, total_assets) -> i128

Rounding Policy:

  • Both functions use round-down (truncation) via integer division
  • Bootstrap case: First deposit gets 1:1 ratio
  • All operations use checked arithmetic to prevent overflow

Test Coverage:

  • 20 unit tests covering:
    • Bootstrap/zero-state behavior
    • Rounding direction verification
    • Round-trip consistency
    • Monotonicity properties
    • Yield accrual effects
    • Edge cases (tiny amounts, overflow protection)

2. Updated Contract Functions (src/lib.rs)

Modified Functions:

  • calculate_shares() - Now delegates to math::assets_to_shares()
  • calculate_assets() - Now delegates to math::shares_to_assets()
  • deposit() - Uses centralized conversion, added rounding documentation
  • withdraw() - Uses centralized conversion, added rounding documentation
  • execute_withdrawal() - Uses centralized conversion, added rounding documentation

Benefits:

  • Eliminated duplicate conversion logic
  • Consistent rounding behavior across all paths
  • Clear documentation of rounding policy
  • Single source of truth for conversion math

3. Updated Fuzz Tests (src/fuzz_math.rs)

Updated helper functions to reference the centralized math module:

  • shares_for() - Updated documentation
  • assets_for() - Updated documentation

All existing property-based tests continue to pass (10,000+ iterations).

4. Documentation

Created comprehensive documentation:

ROUNDING_POLICY.md - Complete policy documentation including:

  • Policy statement and rationale
  • Implementation details and formulas
  • User impact analysis
  • Safety guarantees
  • Testing coverage
  • Integration guide for developers
  • Edge case handling
  • Comparison with ERC-4626 standard

Rounding Policy Summary

Policy: Always round DOWN (truncate) for both minting and burning.

Rationale:

  1. Prevents Over-Minting: Users never receive more shares than entitled
  2. Prevents Over-Withdrawal: Users never withdraw more assets than entitled
  3. Maintains Solvency: Vault always has sufficient assets for all claims
  4. Prevents Value Extraction: Round-trip operations never increase value
  5. Deterministic: Same inputs always produce same outputs across all platforms

Formulas:

shares = (assets × total_shares) / total_assets  [rounds down]
assets = (shares × total_assets) / total_shares  [rounds down]

Test Results

All tests pass successfully:

test result: ok. 119 passed; 0 failed; 0 ignored; 0 measured

Test Breakdown:

  • 20 new unit tests in math.rs
  • 89 existing integration tests (all passing)
  • 10 property-based fuzz tests (10,000+ iterations each)

Key Test Coverage:

  • ✅ Rounding direction verification
  • ✅ Round-trip consistency (no value extraction)
  • ✅ Monotonicity (more in → more out)
  • ✅ Overflow protection
  • ✅ Edge cases (zero supply, tiny amounts)
  • ✅ Yield accrual effects
  • ✅ Multi-user deposit/withdrawal sequences
  • ✅ Fee handling with rounding

Safety Guarantees

The implementation ensures:

  1. No Over-Minting: shares_minted ≤ exact_fractional_shares
  2. No Over-Withdrawal: assets_returned ≤ exact_fractional_assets
  3. Solvency Invariant: total_assets ≥ sum(all_user_redemption_values)
  4. No Value Extraction: withdraw(deposit(x)) ≤ x for all x
  5. Monotonicity: More assets → more shares, more shares → more assets
  6. Determinism: Platform-independent, reproducible results

User Impact

Rounding Loss:

  • Maximum loss per operation: < 1 unit of the result
  • Typical loss: negligible fraction of total value
  • Example: Depositing 100.9 worth of assets yields 100 shares (loss of 0.9)

Protection Against Zero Shares:

  • Contract rejects deposits that would mint zero shares
  • Returns VaultError::InvalidAmount
  • Prevents silent loss of user funds

Tiny Withdrawals:

  • May round to zero assets (withdrawal succeeds but returns 0)
  • Users should avoid withdrawing dust amounts

Compliance with Standards

The implementation aligns with ERC-4626 recommendations:

"ERC-4626 Vault implementers should be aware of the need for specific, opposing rounding directions across the different mutable and view methods, as it is considered most secure to favor the Vault itself during calculations over its users."

Our implementation:

  • ✅ Minting: Round down (favors vault)
  • ✅ Burning: Round down (favors vault)
  • ✅ View functions: Round down (consistent with mutable functions)

Files Changed

  1. New Files:

    • contracts/vault/src/math.rs - Centralized math module
    • contracts/vault/ROUNDING_POLICY.md - Policy documentation
    • contracts/vault/ISSUE_563_IMPLEMENTATION.md - This file
  2. Modified Files:

    • contracts/vault/src/lib.rs - Updated conversion functions
    • contracts/vault/src/fuzz_math.rs - Updated helper documentation

Verification Steps

To verify the implementation:

# Run all tests
cd contracts/vault
cargo test --lib


# Run only math module tests
cargo test --lib math


# Run property-based tests
cargo test --lib fuzz


# Build the contract
cargo build --target wasm32-unknown-unknown --release

Integration Checklist

For integrators and frontend developers:

  • Update UI to show projected shares before deposit
  • Warn users if projected shares would be zero
  • Display rounding loss information (optional)
  • Handle VaultError::InvalidAmount for zero-share deposits
  • Update documentation to reference ROUNDING_POLICY.md
  • Test with tiny deposit amounts
  • Test with high share prices (after yield accrual)

Future Considerations

Potential Enhancements:

  1. Add minimum share amount parameter (reject deposits below threshold)
  2. Add rounding loss estimation to view functions
  3. Add events for rounding loss tracking
  4. Consider share price normalization strategies

Not Recommended:

  • Changing rounding direction (would break safety guarantees)
  • Using floating-point arithmetic (non-deterministic)
  • Implementing banker's rounding (adds complexity, attack surface)

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented May 30, 2026

@Amas-01 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

Contracts: Add deterministic rounding policy for mint burn share conversions

1 participant