feat(vault): sweep_idle_balance dry-run view#588
Open
Osuolale1 wants to merge 2 commits into
Open
Conversation
Recent "-X theirs" auto-resolutions in PRs CalloraOrg#578/CalloraOrg#579 left contracts/vault/src/lib.rs in a non-compiling state with 107 errors. This commit reconstructs the intended state: - remove duplicate get_max_deduct (was defined at L376 and L1305; kept the documented copy at L376) - remove panic-version broadcast (kept the Result-returning version that matches the surrounding contract style and reuses VaultError::MetadataTooLong) - replace inline VaultError enum with the canonical one in errors.rs via `mod errors; pub use errors::VaultError;` (the inline copy was missing the contracterror macro import and was diverging from errors.rs) - declare `mod validators;` so validators.rs is wired into the crate - fix two StorageKey::Meta typos (variant is MetaKey) - add the missing `&ut` (USDC token) argument to two SettlementClient::receive_payment calls (the settlement trait takes 5 args, the call sites were passing 4) - change `max_fee_bps: u16` -> `u32` in `deduct` (Soroban contract ABI does not support u16; updated the u16::MAX sentinel to u32::MAX accordingly) Verified with `cargo check -p callora-vault --lib`.
Add a read-only view that reports how much untracked on-ledger USDC a hypothetical sweep would move, without mutating any state or moving any funds. Useful for indexers, dashboards, and admins inspecting recovery candidates before signing a `distribute` transaction. `dry_run_sweep_idle_balance() -> Result<SweepPreview, VaultError>` returns a structured payload containing: - on_ledger_balance: actual USDC held by the vault contract - tracked_balance: the vault's internal accounting (meta.balance) - idle_balance: max(on_ledger - tracked, 0) -- saturates defensively - has_idle: true iff idle_balance > 0 No auth. No TTL bump. Single cross-contract balance() read. Tests cover: pre-init error path, zero-surplus path, positive-surplus path, cumulative surplus across multiple direct transfers, defensive saturation when tracked > on_ledger, observable-state preservation, and no-auth invocation.
|
@Osuolale1 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.
CLOSE #537
Summary
The vault occasionally accumulates untracked on-ledger USDC surplus (direct transfers that bypass deposit, rounding remnants, manually credited recovery funds). The admin can recover it via distribute() with an admin-specified amount, but until now there was no read-only way to ask the contract what that amount currently is. Indexers and admin tooling had to query the token contract and read meta.balance separately and compute the difference off-chain.
This PR adds dry_run_sweep_idle_balance(), a read-only view that returns a structured SweepPreview describing exactly what a sweep would move.
Changes
contracts/vault/src/views.rs (new) — defines SweepPreview { on_ledger_balance, tracked_balance, idle_balance, has_idle } as a #[contracttype], and implements dry_run_sweep_idle_balance(env) -> Result<SweepPreview, VaultError> inside a #[contractimpl] impl CalloraVault block.
contracts/vault/src/lib.rs — adds mod views; and re-exports SweepPreview.
contracts/vault/tests/sweep_dryrun.rs (new) — 7 integration tests:
README.md — added the view to the vault API listing.
Note on semantics
The function reports max(on_ledger - tracked, 0). It does NOT pick a destination address — there is no canonical sweep recipient in the current contract, and distribute() takes the destination as a parameter. Callers of this view supply the destination themselves when they subsequently call distribute(_, _, preview.idle_balance).
Note on test-environment caveat
The saturation test (tracked > on_ledger) is constructed by transferring USDC out of the vault under mock_all_auths, simulating the pathological case where on-ledger drops below tracked. Clawback was the more natural primitive but requires asset-level clawback flags; transfer is sufficient to exercise the defensive branch.
Acceptance criteria
dry_run_sweep_idle_balance() emits no events, requires no auth, mutates no storage
SweepPreview payload accurately reports the four documented fields under both surplus and non-surplus states
Behaviour saturates safely under the tracked > on_ledger pathological case
All 7 tests pass: cargo test -p callora-vault --test sweep_dryrun
README.md updated; NatSpec-style /// rustdoc on the view, the struct, and every field
No breaking ABI change (additive: new view, new exported type)
Test plan
cargo test -p callora-vault --test sweep_dryrun → 7 passed
CI green (note: settlement crate currently has its own pre-existing merge breakage from prior -X theirs merges; if CI is red for that reason it is not a regression from this PR)
Stacked alongside #<set-admin-PR-#> and #<upgrade-events-PR-#>
This PR's diff temporarily includes the lib.rs cleanup commit from those predecessors (cherry-picked from #<set-admin-PR-#>). Once either predecessor merges into main, this PR's diff narrows automatically to just the four new sweep-dryrun files.