Skip to content

Add missing require_current_state guards - #466

Merged
abayomicornelius merged 2 commits into
Heliobond:mainfrom
abayomiwav:fix/require-current-state-guard-gaps
Aug 24, 2026
Merged

Add missing require_current_state guards#466
abayomicornelius merged 2 commits into
Heliobond:mainfrom
abayomiwav:fix/require-current-state-guard-gaps

Conversation

@abayomiwav

@abayomiwav abayomiwav commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Problem

require_current_state is the guard that rejects a call made against a stale storage-layout version after a contract upgrade — it's already enforced at the top of the large majority of state-mutating functions across both contracts (30+ call sites in investment_vault alone). Six functions across the two contracts were silently missing it, despite being exactly the kind of state-mutating, capital-sensitive operations the guard exists to protect. In every case, a sibling function performing the same class of operation already has the guard, which is how each gap was found — by diffing each function against its nearest neighbor.

What's fixed

investment_vault:

  • set_wormhole_core — configures who is trusted to authorize minting HBS via the Wormhole bridge path. Its sibling set_bridge already had the guard; this one didn't.
  • fund_project_internal — the vault's primary capital-deployment path, reached via fund_project, fund_project_with_approvals, and batch_fund_projects. None of those three public entry points checked this guard anywhere in their call chain before reaching this function.
  • receive_yield_internal — records yield receipts and updates investor accounting.
  • claim_insurance_internal — pays out insurance claims against a project's collateral.
  • calculate_carbon_credits — its two siblings in the same feature, issue_carbon_credits and transfer_carbon_credits, both already checked this guard; this one didn't.

project_registry:

  • certify_project — calls require_not_paused but never require_current_state, unlike every other function that mutates project state (create_project, archive_project, update_impact_score, deposit_collateral, release_collateral, set_creator_reputation).

Why it matters

Without the guard, each of these six functions remains callable — and able to mutate storage or move funds — even after an upgrade has changed the storage layout in a way every other function in the same contract is already protected against. fund_project_internal in particular is the highest-value gap: it's the vault's single largest capital-deployment path, and none of its three public wrappers filled in for the missing check.

Fix approach

Minimal and targeted — each function gets exactly the one-line guard call its siblings already have, added at the top before any other logic runs. No behavior changes beyond closing this gap; no new tests added since this mirrors an existing, already-tested guard pattern used identically elsewhere in both files.

Commits

  1. investment_vault: add missing require_current_state guards — the five investment_vault functions.
  2. project_registry: add missing require_current_state guard to certify_project — the one project_registry function.

Closes #460
Closes #461
Closes #462
Closes #463

@drips-wave

drips-wave Bot commented Aug 24, 2026

Copy link
Copy Markdown

@abayomiwav 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

require_current_state is enforced at the top of the large majority of
investment_vault's public and internal functions (30+ call sites), as
the safety net that rejects calls made against a stale storage-layout
version after a contract upgrade. Five functions in this file were
silently missing it despite being exactly the kind of state-mutating,
capital-sensitive operations the guard exists to protect:

- set_wormhole_core — configures who is trusted to authorize minting
  HBS via the Wormhole bridge path; its sibling set_bridge already had
  the guard, this one didn't.
- fund_project_internal — the vault's primary capital-deployment path,
  reached via fund_project, fund_project_with_approvals, and
  batch_fund_projects. None of those three entry points checked this
  guard anywhere in their call chain before reaching this function.
- receive_yield_internal — records yield receipts and updates investor
  accounting.
- claim_insurance_internal — pays out insurance claims against a
  project's collateral.
- calculate_carbon_credits — its two siblings in the same feature,
  issue_carbon_credits and transfer_carbon_credits, both already
  checked this guard; this one didn't.

Without the guard, all five remain callable (and able to mutate
storage or move funds) even after an upgrade has changed the storage
layout in a way every other function in the file is protected against.
…project

certify_project calls require_not_paused but never
require_current_state, unlike every one of its neighbors that mutates
project state — create_project, archive_project, update_impact_score,
deposit_collateral, release_collateral, and set_creator_reputation all
check both. Without the guard, certify_project stays callable and able
to mutate Project(id).certification_status even against a stale
storage layout after an upgrade, the same class of gap just fixed for
investment_vault in the previous commit.
@abayomiwav
abayomiwav force-pushed the fix/require-current-state-guard-gaps branch from 9d08283 to 646e9a7 Compare August 24, 2026 19:39
@abayomicornelius
abayomicornelius merged commit f932c31 into Heliobond:main Aug 24, 2026
1 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment