fix(project_registry): reject delete_project while the vault holds investments - #650
Merged
abayomicornelius merged 3 commits intoSep 27, 2026
Conversation
InvalidStatusTransition/ProjectStatusUnchanged (Heliobond#329) and CompactStorageTooLarge/VotingPeriodTooLong (Heliobond#332) both claimed 41/42, so the crate failed to compile (E0081). Move the latter pair to 43/44. Refs Heliobond#526
#[cfg(test)] was garbled as #c[cfg)test] and the assert_eq! was missing its closing paren. Because rustc parses cfg(test) items too, this broke compilation of the vault library itself (and so project_registry's tests, which depend on it). Also fixes the test body: testutils imports, a contract context for storage access, and set_sequence_number. Refs Heliobond#526
…vestments delete_project was a placeholder that deleted unconditionally. It now queries get_project_investment(project_id) on the vault set via the new owner-only set_vault, panicking with ProjectHasInvestments (30) when non-zero, and fails closed with the new VaultNotConfigured (45) when no vault is set. The vault interface is declared with #[contractclient] to avoid a crate cycle (the vault imports the registry WASM). Adds get_vault, updates INTERFACE.md, and 9 tests including one against the real InvestmentVault to pin ABI compatibility. Closes Heliobond#526
|
@floraispretty 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
delete_projectwas still the #26 placeholder: it deleted unconditionally, with only aNOTE:comment where the vault check belonged.INTERFACE.mdmeanwhile claimed it "Rejects deletion when the project has active investments", andRegistryError::ProjectHasInvestments(30) was defined but never used. #325 was closed without a fix landing, as the issue documents.This PR implements option 1 from the issue: a real cross-contract check against the vault.
The fix (commit 3)
set_vault(vault)(owner-only) andget_vault()are new. The registry never knew the vault's address; only the vault knew the registry.#[contractclient](get_project_investment(project_id) -> i128) instead of depending on theinvestment-vaultcrate. The vault alreadycontractimport!s the registry's WASM, so a crate dependency would be circular.VaultNotConfigured = 45instead of silently skipping the check. That makes the guarantee inINTERFACE.mdtrue unconditionally.ProjectHasInvestments(30) is finally used.INTERFACE.md: thedelete_projectrow now describes the actual behavior, and rows were added forset_vault/get_vault.scripts/check_interface_docs.pyno longer reports these functions. It still reports existing, unrelated drift for other functions onmain.set_vault(<vault address>)once, ordelete_projectwill (intentionally) reject every call withVaultNotConfigured.Tests (commit 3): 9 new
A small in-test
MockVaultexposesget_project_investmentwith a setter, so the tests control the reported investment exactly:test_get_vault_is_none_until_setNoneby default, then round-tripstest_set_vault_is_owner_onlytest_delete_project_fails_closed_without_vaultVaultNotConfigured, project untouchedtest_delete_project_rejects_active_investmentsProjectHasInvestments, project untouchedtest_delete_project_rejects_any_nonzero_investment1stroop blocks deletiontest_delete_project_succeeds_with_no_investmentProjectNotFoundtest_delete_project_allowed_once_investment_is_repaidtest_delete_project_checks_only_the_target_projecttest_delete_project_against_real_investment_vaultInvestmentVault, which pins that the#[contractclient]interface matches its ABITwo pre-existing build breaks fixed so this could be tested (commits 1–2)
project_registrydid not compile onmain, so no test could run without these:RegistryErrordiscriminants (E0081).InvalidStatusTransition/ProjectStatusUnchanged(ProjectStatus::Active/Funded/Completed are unreachable — status never advances past Pending #329) andCompactStorageTooLarge/VotingPeriodTooLong(compact_storage and create_proposal accept unbounded caller-supplied sizes with no resource-limit guard #332) both used 41/42, from two same-day merges. I moved the latter pair to 43/44. Since the crate couldn't compile with either numbering, no deployed build can depend on these values, and nothing in the repo references them numerically.investment_vault/src/storage.rstest module.#[cfg(test)]was garbled as#c[cfg)test], and anassert_eq!was missing its). rustc parsescfg(test)items too, so this broke the vault library, whichproject_registry's tests depend on. I also fixed the test body: testutils imports, running storage calls inside a contract context, andset_sequence_number.Verification
cargo test -p project-registry: 132 passed, 2 failed. The 2 failures (test_full_heliobond_flowwithError(Contract, #36), andtest_score_history_multiple_updates_ordered, a timestamp assertion) fail the same way before this PR (baseline: 123 passed, 2 failed). All 9 new tests pass; no regressions.rustfmt-clean. I didn't reformat existing unformatted code, to keep the diff focused.time-core 0.1.9), so I ran checks with Rust 1.94. The registry WASM the vault imports was built withSOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1in place ofstellar contract build. Regeneratedtest_snapshots/files were not committed.Still broken on
main(not addressed here)investment_vault/src/test.rshas a syntax error around line 354–361 (an unmatched}), socargo test -p investment-vaultcan't compile. It doesn't affect the vault library or the registry tests, and is out of scope for #526.Closes #526
Closes #527
Closes #528
Closes #529