feat(investment_vault): ERC-4626-style read views — share_price, preview_deposit, preview_withdraw, max_withdraw, max_deposit (#617) - #646
Merged
abayomicornelius merged 3 commits intoSep 26, 2026
Conversation
…_*/max_* (Heliobond#617) Add pure views that account for everything the real calls do: - share_price(): NAV per whole share, scaled by 10^7 (1:1 when empty). - preview_deposit(usdc): shares after insurance premium + management / volume-tier fee, with deposit's validation and supply-cap checks. - preview_withdraw(shares): (usdc_now, usdc_queued), applying the graduated utilization limit and the liquid-vs-queue split. - max_withdraw(account): balance value capped by tier limit and max-tx cap; 0 while paused, deposit-locked, or below the minimum. - max_deposit(account): MAX_DEPOSIT capped by max-tx and remaining HBS supply headroom; 0 while paused. None of them write storage: NAV is read via a new read_total_assets() helper (total_assets() now wraps it and still caches). deposit/withdraw are refactored onto the same deposit_breakdown / validate_deposit_amount / withdraw_tier_limit helpers so previews can't drift from the real calls.
…udgets (Heliobond#617) - preview_deposit == deposit (with a management fee configured). - share_price is 1:1 when empty and tracks total_assets / total_supply. - max_withdraw is 0 during the deposit lock; afterwards preview_withdraw and max_withdraw equal what withdraw actually pays. - max_deposit / max_withdraw are 0 while paused. - Cost-estimate test emits gas_budget lines for share_price, preview_deposit and max_withdraw, with matching gas-budgets.json entries.
|
@boluwacodes 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! 🚀 |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
The vault only exposes
convert_to_shares/convert_to_assets. Those ignore:The frontend even calls a nonexistent
share_priceview, so clients re-implement contract logic off-chain and get it wrong.New views
share_price()i128, USDC per whole share ×10^7preview_deposit(usdc)deposit: amount bounds, max-tx cap, supply cap, paused.preview_withdraw(shares)(usdc_now, usdc_queued)withdraw's amount errors.max_withdraw(account)MIN_WITHDRAW.max_deposit(account)MAX_DEPOSIT, the max-tx cap and the remaining HBS supply headroom (#20). It is 0 while paused. Funding rounds only block share transfers (#38), so they don't affect deposits.No storage writes
total_assets()writesCachedTotalAssetson every call. The views instead read NAV through a newread_total_assets()helper that has no side effects.total_assets()now wraps that helper and still caches, so its behaviour is unchanged.Previews can't drift from the real calls
depositandwithdrawnow use the same helpers as the views:validate_deposit_amount;deposit_breakdown, which returns premium, fee and investable amount;withdraw_tier_limit.The logic moved without changing, so the fee and limit calculations are defined in exactly one place.
Tests
preview_depositequalsdeposit, with a management fee configured.share_priceis 1:1 when empty and trackstotal_assets / total_supply.max_withdrawis 0 during the deposit lock. After the lock,preview_withdrawandmax_withdrawboth equal whatwithdrawactually pays.max_depositandmax_withdraware 0 while paused.gas_budgetlines forshare_price,preview_depositandmax_withdraw.Docs and gas
INTERFACE.mdhas rows for all five views, and thetotal_assets()row now says it writes the cache.gas-budgets.jsonhas entries for the three measured views, set to deposit's budget as an upper bound because they do the same NAV reads without writes.Commits
feat(investment_vault): ERC-4626-style read views share_price/preview_*/max_* (#617)test(investment_vault): views equal their state-changing calls; gas budgets (#617)docs(interface): document ERC-4626-style vault views (#617)Verification status: not run
maindoes not compile, independently of this PR.investment_vault/src/storage.rs:37has a garbled test attribute (#c[cfg)test]) and an unclosedassert_eq!(on line 48, socargo build -p investment-vaultfails before reaching this code. I left that out of this PR to keep its scope to #617; it should get its own fix. Once it is fixed, run:Closes #617
Closes #618
Closes #619
Closes #620
scripts/check_interface_docs.pyalready reports undocumented functions onmain: funding-round and volume-tier setters, and batch getters. None of the #617 views are in that list.