Skip to content

fix(FLOW-7): Enforce sentinel hygiene for NO_YIELDVAULT_ID#38

Merged
liobrasil merged 3 commits intomainfrom
fix/FLOW-7-sentinel-hygiene
Jan 29, 2026
Merged

fix(FLOW-7): Enforce sentinel hygiene for NO_YIELDVAULT_ID#38
liobrasil merged 3 commits intomainfrom
fix/FLOW-7-sentinel-hygiene

Conversation

@liobrasil
Copy link
Collaborator

Summary

Fixes #22

Prevents corruption of "no yieldvault" semantics by properly handling the NO_YIELDVAULT_ID sentinel value.

Changes:

  1. Added guard in _registerYieldVault() to reject NO_YIELDVAULT_ID
  2. Changed createYieldVault() to use NO_YIELDVAULT_ID as placeholder instead of 0 (which can be a valid Cadence yieldVaultId)
  3. Added CannotRegisterSentinelYieldVaultId custom error
  4. Updated documentation to reflect new behavior

Test plan

  • All 70 Solidity tests pass
  • Existing tests continue to work with new sentinel usage

🤖 Generated with Claude Code

liobrasil and others added 2 commits January 19, 2026 18:38
Prevents corruption of "no yieldvault" semantics:

1. Added guard in _registerYieldVault() to reject NO_YIELDVAULT_ID
2. Changed createYieldVault() to use NO_YIELDVAULT_ID as placeholder
   instead of 0 (which can be a valid Cadence yieldVaultId)
3. Added CannotRegisterSentinelYieldVaultId custom error
4. Updated documentation to reflect new behavior

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link
Collaborator

@m-Peter m-Peter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@claude
Copy link

claude bot commented Jan 28, 2026

Code Review: Enforce sentinel hygiene for NO_YIELDVAULT_ID

Summary

This PR addresses issue #22 by properly handling the NO_YIELDVAULT_ID sentinel value. The changes prevent semantic corruption by ensuring the sentinel cannot be registered as a valid vault ID and using it consistently as a placeholder for CREATE requests.


Positive Aspects ✅

  1. Security Enhancement: The guard in _registerYieldVault() at solidity/src/FlowYieldVaultsRequests.sol:1552 properly prevents the sentinel value from being registered, eliminating potential edge case bugs.

  2. Semantic Consistency: Using NO_YIELDVAULT_ID instead of 0 as placeholder is more explicit and aligns with the sentinel's intended purpose, since 0 can be a valid yieldVaultId per the contract comments.

  3. Comprehensive Documentation: All relevant documentation files (FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md, FRONTEND_INTEGRATION.md) have been updated consistently.

  4. Good Test Coverage: The new test test_CreateYieldVault_UsesSentinelYieldVaultIdPlaceholder verifies both the placeholder behavior and the rejection of sentinel registration.


Issues & Recommendations 🔍

1. Inconsistent Test Data (Minor) ⚠️

Location: solidity/test/FlowYieldVaultsRequests.t.sol:219, 252, 270, 350, 1289

Issue: Multiple existing tests use 0 when calling completeProcessing() for failed CREATE requests:

c.completeProcessing{value: 1 ether}(reqId, false, 0, "Failed");

Recommendation: For semantic consistency, these should use c.NO_YIELDVAULT_ID() instead of 0. While 0 works functionally (since failed creates don't register the vault), using the sentinel is more explicit about intent and consistent with the new placeholder behavior.

Example fix:

uint64 sentinelId = c.NO_YIELDVAULT_ID();
c.completeProcessing{value: 1 ether}(reqId, false, sentinelId, "Failed");

2. Potential Edge Case Testing Gap (Low Priority) 💭

New Test Coverage: The new test at solidity/test/FlowYieldVaultsRequests.t.sol:69-93 verifies the error when COA tries to register the sentinel via successful completeProcessing.

Additional Test Scenario: Consider adding a test that verifies what happens if a valid yieldVaultId == 0 from Cadence is properly registered (confirming that 0 is indeed valid and only NO_YIELDVAULT_ID is blocked):

function test_CreateYieldVault_CanRegisterZeroId() public {
    vm.prank(user);
    uint256 reqId = c.createYieldVault{value: 1 ether}(NATIVE_FLOW, 1 ether, VAULT_ID, STRATEGY_ID);
    
    vm.startPrank(coa);
    c.startProcessing(reqId);
    c.completeProcessing(reqId, true, 0, "YieldVault 0 created");
    vm.stopPrank();
    
    assertTrue(c.isValidYieldVaultId(0), "YieldVault ID 0 should be valid");
    assertEq(c.getYieldVaultOwner(0), user);
}

This would document and verify the design intention that 0 is indeed a valid ID.


3. Cadence Test Files May Need Updates (Review Needed) ⚠️

Location: Based on grep results, Cadence test files still use yieldVaultId: 0 for CREATE requests:

  • cadence/tests/evm_bridge_lifecycle_test.cdc:53, 164, 180, 200, 214, 271
  • cadence/tests/error_handling_test.cdc:61

Recommendation: Review whether these test expectations need to be updated to expect NO_YIELDVAULT_ID (which is UInt64.max) instead of 0 for CREATE request placeholders. The Cadence contract at cadence/contracts/FlowYieldVaultsEVM.cdc:106-110 already defines noYieldVaultId as UInt64.max.


Security Considerations 🔒

  1. No Security Vulnerabilities: The guard prevents an edge case where the sentinel could corrupt the ownership mappings.

  2. Reentrancy Protected: Existing nonReentrant modifier on completeProcessing remains in place.

  3. Access Control: The new error can only be triggered by the authorized COA during completeProcessing, which is properly protected by onlyAuthorizedCOA.


Performance Considerations ⚡

Impact: Negligible - adds one equality check in _registerYieldVault() which only runs on successful CREATE operations. The gas cost increase is minimal (~3 gas for the comparison).


Best Practices Assessment ✨

  1. Custom Error: Uses custom error CannotRegisterSentinelYieldVaultId instead of string revert - good for gas efficiency.

  2. Documentation: Comments and natspec updated consistently across the contract.

  3. Breaking Changes: None - this is a defensive fix that prevents undefined behavior. Existing valid yieldVaultIds (including 0) remain unaffected.


Verdict

Approve with minor suggestions. The core changes are solid and address the identified issue effectively. The recommendations above are minor improvements for consistency and completeness, not blockers. The PR can be merged as-is, with the test consistency updates applied in a follow-up if desired.


Test Status: ✅ Per PR description, all 70 Solidity tests pass.

@claude
Copy link

claude bot commented Jan 29, 2026

Code Review: Enforce sentinel hygiene for NO_YIELDVAULT_ID

This PR successfully addresses issue #22 by preventing corruption of the NO_YIELDVAULT_ID sentinel value semantics. The implementation is clean and well-tested.


✅ Strengths

1. Correct Sentinel Value Usage
The change from using 0 to NO_YIELDVAULT_ID as the placeholder for CREATE requests (solidity/src/FlowYieldVaultsRequests.sol:798) is correct and aligns with the documented design. Since 0 can be a valid Cadence yieldVaultId, using the sentinel value removes ambiguity.

2. Robust Guard Implementation
The guard in _registerYieldVault() (solidity/src/FlowYieldVaultsRequests.sol:1552) properly prevents the sentinel value from being registered:

if (yieldVaultId == NO_YIELDVAULT_ID) revert CannotRegisterSentinelYieldVaultId();

This is placed at the beginning of the function, preventing any state changes if the sentinel is passed.

3. Comprehensive Test Coverage
Two new tests provide excellent coverage:

  • test_CreateYieldVault_UsesSentinelYieldVaultIdPlaceholder() - Validates that CREATE requests use the sentinel and that attempting to register it causes a revert
  • test_CreateYieldVault_CanRegisterZeroYieldVaultId() - Confirms that 0 is now a valid yieldVaultId (the original issue)

4. Documentation Updates
All relevant documentation and comments have been updated consistently across:

  • Contract NatSpec comments
  • Design documentation (FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md)
  • Frontend integration guide (FRONTEND_INTEGRATION.md)
  • Cadence test files

🔍 Observations

1. Consistent Sentinel Usage in Tests
The test updates properly replace hardcoded 0 values with c.NO_YIELDVAULT_ID() or FlowYieldVaultsEVM.noYieldVaultId where appropriate. This improves code clarity and prevents future confusion.

2. Backward Compatibility
Since this is fixing a bug before production deployment, there are no backward compatibility concerns. The change is breaking in a good way - it prevents invalid states.

3. Cross-VM Consistency
The Cadence contract already uses noYieldVaultId (UInt64.max) consistently, and this PR brings the Solidity side into alignment, ensuring both sides of the bridge have matching semantics.


💡 Minor Suggestions

1. Consider Adding Negative Test for Deposits/Withdrawals (Optional)
While the current tests cover CREATE requests well, consider adding tests that verify other request types cannot somehow end up with NO_YIELDVAULT_ID as their yieldVaultId. This would provide defense-in-depth, though the current code paths don't allow this scenario.

2. Gas Optimization Note (Informational)
The guard check adds a minimal gas cost (~100 gas for the comparison). This is negligible and the safety benefit far outweighs the cost.


🔒 Security Assessment

No security concerns identified. The changes improve the contract's security posture by:

  • Preventing invalid state where the sentinel value could be registered as a valid vault
  • Removing ambiguity between "no vault yet" (CREATE in progress) and "vault ID 0" (valid vault)
  • Maintaining clear separation between sentinel values and valid data

The guard ensures that even if the Cadence worker malfunctions and returns NO_YIELDVAULT_ID in a completion call, the contract will revert rather than entering an invalid state.


📊 Test Results

Per the PR description:

  • ✅ All 70 Solidity tests pass
  • ✅ Existing tests continue to work with new sentinel usage
  • ✅ Cadence tests updated to use noYieldVaultId consistently

✅ Recommendation

APPROVE - This PR is ready to merge. It:

  • Correctly fixes the reported issue
  • Maintains consistency across EVM and Cadence contracts
  • Includes comprehensive tests
  • Updates all relevant documentation
  • Introduces no breaking changes (since this is pre-production)
  • Improves code clarity and safety

The implementation is clean, well-documented, and properly tested. Nice work! 🎉

@liobrasil liobrasil merged commit 2426426 into main Jan 29, 2026
4 checks passed
@liobrasil liobrasil deleted the fix/FLOW-7-sentinel-hygiene branch January 29, 2026 01:20
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.

FLOW-7: Sentinel Hygiene: Reserved NO_YIELDVAULT_ID and Create Placeholder Id

2 participants