Skip to content

B36 - #206

Closed
matteoettam09 wants to merge 9 commits into
mainfrom
b36
Closed

B36#206
matteoettam09 wants to merge 9 commits into
mainfrom
b36

Conversation

@matteoettam09

@matteoettam09 matteoettam09 commented Apr 23, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added redemption transfer failure recovery mechanism allowing users to claim underlying funds if initial transfer fails due to token restrictions.
  • Bug Fixes

    • Improved handling of failed asset removals during vault decommissioning to retry in subsequent epochs.
    • Enhanced Chainlink price feed validation logic.
  • Security

    • Strengthened authorization checks for critical configuration updates.
    • Added system state validation gates for sensitive operations.
    • Expanded vault-caller validation to accept both active and decommissioned vaults.

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces system idle state gating for critical operations, implements conditional asset decommissioning with failed token retry logic, adds deferred redemption claiming for transfer-blocked redeemers, and removes stale round validation from price feed checks.

Changes

Cohort / File(s) Summary
System Idle Check Infrastructure
contracts/test/MockOrionConfig.sol
Adds configurable _systemIdle flag with isSystemIdle() getter and setSystemIdle() setter for testing system idle state enforcement.
System Idle Authorization Gating
contracts/LiquidityOrchestrator.sol, contracts/execution/UniswapV3ExecutionAdapter.sol, contracts/vaults/OrionTransparentVault.sol
Enforces isSystemIdle() checks before state mutations: updateVKey and setTargetBufferRatio require idle state, submitIntent requires idle state before processing, and setAssetFee requires idle state before pool configuration.
Conditional Asset Decommissioning
contracts/LiquidityOrchestrator.sol, contracts/OrionConfig.sol, contracts/interfaces/IOrionConfig.sol
Implements failed token retry mechanism: completeAssetsRemoval() now accepts failedTokens list and conditionally retains assets not sold in current epoch; upkeep finalization passes failed tokens, decommission finalization verifies asset liquidation (tokens.length == 0) and queue drainage before completion.
Vault Caller & Redemption Transfer Validation
contracts/LiquidityOrchestrator.sol
Broadens vault validation in returnDepositFunds and transferRedemptionFunds to accept both active vaults (isOrionVault) and decommissioned vaults (isDecommissionedVault), reverting with NotAuthorized() otherwise.
Redemption Transfer Failure & Recovery Flow
contracts/vaults/OrionVault.sol, contracts/interfaces/IOrionVault.sol
Adds deferred redemption claiming: fulfillRedeem catches transfer failures and records pending claims in pendingUnderlyingClaims mapping with RedemptionTransferFailed event; new claimUnderlying() method (nonReentrant) retries transfer and emits RedemptionClaimed. Introduces pendingUnderlyingClaims(address) view to query claimable amounts.
Price Feed Stale Round Detection Removal
contracts/price/ChainlinkPriceAdapter.sol
Removes roundId/answeredInRound staleness validation logic while retaining bounds, positivity, timestamp, and max staleness checks based on updatedAt.
Interface & Parameter Updates
contracts/interfaces/IOrionConfig.sol
Updates completeAssetsRemoval() signature to accept address[] calldata failedTokens and renames setProtocolRiskFreeRate parameter from riskFreeRate to newRiskFreeRate with NatSpec documentation.

Sequence Diagrams

sequenceDiagram
    participant User
    participant Vault as OrionVault
    participant LO as LiquidityOrchestrator
    
    User->>Vault: fulfillRedeem (batch)
    Vault->>LO: transferRedemptionFunds (per user)
    alt Transfer Succeeds
        LO-->>Vault: Success
        Vault->>Vault: Emit Redeem
    else Transfer Fails (denylist, etc.)
        LO-->>Vault: Revert
        Vault->>Vault: pendingUnderlyingClaims[user] += amount
        Vault->>Vault: Emit RedemptionTransferFailed
    end
    
    Note over Vault: Later: transfer blocker resolved
    User->>Vault: claimUnderlying()
    Vault->>Vault: amount = pendingUnderlyingClaims[caller]
    Vault->>Vault: pendingUnderlyingClaims[caller] = 0
    Vault->>LO: transferRedemptionFunds (retry)
    LO-->>Vault: Success
    Vault->>Vault: Emit RedemptionClaimed
Loading
sequenceDiagram
    participant Upkeep
    participant LO as LiquidityOrchestrator
    participant Config as OrionConfig
    
    Upkeep->>LO: Epoch finalization
    LO->>Config: completeAssetsRemoval(failedTokens)
    
    Note over Config: For each decommissioning asset:
    loop Asset Processing
        alt Asset in failedTokens
            Config->>Config: Retain in _decommissioningAssets
        else Asset sold successfully
            Config->>Config: Remove from whitelistedAssets
            Config->>Config: Emit AssetRemoved
            Config->>Config: Swap-and-pop from _decommissioningAssets
        end
    end
    
    Config-->>LO: Return
    
    Note over LO: Later: Decommission finalization
    LO->>Config: Check queue drained & portfolio liquidated
    alt Conditions met
        LO->>Config: completeVaultDecommissioning
    else Blocked
        LO->>LO: Revert (retry next epoch)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • #140: Directly modifies completeAssetsRemoval() signature to accept failedTokens and alters decommissioning asset retention logic in OrionConfig.
  • #84: Modifies the same decommissioning completion flow in LiquidityOrchestrator and OrionConfig for handling decommissioned vault state transitions.
  • #71: Changes fulfillRedeem access control to onlyLiquidityOrchestrator; this PR extends redemption handling with transfer failure recovery logic in the same function.

Poem

🐰 Through idle gates the orchestrator hops,
Assets pause when failed token sales stop,
Redeemers blocked find solace in the queue,
Claiming later when the path breaks through,
System dances—checks and retries anew! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'B36' is a vague, non-descriptive identifier that provides no meaningful information about the changeset. It does not convey what the pull request actually changes. Revise the title to clearly describe the main change, such as 'Add system idle checks and improve redemption failure handling' or a similar concise summary of the primary modifications.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch b36
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch b36

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/vaults/OrionVault.sol`:
- Around line 746-753: The current claimUnderlying function zeroes
pendingUnderlyingClaims[msg.sender] before calling
liquidityOrchestrator.transferRedemptionFunds, which loses the user's claim if
the external call reverts; to fix, either (A) move the line that sets
pendingUnderlyingClaims[msg.sender] = 0 to after the successful transfer so the
mapping is only cleared on success, or (B) keep the CEI order but wrap
liquidityOrchestrator.transferRedemptionFunds(msg.sender, amount) in a try/catch
and in the catch restore pendingUnderlyingClaims[msg.sender] = amount and
re-revert or emit an error; update emit RedemptionClaimed only after successful
transfer and keep nonReentrant modifier unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b89b63f7-1bda-4d1a-831b-08949b518218

📥 Commits

Reviewing files that changed from the base of the PR and between 54fc102 and ba811da.

📒 Files selected for processing (9)
  • contracts/LiquidityOrchestrator.sol
  • contracts/OrionConfig.sol
  • contracts/execution/UniswapV3ExecutionAdapter.sol
  • contracts/interfaces/IOrionConfig.sol
  • contracts/interfaces/IOrionVault.sol
  • contracts/price/ChainlinkPriceAdapter.sol
  • contracts/test/MockOrionConfig.sol
  • contracts/vaults/OrionTransparentVault.sol
  • contracts/vaults/OrionVault.sol

Comment on lines +746 to +753
/// @inheritdoc IOrionVault
function claimUnderlying() external nonReentrant {
uint256 amount = pendingUnderlyingClaims[msg.sender];
if (amount == 0) revert ErrorsLib.InsufficientAmount();
pendingUnderlyingClaims[msg.sender] = 0;
liquidityOrchestrator.transferRedemptionFunds(msg.sender, amount);
emit RedemptionClaimed(msg.sender, amount);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

User loses funds permanently if claimUnderlying fails again.

The claim amount is zeroed at line 750 before the external call at line 751. If transferRedemptionFunds reverts again (e.g., user is still on the denylist), the user permanently loses their underlying tokens since the mapping was already cleared.

Consider reverting on transfer failure to preserve the user's claim:

🐛 Proposed fix to preserve claim on failure
     function claimUnderlying() external nonReentrant {
         uint256 amount = pendingUnderlyingClaims[msg.sender];
         if (amount == 0) revert ErrorsLib.InsufficientAmount();
-        pendingUnderlyingClaims[msg.sender] = 0;
-        liquidityOrchestrator.transferRedemptionFunds(msg.sender, amount);
-        emit RedemptionClaimed(msg.sender, amount);
+        // Call first, only clear on success to preserve claim if transfer fails again
+        liquidityOrchestrator.transferRedemptionFunds(msg.sender, amount);
+        pendingUnderlyingClaims[msg.sender] = 0;
+        emit RedemptionClaimed(msg.sender, amount);
     }

Note: This changes from CEI pattern but is safe here because nonReentrant prevents reentrancy, and the external call is to a trusted contract (liquidityOrchestrator). If you prefer maintaining CEI, wrap the external call in a try/catch that re-stores the amount on failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/vaults/OrionVault.sol` around lines 746 - 753, The current
claimUnderlying function zeroes pendingUnderlyingClaims[msg.sender] before
calling liquidityOrchestrator.transferRedemptionFunds, which loses the user's
claim if the external call reverts; to fix, either (A) move the line that sets
pendingUnderlyingClaims[msg.sender] = 0 to after the successful transfer so the
mapping is only cleared on success, or (B) keep the CEI order but wrap
liquidityOrchestrator.transferRedemptionFunds(msg.sender, amount) in a try/catch
and in the catch restore pendingUnderlyingClaims[msg.sender] = amount and
re-revert or emit an error; update emit RedemptionClaimed only after successful
transfer and keep nonReentrant modifier unchanged.

@matteoettam09
matteoettam09 deleted the b36 branch April 25, 2026 10:42
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.

2 participants